现在的位置: 首页 > 综合 > 正文

顺序统计量

2012年12月05日 ⁄ 综合 ⁄ 共 1694字 ⁄ 字号 评论关闭

前言

本文内容主要是 寻找最大最小值,寻找第i小值,以及随机函数的改写。在一个大小为n的集合里面,第i个顺序统计量是指该集合中第i小的元素。

寻找最大最小值

普遍算法是遍历数组,设置max和min存储最大最小值。每个元素都与max和min比较,然后分别改变max和min的值(如果需要改变的话)。如此算来该算法需要进行比较的次数为2n次。那么如果想缩短比较的次数该肿么办呢?

假设数组A的长度为奇数,max = min = A[0],然后A[1]…..A[n-1]一共n-1个(偶数个)元素。每次取两个元素比较,将比较后的较大值与max比较,将较小值与min比较,然后相应的修改max和min值,一共比较三次。按照上面的描述,比较的总次数应该为 3*(n-1)/2。

假设数组A的长度为偶数,比较A[0]和A[1],然后将较大值赋值给Max,较小值赋值给min。然后对A[2]……A[n-1]一共n-2个(偶数个)元素进行间隔为2的遍历,每次与上面类似,每次取两值,所以比较的总次数应该为3*(n-2)/2 + 1。

综合所述,长度为n的数组按照上述做法比较的总次数可以从2n缩减到3n/2左右。

具体实现略。

寻找第i小值

寻找最大值和最小值可以遍历寻找,但是寻找第i小值肿么办?第一种办法是先排序,然后直接索引。但是基于比较的排序的执行时间底线是nlogn。而非基于比较的排序又需要各种条件。有木有寻找一种查找效率为O(n)的算法呢,既然是O(n),那么肯定不能排序了,那不排序又怎么找呢?大家可以回想一下快速排序的分区算法,没执行一次能够找出基准值的最终位置,即左边的值都不大于基准值,右边的值都不小于基准值,这从而让人想起了折半查找,分治思想。

   1: //在区间[low, high)搜索第k小的元素 

   2: int RandomizedSearchK(int A[], int low, int high, int k)

   3: {

   4:     if(low == high-1)

   5:         return A[low];  

   6:  

   7:     int r = Partition(A, low, high);  //返回基准值索引

   8:     int n = r - low + 1;  // A[low.....r]的元素个数

   9:     if (n == k)   //基准值正好是第k小

  10:         return A[r];

  11:     else if (n < k)

  12:         RandomizedSearchK(A, r+1, high, k - n);  //查找基准值右边第k-n小

  13:     else 

  14:         RandomizedSearchK(A, low, r, k);   // 查找基准值左边第k小

  15: }

不了解RandomizedPartition函数的可以去查看我的另一篇博文

随机函数

我们常用的伪随机函数rand(),它是返回一个[0, RAND_MAX]的随机数,其中RAND_MAX号称是大整数,在我的系统中是32767。如果要取得[0, n)的随机值,只需rand()%n即可。但是问题来了,比如当n = 20000的时候,rand() = 10000 或者 30000 最后rand()%n的结果都是10000,但是rand() = 15000的时候 rand()%n的结果为15000,也就是说随机到的10000的次数是15000的次数两倍,对每个数的概率不一样,也就不是随机了。所以要更随机一点,可以改写随机算法。

因为 mod 2 == 0或者1的值的个数不好确定,如上例mod 10000最后概率都不相同。所以不能采取mod策略。改写的随机算法是利用 RAND_MAX >= n * bucketsize。其中n表示随机范围,也可以抽象成n个桶,然后bucketsize是每个桶的大小,而且可知每个桶的大小都是相等的(里面的数据一样多),因此随机到的桶的编号的概率是相等的。

   1: // 随机范围[0, n)

   2: int myrand(int n)

   3: {

   4:     int bucketsize = RAND_MAX / n;

   5:     int r;

   6:     do

   7:     {

   8:         r = rand() / bucketsize;

   9:     }while (r >= n);   //即如果 出现的rand()值大于等于 n * bucketsize的值就重新随机

  10:     return r;  // 返回桶子的编号

  11: }

抱歉!评论已关闭.