第16周外部排序【项目-1】置换-选择算法模拟

该博客介绍了如何使用程序模拟置换-选择算法来生成外部排序的初始归并段。通过处理包含18个记录的大文件,利用内存工作区处理5个记录的能力,最终输出归并段文件。文章展示了代码实现,并提及运行结果,但未给出具体细节。作者对此过程的体会是代码量较大。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编写程序,模拟置换-选择算法生成初始归并段的过程。
设大文件中的记录共有18个: 15 4 97 64 17 32 108 44 76 9 39 82 56 31 80 73 255 68
内存工作区可以容纳5个记录,输出产生的归并段文件。
在模拟中,输入文件数据和输出的归并段数据均直接置在内存中即可。

代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#define MaxSize 50          //每个文件最多记录数
#define MAXKEY 32767        //最大关键字值∞
#define W 5                 //内存工作区可容纳的记录个数
typedef int LoserTree[W];   //败者树是完全二叉树且不含叶子,可采用顺序存储结构
typedef int InfoType;       //定义其他数据项的类型
typedef int KeyType;        //定义关键字类型为整型
typedef struct              //记录类型
{
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项,具体类型在主程中定义
} RecType;
typedef struct
{
    RecType rec;            //存放记录
    int rnum;               //所属归并段的段号
} WorkAreaType;
typedef WorkAreaType WorkArea[W];               //内存工作区,容量为W
typedef struct
{
    RecType recs[MaxSize];  //存放文件中的数据项
    int length;             //存放文件中实际记录个数
    int currec;             //存放当前位置
} FileType;                 //文件类型
FileType Fi;                //定义输入文件,为全局变量
FileType Fo;                //定义输出文件,为全局变量
void initial()              //输入输出文件初始化
{
    int n=19,i;
    KeyType a[]= {15,4,97,64,17,32,108,44,76,9,39,82,56,31,80,73,255,68,MAXKEY};
    for (i=0; i<n; i++)
        Fi.recs[i].key=a[i];
    Fi.length=n;
    Fi.currec=-1;
    Fo.currec=-1;
    Fo.length=0;
}
void Select_MiniMax(LoserTree ls, WorkArea wa,int q)
//从wa[q]起到败者树的根比较选择最小记录,并由q指示它所在的归并段
{
    int p,s,t;
    for (t=(W+q)/2,p=ls[t]; t>0; t=t/2,p=ls[t])
        if ((wa[p].rnum<wa[q].rnum) || (wa[p].rnum==wa[q].rnum && wa[p].rec.key<wa[q].rec.key))
        {
            s=q;
            q=ls[t];        //q指示新的胜者
            ls[t]=s;
        }
    ls[0]=q;
}
void Construct_Loser(LoserTree ls,WorkArea wa)
//输入W个记录到内存工作区wa,建败者树ls,选最小的记录并由s指示其在wa中的位置
{
    int i;
    for(i=0; i<W; i++)
        wa[i].rnum=wa[i].rec.key=ls[i]=0;       //工作区初始化
    for(i=W-1; i>=0; i--)
    {
        Fi.currec++;                            //从输入文件读入一个记录
        wa[i].rec=Fi.recs[Fi.currec];
        wa[i].rnum=1;                           //其段号为1
        Select_MiniMax(ls,wa,i);                //调整败者
    }
}
void get_run(LoserTree ls,WorkArea wa,int rc,int &rmax)
//求得一个初始归并段
{
    int q;
    KeyType minimax;                //当前最小关键字
    while (wa[ls[0]].rnum==rc)      //选得的当前最小记录属当前段时
    {
        q=ls[0];                    //q指示当前最小记录在wa中的位置
        minimax=wa[q].rec.key;
        Fo.currec++;                //将刚选得的当前最小记录写入输出文件
        Fo.length++;
        Fo.recs[Fo.currec]=wa[q].rec;
        Fi.currec++;                //从输入文件读入下一记录
        wa[q].rec=Fi.recs[Fi.currec];
        if (Fi.currec>=Fi.length-1) //输入文件结束,虚设记录(属rmax+1段)
        {
            wa[q].rnum=rmax+1;
            wa[q].rec.key=MAXKEY;
        }
        else                        //输入文件非空时
        {
            if(wa[q].rec.key<minimax)
            {
                rmax=rc+1;          //新读入的记录属下一段
                wa[q].rnum=rmax;
            }
            else                    //新读入的记录属当前段
                wa[q].rnum=rc;
        }
        Select_MiniMax(ls,wa,q);    //选择新的当前最小记录
    }
}
void Replace_Selection(LoserTree ls,WorkArea wa)
//在败者树ls和内存工作区wa上用置换-选择排序求初始归并段
{
    int rc,rmax;
    RecType j;                          //j作为一个关键字最大记录,作为一个输出段结束标志
    j.key=MAXKEY;
    Construct_Loser(ls,wa);             //初建败者树
    rc=1;                               //rc指示当前生成的初始归并段的段号
    rmax=1;                             //rmax指示wa中关键字所属初始归并段的最大段号
    while(rc<=rmax)                     //rc=rmax+1标志输入文件的置换-选择排序已完成
    {
        get_run(ls,wa,rc,rmax);         //求得一个初始归并段
        Fo.currec++;                    //将段结束标志写入输出文件
        Fo.recs[Fo.currec]=j;
        Fo.length++;
        rc=wa[ls[0]].rnum;              //设置下一段的段号
    }
}

