概率算法 -- 从集合中选取N个不重复的元素

      这个问题见了很多,一般表述是,1到n的整数中随机选k个这k个不能重复。变一下即可以是n个元素选k个,不能重复。

 

    有不同复杂度的算法。第一个是笨办法:

(1)随机选一个数,然后判断这个数字是否已经被选过了(是否重复),不过不重复,那就就选他了,如果重复,就再随机选一个。

空间复杂度:O(1)

时间复杂度:O(k^2)

而且有一个问题,如果是100个选90个,那么最后冲突的概率很大,选一个数字要重复随机好多次才行。效率低。不要。

如果用hash来解决判重的问题:

空间:O(n)

时间:O(k)

 

(2)第二种是第一种的改进。先在1到n-k+1中随机选一个,然后把范围扩大到n-k+2,再随机选一个,如果随机选中的是之前已经选过的,那么就选择第n-k+2个元素。

比如:从1-8中选4个数字。

第一步.从1到5中随机选一个,比如选了2.

第二步。从1到6中随机选一个。如果选出的是i=1,3,4,5,6,那么就选i即可。如果选出的是2,这个时候就重复了,此时的处理方式是选择i=6。

第三步,从1到7中随机选一个数,如果跟之前的重复,那么就选那种7.

第四步从1到8中随机选一个数,如个之前的重复,就选8。

 

空间复杂度:O(1)

时间复杂度:O(k^2)

这种方法确保每次都能产生随机数。避免了第一种方法的问题。

方法1和方法2都可以用hash来解决判重时的消耗。

此时方法2的复杂度为:

空间:O(n)

时间:O(k)

 

(3)方法3是经典的交换法。

产生第一个随机元数,将其交换到数组的第一个位置a[0]。这样,只要在a[1]和a[n-1]中随机选泽,即可以确保不重复了。

所以,再在a[1]到a[n-1]中产生一个随机数,交换到a[1]位置,。。。。

空间:O(n)

时间:O(k)

 

(4)因为是从n个中产生k个,所以每个元素选中的概率是k/n。

所以,对于a[0],以k/n的概率选泽他。如果选中了,那么对剩下的数组来说,就是在n-1个元素中选k-1个元素的问题;如果没选中,那么

对剩下的数组来说,就是在n-1个元素中选k个元素的问题。迭代即可。

时间复杂度:O(n)

空间复杂度:O(1)

这个算法不一定要遍历到最后才结束,如果已经选够了n个数,就可以结束了。

 

(5)先选中前k个元素,然后开始遍历剩下的元素,如第k+1个元素来的时候,以k/(1+k)的概率去替换之前k个元素中的一个。至于替换哪一个,就是均匀的选了。

对于新来的这个元素,被选中的概率是:k/(k+1) (替换)

对于之前的元素,被选中的概率是:(1-k/(k+1))+k/(k+1) * (k-1)/k   = 1/(k+1) + (k-1)/(k+1)= k/(+1)   (不替换+替换但没被选中替换出去)

第k+2个元素来的时候,以k/(k+2)的概率去替换之前k个元素中的一个。

 

时间复杂度:O(n)

空间复杂度:O(1)

这种方法还适合在总数量未知的情况下使用。

 

O(n)的算法未必比O(k^2)的算法好,注意n和k的区别。比如在全体4字节长的整数中随机选3个不重复的,就不要用(4)和(5)了。

 

你可能感兴趣的:(算法,随机数)