我有一个方向链表而不知道它的大小。
我想在此列表中获得一个随机元素,我只有一次机会遍历列表。 (我不允许遍历两次或更多次)
这个问题的算法是什么?谢谢!
答案 0 :(得分:18)
这只是reservoir sampling,水库大小为1。
基本上它非常简单
这是统一采样的,因为在一天结束时挑选任何元素的概率是1 / n(练习给读者)。
答案 1 :(得分:1)
这可能是一个采访问题。数据科学家使用的数据库采样将大量数据流存储在有限存储空间中。
如果你必须从任何具有元素n的数组中收集k个元素,这样你收集的每个元素的概率应该相同(k / n),你可以按照两个步骤,
1)将第一个k元素存储在存储中。 2)当下一个元素(k + 1)来自流时,显然你的集合中没有空格。生成一个从o到n的随机数,如果生成的随机数小于k假设l,则替换storage [l ]来自流的(k + 1)元素。
现在,回到你的问题,这里的存储大小是1.所以你将选择第一个节点,迭代列表中的第二个元素。现在生成随机数,如果它是1,则单独保留样本,否则切换列表中的存储元素
答案 2 :(得分:0)
这个问题可以使用油藏采样来完成。它基于从n个项目中选择k个随机项目,但这里n可能非常大(它不必适合内存!)和(如你的情况)最初未知。
维基百科有一个可理解的算法,我在下面引用:
array R[k]; // result
integer i, j;
// fill the reservoir array
for each i in 1 to k do
R[i] := S[i]
done;
// replace elements with gradually decreasing probability
for each i in k+1 to length(S) do
j := random(1, i); // important: inclusive range
if j <= k then
R[j] := S[i]
fi
done
这个问题只需要1个值,所以我们采用k = 1。
C实施:
答案 3 :(得分:0)
这是我找到的最简单的方法,它工作正常并且可以理解:
public int findrandom(Node start) {
Node curr = start;
int count = 1, result = 0, probability;
Random rand = new Random();
while (curr != null) {
probability = rand.nextInt(count) + 1;
if (count == probability)
result = curr.data;
count++;
curr = curr.next;
}
return result;
}