选择算法



一、实验目标

  • 实现最坏情况下线性时间选取第k大元素的算法和Randomized_select,并比较效率

二、设计思路

最坏情况下线性时间选取第k大元素的算法

  • 将n个元素分成5个一组,最后一组有n mod 5个元素

  • 用插入排序对每一组排序,并取其中值

    • 本实验使用了STL里自带的插入排序 __insertion_sort
    • 没有使用新的数组存储n/5个组的中值,而是将它们swap到了数组的最前方,再进行递归调用,从而实现了原址排序
  • 递归调用此算法,寻找n/5个中位数的中位数x

  • 用x作为pivot对数组进行划分

  • 判断是否找到目标值,如果没有则继续递归搜索

    if i < k then return k;

    else if i < k then 找左区间的第i个最小元

    else 找右区间第i-k个最小元

Randomized_select

  • 使用分治思想,对输入数组进行随机划分

  • 使用Randomized_partition对数组进行划分

    • Randomized_partition和partititon的主要区别在于Randomized_partition在算法开始时会将随机选取元素作为A[r]
  • 判断是否找到目标值

    if i < k then return k;

    else if i < k then 找左区间的第i个最小元

    else 找右区间第i-k个最小元

计时部分

  • 利用里的函数

  • 将两种算法分别按照answer.txt里的数据规模测试10遍,再进行统计和分析

  • 由于Randomized_select速度较快,为了精确性计时使用ms(毫秒)作为单位;

    而最坏情况为线性时间的算法采用m(秒)作为单位

  • 源码

    clock_t start,end;
        for(int j = 0;j<10;j++){
            std::cout << "Randomized_Select begins:" << std::endl;
            for(int i = 10;i <= 100000;i*=10){
                start = clock();
                vector<int>temp = arr;
                int ans = Randomized_Select(temp,0,temp.size()-1,i);
                cout<<i<<"   "<<ans<<endl;
                end = clock();
                double time = (double)(end - start)/CLOCKS_PER_SEC;
                cout<<"Total time of Randomized_select :"<<time*1000<<" ms"<<endl;
            }
        }
        
        for(int j = 0;j < 10;j++){
            std::cout << "K_select begins:" << std::endl;
            for(int i = 10;i <= 100000;i*=10){
                start = clock();
                vector<int>temp = arr;
                int ans = k_select(temp,0,temp.size()-1,i);
                cout<<i<<"   "<<ans<<endl;
                end = clock();
                double time1 = (double)(end - start)/CLOCKS_PER_SEC;
                cout<<"Total time of k_select :"<<time1<<" s"<<endl;
            }
        }
    
    

总结

  • 显然,就期望时间而言,randomized_select算法比最坏情况是线性时间寻找第k大元素的算法的时间复杂度要好很多,它的常数项要小很多

    对比发现,randomized_select比另一种算法少了对于每组元素用插入排序的过程,也少了递归寻找中位数的中位数的过程,因此其O(n)的系数要小很多

randomized select

#include<iostream>
#include<random>
#include<vector>
using namespace std;
int Random(int p,int r){
    uniform_int_distribution<unsigned> u(p,r);
    default_random_engine e;
    return u(e);
}
int Randomized_Partition(vector<int>&arr,int p,int r){
    swap(arr[Random(p,r)], arr[r]);
    int x = arr[r];
    int i = p - 1;
    for (int j = p; j < r; ++j){
        if (arr[j] < x){
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[r]);
    return i + 1;
}
int Randomized_Select(vector<int>arr, int p, int r, int i){
    if (p == r)
        return arr[p];
    int q = Randomized_Partition(arr, p, r);
    int k = q - p + 1;
    if (k == i)
        return arr[q];
    else if (k > i)
        return Randomized_Select(arr, p, q - 1, i);
    else
        return Randomized_Select(arr, q + 1, r, i-k);
}
int main(int argc, const char** argv) {
    vector<int>a = {0,1,2,3,4,5,6,7,8,9,10};
    int k = Randomized_Select(a,0,10,3);
    cout<<k<<endl;
    return 0;
}

K select (referring to <Introduction to Algorithms >)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool cmp(int a,int b){
    return a<b;
}
int partition_(vector<int>&arr,int l,int r,int x){
    int i;
    for(i = l;i < r;i++)
    {
        if(arr[i] == x)break;
    }    
    swap(arr[i],arr[r]);

    i = l;
    for(int j = l;j <= r-1;j++){
        if(arr[j]<=x){
            swap(arr[i],arr[j]);
            i++;
        }
    }
    swap(arr[i],arr[r]);
    return i;
}

int k_select(vector<int>&arr,int l,int r,int k){
    //边界条件
    if(r-l<=140)
    {
        __insertion_sort(arr.begin(),arr.end(),cmp);
        return arr[l+k-1];
    }

    //将数组分成5个一小组再分别进行排序
    int t = l - 1;//把找到的中位数放到前面来,方便选中位数的中位数
    for(int sta = l,end = sta + 4;end <= r;sta+=5,end+=5)
    {
        vector<int>::iterator st = next(arr.begin(),sta),ed = next(arr.begin(),end+1);
        __insertion_sort(st,ed,cmp);
        t++;
        swap(arr[t],arr[sta+2]);
    }

    int pivot_id = (t-l+2)/2;
    int pivot = k_select(arr,l,t,pivot_id);
    
    //判断结果 递归调用
    int m = partition_(arr,l,r,pivot);
    int cur = m - l + 1;
    if(k == cur)return arr[m];
    if(k < cur)return k_select(arr,l,m-1,k);
    if(k > cur)return k_select(arr,m+1,r,k-cur);
    return 0;//为了不让编译器warning   QwQ
}
int main(int argc, const char** argv) {
    vector<int>a;
    for(int i = 0;i < 10000;i++)a.push_back(i);
    int k = k_select(a,0,a.size()-1,100);
    cout<<k<<endl;
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值