• C++分治策略实现线性时间选择


    问题描述:

    给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素,即如果将这n个元素依其线性序排列时,排在第k个的元素即为要找到元素。

    细节须知:(与之前的随笔相比)

    (1)设置了对于程序运行次数的手动输入设定

    (2)取消了文件的读入,直接生成随机数进行排序查找

    (3)扩大了随机数的范围、数组的可申请大小

    (4)时间统计精确到了微秒级

    (5)运行结束后一次性写入提升了程序稳定性,写入的数据可用于数据分析图表

    算法原理:

    将n个输入元素划分成⌈n/5⌉个组,每组5个元素,只可能有一个组不是5个元素。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共⌈n/5⌉个。递归调用算法Select来找出⌈n/5⌉个元素的中位数。如果⌈n/5⌉是偶数,就找它的两个中位数中较大的一个。以这个元素作为划分基准。以此递归排序找到所需的第k项。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<fstream>
      6 #include<algorithm>
      7 #include<windows.h>
      8 #include<ctime>
      9 using namespace std;
     10 LARGE_INTEGER nFreq;//LARGE_INTEGER在64位系统中是LONGLONG,在32位系统中是高低两个32位的LONG,在windows.h中通过预编译宏作定义
     11 LARGE_INTEGER nBeginTime;//记录开始时的计数器的值
     12 LARGE_INTEGER nEndTime;//记录停止时的计数器的值
     13 
     14 //一次快排
     15 int Partition(int nums[],int p,int r,int x)
     16 {
     17     if(p>r) return -1;
     18     //找出基准x的位置并与第一位交换
     19     for(int i=p;i<=r;i++)
     20     {
     21         if(nums[i]==x)
     22         {
     23             swap(x,nums[p]);
     24             break;
     25         }
     26     }
     27     int left=p,right=r;
     28     while(left<right)
     29     {
     30         while(left<right && nums[right]>=x) right--;
     31         nums[left]=nums[right];
     32         while(left<right && nums[left]<x) left++;
     33         nums[right]=nums[left];
     34     }
     35     nums[left]=x;
     36     return left;
     37 }
     38 
     39 //快速排序
     40 void QuickSort(int nums[],int low,int high)
     41 {
     42     if(low>high) return;
     43     int key=nums[low];
     44     int left=low,right=high;
     45     while(left<right)
     46     {
     47         while(left<right && nums[right]>=key) right--;
     48         nums[left]=nums[right];
     49         while(left<right && nums[left]<key) left++;
     50         nums[right]=nums[left];
     51     }
     52     nums[left]=key;
     53     QuickSort(nums,low,left-1);
     54     QuickSort(nums,left+1,high);
     55 }
     56 
     57 int Select(int nums[],int p,int r,int k)
     58 {
     59     if(r-p<75)
     60     {
     61         QuickSort(nums,p,r);
     62         return nums[p+k-1];
     63     }
     64     //每5个为一组,找到各组的中位数,并存储在前(r-p-4)/5个位置里
     65     for(int i=0;i<=(r-p-4)/5;i++)
     66     {
     67         QuickSort(nums,p+5*i,p+5*i+4);
     68         swap(nums[p+i],nums[p+5*i+2]);
     69     }
     70     //找所有中位数的中位数
     71     int x=Select(nums,p,p+(r-p-4)/5,(r-p-4)/10);
     72     //以x为基准做一次快排
     73     int i=Partition(nums,p,r,x);
     74     int j=i-p+1;
     75     //判断k属于那个部分
     76     if(k<=j)
     77         return Select(nums,p,i,k);
     78     else
     79         return Select(nums,i+1,r,k-j);
     80 }
     81 int main(){
     82     ofstream fout;
     83     double cost;
     84     int i = 0,m = 0,n = 0,key = 0;
     85     cout<<"Please enter the number of times you want to run the program:";        //输入程序运行次数
     86     cin>>m;
     87     int data_amount[m];
     88     double runtime[m];
     89     srand((unsigned int)time(NULL));      //设置随机数种子
     90     for(i=0;i<m;i++)
     91     {
     92         n=10000+RAND_MAX*(rand()%300)+rand();           //RAND_MAX=32767,随机生成数据量
     93         data_amount[i]=n;                               //限定数据规模为10000~9872867
     94         cout<<"☆The "<<i+1<<"th test Data amount is:"<<n<<endl;
     95         int j=0;
     96         int *a=(int *)malloc(n*sizeof(int));
     97         for(j=0;j<n;j++){
     98             a[j]=RAND_MAX*(rand()%400)+rand();          //随机生成0~13139567的随机数
     99         }
    100         key=rand()%10000+1;   //随机生成1~10000作为所要选择的次序
    101         QueryPerformanceFrequency(&nFreq);//获取系统时钟频率
    102         QueryPerformanceCounter(&nBeginTime);//获取开始时刻计数值
    103         int t=Select(a,0,n-1,key);
    104         QueryPerformanceCounter(&nEndTime);//获取停止时刻计数值
    105         cost=(double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;
    106         runtime[i]=cost;
    107         cout<<"The "<<key<<"th number is:"<<t<<endl;
    108         cout<<"The running time is:"<<cost<<" s"<<endl;
    109         free(a);
    110     }
    111     fout.open("data.txt");
    112     if(!fout){
    113         cerr<<"Can not open file 'data.txt' "<<endl;
    114         return -1;
    115     }
    116     for(i=0;i<m;i++){
    117            fout<<data_amount[i]<<","<<runtime[i]<<endl;
    118     }
    119     fout.close();
    120     cout<<"Success!"<<endl;
    121     return 0;
    122 }

    程序设计思路:

    假设输入的数据规模为n,要查找的数据次序为k。

    (1)判断数组长度,若小于75则直接进行快速排序,否则进行之后的算法。

    (2)将n个输入元素划分成⌈n/5⌉个组,每组五个元素。用快速排序将每组中的元素排好序,并确定每组的中位数,共⌈n/5⌉个。

    (3)递归调用算法Select来找出这⌈n/5⌉个元素的中位数。如果⌈n/5⌉是偶数,就找它的两个中位数中较大的一个。以这个元素作为划分基准。

    (4)以此递归调用进行排序,最终搜索得到要找的第k项。

    时间复杂性分析:

    为了分析算法Select的计算时间复杂性,设n=r-p+1,即n为输入数组的长度。算法的递归调用只有在n≥75时才执行。因此,当n<75是算法Select所用的计算时间不超过一个常数C1。找到中位数的中位数x后,算法Select以x为划分基准调用Partition对数组a[p:r]进行划分,这需要O(n)时间。算法Select的for循环体行共执行n/5次,每一次需要O(1)时间。因此,执行for循环共需O(n)时间。

    设对n个元素的数组调用算法Select需要T(n)时间,那么找中位数的中位数x至多用了T(n/5)的时间。已经证明了,按照算法所选的基准x进行划分所得到的2个子数组分别至多有3n/4个元素。所以,无论对哪一个子数组调用,Select都至多用了T(3n/4)的时间。

    总之,可以得到关于T(n)的递归式

     

    解此递归式可得T(n)=O(n)

    经过5000次不同规模数据的实验并统计运行时间得到如下算法效率分析图

  • 相关阅读:
    Python 判断所有文件或文件夹
    Ubuntu显卡GPU 驱动的安装
    新系统ubuntu 需要配置项目 安装 cudnn 修改pip国内源
    Ubuntu下设置DNS的方法 和国内公共DNS汇总
    学习vue ,环境搭建(VS code、node.js、cnpm、vue-cli)创建项目 并引入element
    mybatis mysql count(*) 返回结果为null的解决
    使用V2Ry和V2RyN搭建本地代理服务器供局域网用户连接
    类似samba raidrive
    shell if判断语句 报错:syntax error near unexpected token `elif'
    stat()返回失败Invalid parameter
  • 原文地址:https://www.cnblogs.com/Jesse-Cavendish/p/11693617.html
Copyright © 2020-2023  润新知