BFPRT实现

来源:互联网 发布:pc蛋蛋连中20期算法 编辑:程序博客网 时间:2024/06/11 01:46

这里自己实现了一下BFPRT算法,并与别人的实现版本进行效率对比,以及与C++标准库中的sort排序后选取top-k进行效率对比。发现,C语言版本的效率更高一些,在数据量不是海量数据时,sort的速度竟然比BFPRT要快。

原因猜想:O(nlgn),就算是n=50000000,lgn也才25.5754,而bfprt的常数因子要比这个大,再加上stl做了大量的优化,速度上就更快了。

////  BFPRT.cpp//  NowCoder////  Created by soybeanmilk on 2017/7/22.//  Copyright © 2017年 soybeanmilk. All rights reserved.//#include <iostream>#include <vector>#include <ctime>#include <cstdlib>using namespace std;void insertsort(vector<int> &vi, int b, int e){//包含e    for(int i=b+1;i<=e;++i){        int temp=vi[i];        int j=i-1;        for(;j>=b && vi[j]>temp;--j){            vi[j+1]=vi[j];        }        vi[j+1]=temp;    }}int getMidIndex(vector<int> &vi, int b, int e){//包含e    //找中位数,最后返回找到的元素的下标    if(b==e) return b;    int i=b;    int index=0;    for(;i<=e-4;i+=5){//e-(e-4)+1=5        insertsort(vi, i, i+4);        //vi[b+index]=vi[i+2];//这个会使数组乱掉!        swap(vi[b+index],vi[i+2]);//应该是交换元素,而不仅仅是将中位数移动到一块。        ++index;    }    if(i<=e){//剩余的不足5个的元素        insertsort(vi,i,e);        //vi[b+index]=vi[i+(e-i)/2];//这个会使数组乱掉!        swap(vi[b+index],vi[i+(e-i)/2]);//i~e可能为偶数个数,取中间两位低的一位为是位数,跟二分查找中取低位一样。        ++index;//与上面的情况保持一致,index表示中位数的个数    }    return getMidIndex(vi, b, b+index-1);//继续找中位数的中位数,最后返回找到的元素的下标}int partition(vector<int> &vi, int b, int e, int val){//包含e    while(b<e){        while(b<e && vi[e]>val) --e;        if(b<e) vi[b]=vi[e];        while(b<e && vi[b]<=val) ++b;        if(b<e) vi[e]=vi[b];    }    vi[b]=val;    return b;}int BFPRT(vector<int> &vi, int b, int e, int k){//包含e,这里在调用BFPRT时,需要先对k做一下转换,    //找前10个最小的,那么这里传的下k为9,因为下标从0开始,0~9正好是10个数。    if(k<b || k>e)//表示k不符合规则        return -1;    int midindex=getMidIndex(vi, b, e);    int realindex=partition(vi, b, e, vi[midindex]);    if(realindex==k) return realindex;//返回此realindex,在此之前(包含此下标)的元素即为要找的全部元素。    //这个函数需要返回值,主要是为了返回-1这个错误情况。    if(realindex<k) return BFPRT(vi, realindex+1, e, k);//其实就是一直找下标k    else return BFPRT(vi, b, realindex-1, k);//其实就是一直找下标k}double random(double start, double end){    return start+(end-start)*rand()/(RAND_MAX + 1.0);}//别人的实现:http://blog.csdn.net/acdreamers/article/details/44656295#include <iostream>#include <string.h>#include <stdio.h>#include <time.h>#include <algorithm>using namespace std;const int N = 100000005;int a[N];//插入排序void InsertSort(int a[], int l, int r){    for(int i = l + 1; i <= r; i++)    {        if(a[i - 1] > a[i])        {            int t = a[i];            int j = i;            while(j > l && a[j - 1] > t)            {                a[j] = a[j - 1];                j--;            }            a[j] = t;        }    }}//寻找中位数的中位数int FindMid(int a[], int l, int r){    if(l == r) return a[l];    int i = 0;    int n = 0;    for(i = l; i < r - 5; i += 5)    {        InsertSort(a, i, i + 4);        n = i - l;        swap(a[l + n / 5], a[i + 2]);    }        //处理剩余元素    int num = r - i + 1;    if(num > 0)    {        InsertSort(a, i, i + num - 1);        n = i - l;        swap(a[l + n / 5], a[i + num / 2]);    }    n /= 5;    if(n == l) return a[l];    return FindMid(a, l, l + n);}//寻找中位数的所在位置int FindId(int a[], int l, int r, int num){    for(int i = l; i <= r; i++)        if(a[i] == num)            return i;    return -1;}//进行划分过程int Partion(int a[], int l, int r, int p){    swap(a[p], a[l]);    int i = l;    int j = r;    int pivot = a[l];    while(i < j)    {        while(a[j] >= pivot && i < j)            j--;        a[i] = a[j];        while(a[i] <= pivot && i < j)            i++;        a[j] = a[i];    }    a[i] = pivot;    return i;}int BFPTR_other(int a[], int l, int r, int k){    int num = FindMid(a, l, r);    //寻找中位数的中位数    int p =  FindId(a, l, r, num); //找到中位数的中位数对应的id    int i = Partion(a, l, r, p);        int m = i - l + 1;    if(m == k) return a[i];    if(m > k)  return BFPTR_other(a, l, i - 1, k);    return BFPTR_other(a, i + 1, r, k - m);}int main(){    //生成测试数据    vector<int> vi;    srand(unsigned(time(0)));    int nums=50000000;    for(int i=0;i<nums;i++)        vi.push_back(int(random(0., 10.)*1000));    vector<int> vitp(vi);//备份测试数据,给sort函数使用    int k=666%nums;//随机取k值    cout<<nums<<" "<<k<<endl;    //我的实现    clock_t start=clock();    cout<<"my BFPRT: "<<endl;    BFPRT(vi, 0, (int)vi.size()-1, k);//注意这里(int)vi.size()-1    clock_t end=clock();    cout<<"used time: "<<(double)(end-start)/CLOCKS_PER_SEC<<"s"<<endl;    sort(vi.begin(),vi.begin()+k);//为了与下面的直接排序进行结果对比,所以排序一下。    //别人的实现    cout<<"other BFPRT: "<<endl;    for(int i=0;i<nums;i++)        a[i]=vitp[i];    start=clock();    BFPTR_other(a, 0, nums - 1, k);    end=clock();    cout<<"used time: "<<(double)(end-start)/CLOCKS_PER_SEC<<"s"<<endl;    //直接使用sort排序再选top-k    cout<<"direct sorted and select top-k: "<<endl;    start=clock();    sort(vitp.begin(),vitp.end());//不使用vi,而使用vitp,因为vi经过BFPRT后,大部分都是有序的了    end=clock();    cout<<"used time: "<<(double)(end-start)/CLOCKS_PER_SEC<<"s"<<endl;    //这里只验证我的结果是否正确    int isok=true;    for(int i=0;i<k;i++){        if(vi[i]!=vitp[i]){            isok=false;            break;        }    }    if(isok) cout<<"ok!"<<endl;    else cout<<"wrong!"<<endl;    return 0;}
结果如下:

50000000 666my BFPRT: used time: 4.48627sother BFPRT: used time: 3.4281sdirect sorted and select top-k: used time: 3.25237sok!



原创粉丝点击