使用C语言完成磁盘调度算法中的 最短寻道时间优先调度算法 和 电梯调度算法 !
假设进程请求的访问磁盘的磁道序列号为:100 55 58 39 18 90 160 150 38 184
请输出采用不同算法时访问的磁道号,并计算平均寻道长度。
挑了两个相对简单的磁盘调度算法, 模拟可解, 不考虑时间复杂度的情况下都可以暴力搜索.
(天天都在写暴力, 只会暴力了QAQ.
借这个实验作业来书写一下磁盘的两个调度算法 – 最短寻道时间优先调度算法 ( S S T F ) (SSTF) (SSTF)和电梯调度算法 ( S C A N ) (SCAN) (SCAN).
平均寻道长度: 总共走的磁道数 / 更改磁道的次数.
平均寻道时间: 平均寻道长度 * 移动每道的单位时间
一. 最短寻道时间优先调度 ( S S T F ) (SSTF) (SSTF)
怎么个实现过程? 从最开始的磁道开始时, 每次都寻找到离当前位置最近的磁道. (你没有看错, 就是这么简单. 过程可结合输出来理解. 那么直接贴上代码.
#include<bits/stdc++.h>
#define getpch(type) (type*)malloc(sizeof(type))
using namespace std;
struct _list{
//构造能够双向遍历的链表结构体
struct _list* left;
int val;
struct _list* right;
};
struct _list* ide; //确定“头”指针
int cnt, first;
double run; //步长记录
vector<int>nums; //存储磁道号
void _input(){
//输入
cin >> first;
vector<int>vec(1, first);
while(cin >> cnt) vec.emplace_back(cnt);
nums = vec; //vec可删除
}
void left_work(){
ide = ide->left;
//输出操作
cout << setw(14) << ide->val;
cout << " ";
cout << setw(8) << ide->right->val-ide->val << endl;
run += ide->right->val-ide->val;
//删除节点(如果从节点A离开,那么A节点被删除
struct _list* p = ide->right;
if(ide->right->right) ide->right->right->left = ide;
ide->right = ide->right->right;
free(p); //释放指针空间
}
void right_work(){
ide = ide->right;
//输出操作
cout << setw(14) << ide->val;
cout << " ";
cout << setw(8) << ide->val-ide->left->val << endl;
run += ide->val-ide->left->val;
//删除节点(如果从节点A离开,那么A节点被删除
struct _list* p = ide->left;
if(ide->left->left) ide->left->left->right = ide;
ide->left = ide->left->left;
free(p); //释放指针空间
}
// void debug(){
// struct _list* head = ide;
// while(head->left) head=head->left;
// while(head) {cout << head->val << ' '; head=head->right;}
// cout << endl;
// }
void Print(){
int n = nums.size() - 1;
cout << "start: " << ide->val << endl;
cout << "the next place len nums" << endl;
while(ide->left || ide->right){
//因为当左右都不具有节点时,我们没法离开,所以会“死”,那么我提前结束就好了,反正也遍历完成了
int cnt = ide->val;
//寻找left和right近的那个, 并基于相应的操作
if(!ide->left && ide->right) right_work(); //如果没有更小的了
else if(!ide->right && ide->left) left_work(); //如果没有更大了了
else if(ide->right && ide->left) cnt-ide->left->val < ide->right->val-cnt?left_work():right_work(); //存在小的节点并且存在大的节点
// debug();
}
cout << setiosflags(ios::fixed) << setprecision(1) << "平均寻道时间: " << run / (nums.size()-1) << endl;
}
void _work(){
sort(nums.begin(), nums.end());
struct _list* l = NULL;
// for(auto ao : nums) cout << ao << " ";
for(auto ao : nums){
struct _list* _list_ = getpch(struct _list);
if(ao == first) ide = _list_; //标记head节点
_list_->val = ao; //赋值
//链表的建立, 左右索引指针的赋值(能够双向遍历的链表
if(l) l->right = _list_;
_list_->left = l;
l = _list_;
}l->right = NULL;
// debug();
Print();
}
signed main(void){
//这样书写的优点,时间复杂度小nlog(n), 用桶排可压缩到n
freopen("in.txt","r",stdin); //100 55 58 39 18 90 160 150 38 184
freopen("SSTFout.txt","w",stdout);
_input();
_work();
return 0;
}
这里使用的是建立一个从小到大排好顺序的 可以双向的寻址的链表. 然后每一次更改位置都删除上一个节点. 这样每次寻找都只需要比较 l e f t left left 和 r i g h t right right 的大小就好了. 这样写主要为了降低时间复杂度.
你也可以不对读取的数据进行处理, 而是每次都寻找未被索引的离当前位置最近的磁道(从头到尾查找). 但是这样就会造成极高的时间复杂度. (这样书写很简单, 你也不需要建立链表, 只需要一个数组就可以了. 如果你不在乎时间复杂度建议就暴力查询. 主体的代码片段如下:
bool bo[100]; //标记是否被索引过
int nums[100]; //记录需要索引的磁道号
int first; //第一个磁道
int ide; //标记下一个索引的位置, 初始等于first的下标
int sum = 0; //标记索引过的个数
while(sum < n){
//n表示需要索引的总的磁道个数
int cnt = INT_MAX, ans;
for(int i = 0;i < n;i ++){
//每次都暴力寻找离当前位置最近的磁道
if(cnt < abs(nums[i] - nums[ide]) && bo[i]){
cnt = abs(nums[i] - nums[ide]), ans = i;
}
}
//nums[ans]
bo[ans] = true, ide = ans;
}
给出第一个使用双向链表遍历得到的结果:
start: 100
the next place len nums
90 10
58 32
55 3
39 16
38 1
18 20
150 132
160 10
184 24
平均寻道时间: 27.6
最短寻道时间优先调度算法本身存在的问题: 因为我的示例都是给好了需要访问的磁道, 而未进行后续添加. 实际过程中, 我们需要访问的磁道都是一个动态的过程. 假若我们每次到达指定的磁道后, 又在附近的磁道发出了请求, 那么远的磁道就可能一直存在不访问的情况. 也就是"饥饿"现象.
二. 电梯调度算法 ( S C A N ) (SCAN) (SCAN)
使用电梯调度算法,我们必须在最开始就给定一个向外还是向里寻找的优先级. 然后在这个优先级的作用下, 寻找离当前磁盘号最近的磁盘号. (优先级优于大小, 当外道优先级大于内道时, 那么我们需要先全部索引完 f i r s t first first 的外道磁盘号, 再索引 f i r s t first first 的内道磁盘号.
代码演示:
#include<bits/stdc++.h>
using namespace std;
char trend;
int first, cnt, ans, far;
double run;
vector<int>nums;
void _input(){
trend = getchar();
cin >> first; nums.emplace_back(first);
while(cin >> cnt) nums.emplace_back(cnt);
}
void Print(){
if(trend == '+') far = 1;
else far = -1;
cout << "start: " << nums[ans] << endl;
cout << "the next place len nums" << endl;
int ide = ans + far;
while(ide < nums.size() && ide >= 0){
cout << setw(14) << nums[ide];
cout << " ";
cout << setw(8) << abs(nums[ide] - nums[ide - far]) << endl;
run += abs(nums[ide] - nums[ide - far]);
ide += far;
}
int c = abs(nums[ide - far] - nums[ans]);
far = -far, ide = ans + far;
while(ide < nums.size() && ide >= 0){
cout << setw(14) << nums[ide];
cout << " ";
cout << setw(8) << abs(nums[ide] - nums[ide - far]) << endl;
run += abs(nums[ide] - nums[ide - far]);
ide += far;
}
cout << setiosflags(ios::fixed) << setprecision(1) << "平均寻道时间: " << (run + c) / (nums.size() - 1) << endl;
}
void _work(){
//排序后,只需要判断一次距离就行
sort(nums.begin() , nums.end());
ans = find(nums.begin() , nums.end() , first) - nums.begin();
Print();
}
signed main(void){
freopen("in.txt","r",stdin); //+ 100 55 58 39 18 90 160 150 38 184
freopen("SCANout.txt","w",stdout);
_input();
_work();
return 0;
}
实际上我们只需要对输入数据排序, 然后分别遍历 f i r s t first first 所在位置的左边或者右边即可. (具体先遍历左边还是右边, 看给定的优先级.
数据输入中的 ‘ + + +’ 表示先向外道寻找, 而 ‘ − - −’ 理所当然先向里道寻找.
这个算法很简单, 也不需要绕弯子.
结果演示如下:
start: 100
the next place len nums
150 50
160 10
184 24
90 10
58 32
55 3
39 16
38 1
18 20
平均寻道时间: 27.8
三. CSCAN
快期末考试了, 还没开始预习, 这种东西真的会考吗 눈_눈
--over