问题描述:
假设有Sum=10亿个关键字,以文件形式存储,一行一个关键字。
其中有重复的。共大约有K=1亿个不同关键字。
现在要求从这里 随机选取 N个不同的关键字。
注意:随机选取要考虑 关键字的频数
N可能很小也可能很大
请设计一个 尽量高效的算法
------------------------------------------------------------------------
当前思路:
N如果较小,就按一般方式处理
N如果较大,可能需要判断 当前选择的关键字是否已经被选择,如果被选择过了,则需重选。这个过程的复杂度 太大
所以要尽量避免,选择到重复的关键字。 直接思路是每次选择 一个关键字后 就将其从被选 关键字集里剔除。但这里必须保证每个关键字被选中的概率不变。
比如 有如下关键字和对应频数
A 2
B 1
C 1
D 1
在从这里选择两个关键字的前提下,A被选中的概率是:2/5*1+3/5*2/4=21/30 以此类推
如果每个关键字只出现一次的话,就相对容易些,维持一个线性表,在逻辑上将这个线性表看成两部分,
前面部分是待选关键字集,后面是已选关键字集。每次随机选择一个关键字后,就把它和后面的数据交换一下。更改rand范围即可。
但是这里每个关键字出现的次数是不一样的。rand的设计不再那么简单
总体上有那么几个想法
1.保留重复判断
1.1利用折半查找进行快速定位与判断
对已选择的关键字集进行折半插入排序,每次选择了一个关键字后,用折半查找进行定位与判断。
折半插入排序代价是O(N2),每次插入一个新的关键字
折半查找代价是O(logN)。所以时间复杂度是O(N2)
空间复杂度O(N)
1.2利用hash进行快速判断
有点类似与数据库cache中,页面是否已在cache里的判断。引入hash,链表等。
假设hash使用mod(N)实现
空间复杂度是O(N)
时间复杂度是O(N)
1.3保留重复判断的一个重要问题
保留重复判断有一个非常严重的问题,假设N逼近关键字种类数。
越到后面,随机选择到新的关键字的概率越小,需要重复随机选择的次数越多。
故上述方法一般适用N<<K的情况。当N->K/2时,考虑取消重复判断,即需要构造随机选择
2.取消重复判断
难点在于随机选择的构造,也许这个构造本身的代价也是很大的。
我想了很久,要即可行效率又不能比前面的差。
我的想法是先计算出每个关键字被选到的概率。
还是前面的例子A 21/30 B 1/10 C 1/10 D 1/10
rand=rand(0,sum)sum初始为1
然后麻烦的就是建立rand和i(关键字序号)之间的关系,这个查找过程消耗的代价也很大。
因为每次选完一个后,都要剔除,所以使用累计概率是不妥的。只能一个个比对过去,边计算累计概率,边比对。
概率的计算复杂度目前还没数
后面选择的复杂度应该是O(N)的。