int main()
{
    int i=0,rno=1;
    initial();
    LoserTree ls;
    WorkArea wa;
    printf("大文件的记录为:\n  ");
    while (Fi.recs[i].key!=MAXKEY)
    {
        printf("%d ",Fi.recs[i].key);
        i++;
    }
    printf("\n");
    Replace_Selection(ls,wa);       //用置换-选择排序求初始归并段
    printf("产生的归并段文件的记录如下:\n");
    printf("  归并段%d:",rno);     //输出所有的归并段
    for (i=0; i<Fo.length; i++)
        if (Fo.recs[i].key==MAXKEY)
        {
            printf("∞");
            if (i<Fo.length-1)
            {
                rno++;
                printf("\n  归并段%d:",rno);
            }
        }
        else
            printf("%d ",Fo.recs[i].key);
    printf("\n  共产生%d个归并段文件\n",rno);
    return 0;
}


运行结果:

心得与体会:

这代码太长了!!

 

代码使用 PyQt5 创建统一界面,通过选项卡切换不同实验;一定要严格遵守要求,把所有要求都实现,不要遗漏,一定要按要求,页面的布局要简约大方【要求设计一个整合系统 使得每个项目实验可以在一个整体系统中进行演示。 (可视化界面内容格式,不做硬性要求) 实验项目一:动态资源分配算法模拟--银行家算法 实验内容: 主要用于解决多种资源被多个独立执行的进程共享的安全算法。采用矩阵存储资源的数据,通过对系统资源预分配后检查系统状态,以避免死锁的产生。 学习要求: 1.资源种类与数目可在界面进行设置,在资源分配过程中可以随时增加进程及其对资源的需求。 2.可读取样例数据(要求存放在外部文件中)进行资源种类、数目与进程数的初始化。 3.在资源分配过程中可以随时进行系统安全状态检测。 4.如果能够通过系统安全状态检测,则系统对该进程进行资源分配;当进程满足所有资源分配后能够自行释放所有资源,退出资源竞争。 5.要求进行安全性检查时按指定策略顺序进行,即按每个进程当前Need数由小至大进行排序,如果Need数相同,则按序号由小至大进行排序; 6.具有一定的数据容错性。 实验检查要求: 假设有 5 个进程(P0, P1, P2, P3, P4)。 系统提供 3 种资源(A, B, C),资源总量分别为 A=10, B=5, C=7。 输入:一组代表性的数据(下图展示) Max 最大需求矩阵: P0: [7, 5, 3] P1: [3, 2, 2] P2: [9, 0, 2] P3: [2, 2, 2] P4: [4, 3, 3] Allocation 当前分配矩阵: P0: [0, 1, 0] P1: [2, 0, 0] P2: [3, 0, 2] P3: [2, 1, 1] P4: [0, 0, 2] Available: [3, 3, 2] (表示系统中可用的 3 个 A 资源,3 个 B 资源,2 个 C 资源) 输出:安全序列 安全序列会有五种情况,均正确: 1、P3 -> P1 -> P4 -> P2 -> P0 2、P3 -> P1 -> P4 -> P0 -> P2 3、P3 -> P1 -> P0 -> P2 -> P4 4、P1 -> P3 -> P4 -> P2 -> P0 5、P1 -> P3 -> P0 -> P2 -> P4 下面请同学们尝试将可用资源能否有安全序列 比如:Available: [3, 1, 2] ->没有安全序列,发生死锁 实验项目二:通用处理机调度演示程序 实验内容:设计一个模拟处理机调度算法,以巩固和加深处理机调度的概念。 实验要求: 1. 进程调度算法包括:时间片轮转算法、先来先服务算法、短作业优先算法、静态优先权优先调度算法、高响应比调度算法 。 2. 每一个进程有一个PCB,其内容可以根据具体情况设定。 3. 进程数、进入内存时间、要求服务时间、作业大小、优先级等均可以在界面上设定。 4. 可读取样例数据(要求存放在外部文件中)进行进程数、进入内存时间、时间片长度、作业大小、进程优先级的初始化 5. 可以在运行中显示各进程的状态:就绪、执行(由于不要求设置互斥资源与进程间的同步关系,故只有两种状态) 6. 采用可视化界面,可在进程调度过程中随时暂停调度,查看当前进程的状态及相应的阻塞队列。 7. 有性能比较功能,可比较同一组数据在不同调度算法下的平均转时间。 实验检查要求: 输入:n个进程的基本信息:进程号、到达时间、服务时长(作业大小)、优先级别;(建议进程信息从文件读取) 输出:显示调度过程, 进程数:4 时间片长度:2(仅适用于时间片轮转算法) 初始的进程列表及其属性: (从本文中读入,效果如下) 示范样例: P1:到达时间 0,运行时间 5,优先级 3 P2:到达时间 1,运行时间 3,优先级 2 P3:到达时间 2,运行时间 8,优先级 1 P4:到达时间 3,运行时间 6,优先级 4 操作:选择任意算法(时间片轮转算法、先来先服务算法、短作业优先算法、静态优先权优先调度算法、高响应比调度算法) 部分算法的结果输出: 1. 先来先服务(FCFS): 执行顺序:P1 -> P2 -> P3 -> P4 完成时间:P1 = 5, P2 = 8, P3 = 16, P4 = 22 转时间:P1 = 5, P2 = 7, P3 = 14, P4 = 19 等待时间:P1 = 0, P2 = 4, P3 = 6, P4 = 13 平均转时间:11.25 平均等待时间:5.75 2. 最短作业优先(SJF): 执行顺序:P1 -> P2 -> P4 -> P3 完成时间:P1 = 5, P2 = 8, P4 = 14, P3 = 22 转时间:P1 = 5, P2 = 7, P4 = 11, P3 = 20 等待时间:P1 = 0, P2 = 4, P4 = 5, P3 = 12 平均转时间:10.75 平均等待时间:5.25 3. 静态优先权优先调度: 执行顺序:P1 → P3 → P2 → P4 完成时间:P1 = 5, P3 = 13, P2 = 16, P4 = 22 转时间:P1 = 5, P3 = 11, P2 = 15, P4 = 17 等待时间:P1 = 0, P3 = 3, P2 = 12, P4 = 13 平均转时间:12.5 平均等待时间:7 4. 时间片轮转(RR, 时间片 = 2): 执行顺序: 时间段 进程 0 - 2 P1 2 - 4 P2 4 - 6 P3 6 - 8 P1 8 -10 P4 10-11 P2 11-13 P3 13-14 P1 14-16 P4 16-18 P3 18-20 P4 20-22 P3 完成时间:P1 =14, P2 = 11, P3 = 22, P4 = 20 转时间:P1 = 14, P2 = 0, P3 = 22, P4 = 17 等待时间:P1 = 9, P2 = 7, P3 = 12, P4 = 11 平均转时间:15.25 平均等待时间:9.75 5.高响应比调度算法(HRRN) 执行顺序:P1 → P2 → P4 → P3 完成时间:P1 = 5, P3 =8, P2 = 14, P4 = 22 转时间:P1 = 5, P3 = 7, P2 = 11, P4 =20 等待时间:P1 = 0, P3 = 4, P2 = 5, P4 = 12 平均转时间 = (5 + 7 + 11 + 20) / 4 = 10.75 平均等待时间 = (0 + 4 + 5 + 12) / 4 = 5.25 实验项目三:进程间通信 实验内容:在Windows环境下,1个读者和N个写者随机读写一个共享缓冲区的问题。缓冲区非满可以连续写(满时等待),缓冲区非空可以连续读(空时等待);读和写的顺利随机交替,按照写入到缓冲区的先后顺序读出的缓冲区的每一个数据,数据读出后该缓冲区即为空闲等待新数据写入;显示读者和写者的数据写入和读取的过程。编程实现基于共享内存的进程间通信问题。 实验要求: (1)当两个进程通信时需满足进程同步机制。 (2)当多个进程(大于2,约定只有一个读者和多个写者)通信时需使用信号量机制实现互斥操作。 (3)编程语言不限。 实验检查要求: 输入:参数设置1个读者,n个写者,缓冲区大小可以设置。 操作:执行读/写过程 输出: 显示读者和写者交互过程,由读者按照写入到缓冲区先后顺序读出每一个数据。 示范样例(尝试实现可视化): 输入: 读者数:1 写者数:3 缓冲区大小:3 (一般设为2的倍数,16) 输出: 时间 0.0s: 缓冲区状态: [ ][ ][ ] 写者1 写入了数据 'A'。 缓冲区状态: [A][ ][ ] 时间 0.1s: 写者2 写入了数据 'B'。 缓冲区状态: [A][B][ ] 时间 0.2s: 写者3 写入了数据 'C'。 缓冲区状态: [A][B][C] 时间 0.3s: 缓冲区已满,写者1、写者2、写者3 正在等待。 时间 0.4s: 读者读取了数据 'A'。 缓冲区状态: [ ][B][C]   实验项目四:存储管理动态分配算法模拟 实验内容:设计主界面以灵活选择算法,且以下算法都要实现:首次适应算法、循环首次适应算法、最佳适应算法; 实验要求: 用一种结构化高级语言构造分区描述器,编制动态分区分配算法和回收算法模拟程序,并掌握分配算法的特点,提高编程技巧和对算法的理解和掌握。 实验检查要求: 输入:通过参数初始存储区域的大小(10000KB)。 操作:(1)可以通过界面或者命令行对整个存储空间进行分区,模拟硬盘分析A、B、C,也可以删除和合并分区; (2)根据“首次适应算法、循环首次适应算法、最佳适应算法”,选择“A、B、C”某一个分区,进行存储空间的分配。实例:分配4个作业,大小分别是50KB,100KB,150KB,200KB;然后释放作业4(200KB),释放作业1(50KB)和作业2(100KB),同一分区的连续空闲空间要自动合并。 输出: 显示读者和写者交互过程,由读者按照写入到缓冲区先后顺序读出每一个数据。 初始存储空间大小:10000KB +-----------+-----------+-----------+ | 分区 A | 分区 B | 分区 C | | 4000 KB | 3000 KB | 3000 KB | +-----------+-----------+-----------+ 1. 第一次分配:分配4个作业,大小分别为 50、100、150、200。 内存状态: [作业1 50KB][作业2 100KB][作业3 150KB][作业4 200KB][空闲3500KB] 空闲块表示意图: +------------+--------------+-----------+ | 空闲块编号 | 起始地址 (KB)| 大小 (KB) | +------------+--------------+-----------+ | 1 | 500 | 3500 | +------------+--------------+-----------+ 2. 操作:释放作业4 内存状态: [作业1 50KB][作业2 100KB][作业3 150KB][空闲3700KB] 空闲块表示意图: +------------+--------------+-----------+ | 空闲块编号 | 起始地址 (KB) | 大小 (KB) | +------------+--------------+-----------+ | 1 | 300 | 3700 | +------------+--------------+-----------+ 3. 操作:释放作业1和作业2 内存状态: [作业1 50KB][作业2 100KB][作业3 150KB][空闲200KB][空闲3500KB] 空闲块表示意图: +------------+--------------+-----------+ | 空闲块编号 | 起始地址 (KB) | 大小 (KB)| +------------+--------------+-----------+ | 1 | 300 | 3700 | | 2 | 0 | 150 | +------------+--------------+-----------+ 最终内存状态(A区): +------------+-------------+---------------+ | 空闲150KB | 作业3 150KB | 空闲3700KB | +------------+-------------+---------------+ | 地址 0-149 | 地址150-299 | 地址300-3999 | +------------+-------------+---------------+   实验项目五:存储管理之虚拟存储器模拟--页面置换算法 实验内容:编程序实现先进先出算法(FIFO)、最近最久未使用算法(LRU)算法、最佳置换算法(OPT)的具体实现过程,并计算访问命中率。 实验要求: 1.设计主界面以灵活选择算法,且以上算法都要实现。 2. 用随机数方法产生页面走向。 3. 假定初始时页面都不在内存。 实验检查要求: 页面引用序列: 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1 物理内存框数为 3 结果对比: 算法 缺页次数 总请求次数 缺页率 命中率 FIFO 15 20 75 25 LRU 12 20 60 40 OPT 9 20 45 55   实验项目六:文件系统设计 实验内容:以内存作为存储区,模拟UNIX的文件系统结构,设计一个简单的文件系统,并提供以下的文件和目录操作接口:创建文件、删除文件、打开文件、关闭文件、读文件、写文件。 实验要求: 设计文件系统的数据结构和数据结构间的关系,设计文件的基本操作算法,完成系统的总控模块编程和各个操作模块编程。上机测试各个模块,没执行一个操作,打印有关数据结构的内容,判断操作的正确性。 实验检查要求: 假设我们需要在 D1 目录下创建一个子目录 D2,然后在 D2 目录下创建一个文件 D1,并防止同名文件或目录的创建。 1. 创建目录 D1: 当前结构: / ├── D1 2. 进入 D1,创建目录 D2: 当前结构: / ├── D1 └── D2 3. 进入 D2,创建文件 D1: 当前结构: / ├── D1 └── D2 └── D1 (文件) 4. 尝试在 D2 下再次创建 D1 文件(同名文件): 错误:在目录 D2 下已存在名为 D1 的文件或目录 5. 尝试在D1 文件内写入内容:“ABCD” 6. 读取D1 文件内容   实验项目七:磁盘调度管理 实验内容:设计一个磁盘调度系统,针对磁盘访问序列,可以选择先来先服务算法(FCFS)、最短寻道时间优先算法(SSTF)、扫描算法(SCAN)、循环扫描算法(CSCAN)来实现。 实验要求: 1.系统主界面可以灵活选择某种算法。 2.每种调度算法实现之后,要计算出每种算法的平均寻道长度,并比较结果。 3.采用改进算法之后是否会使性能提高? 实验检查要求: 输入: 磁盘访问序列: 例如,给定磁盘请求序列 [98, 183, 37, 122, 14, 124, 65, 67]。 初始磁头位置: 例如,初始磁头位置为 53。 磁盘范围: 例如,磁盘磁道的范围为 0-199。 输出结果对比: 最短寻道时间优先算法(SSTF)最优   实验项目八:多进程同步模拟--桔子苹果问题 实验内容:有两类生产者,一类负责生产桔子,一类负责生产苹果;有两类消费者,一类负责消费桔子,一类负责消费苹果;他们共享一个有20个存储单元的有界缓冲区,每个存储单元只能放入一种产品(桔子/苹果)。 实验要求: 1.二类生产者与二类消费者数目均为20,即20个生产者负责生产桔子,20个生产者负责生产苹果;20个消费者负责消费桔子,20个消费者负责消费苹果 。 2.二类生产者的生产速度与二类消费者的消费速度均可独立在程序界面调节,在运行中,该值调整后立即生效。 3.多个生产者或多个消费者之间必须有共享对缓冲区进行操作的函数代码,同时需要考虑算法的效率性。 4.每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前生产者与消费者的指针位置,以及生产者和消费者线程标识符。 5.采用可视化界面,可在运行过程中随时暂停,查看当前生产者、消费者以及有界缓冲区的状态。 输入: 生产者数:20 消费者数:20 缓冲区大小:20 初始缓冲区状态:空 示范样例 生产者生产桔子和苹果的速率(同学们可以自行调节): 桔子生产者:生产桔子,每秒 2 个。 苹果生产者:生产苹果,每秒 1 个。 样例消费者消费的速率: 桔子消费者:消费桔子,每 1 秒消费 1 个。 苹果消费者:消费苹果,每 1 秒消费 1 个。 输出: 动态展示每个生产者和消费者的操作,缓冲区的实时状态,并输出程序结束时的缓冲区内容和操作日志。(如下图,此时缓冲区中桔子比苹果要多,类似这种效果即可) 缓冲区状态 桔子 桔子 桔子 空 空 空 空 空 空 空 苹果 苹果 空 空 空 空 空 空 空 空 】
最新发布
05-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值