使用Fisher-Yates shuffle从链表中获取k个随机值

时间:2014-01-23 13:05:51

标签: algorithm shuffle

我有一个庞大的整数链表(假设它的大小为N,但我不知道N)并希望在最短的时间/空间内从中获取k个随机值。

我认为必须有可能编写一个由内到外的Fisher-Yates shuffle变体来解决O(N)时间和O(k)额外空间中的这个问题。

任何人都可以帮助我获得具有指定时间/空间范围的统计上正确的解决方案吗?

我认为我目前的代码接近正确的解决方案:

public class Node
{
    public int Data;

    public Node Next;

    // O(N) time, O(k) additional space
    public int[] GetRandomData(int k)
    {
        var a = new int[k];
        var rand = new Random();

        int n = 0;
        Node cur = this;
        while (cur != null)
        {
            int r = rand.Next(0, n);

            if (r < k)
            {
                a[r] = cur.Data;
            }

            cur = cur.Next;          
        }

        if (n < k) throw new ArgumentException("k is bigger than N");        
        return a;
    }
}

3 个答案:

答案 0 :(得分:2)

这将返回来自未知长度序列的k个项目的均匀分布的随机样本。该算法称为reservoir sampling

def sample(seq, k):
    seq = iter(seq)
    result = [seq.next() for _ in xrange(k)]
    for i, s in enumerate(seq):
        r = random.randrange(i + k + 1)
        if r < k: result[r] = s
    return result

答案 1 :(得分:0)

我最终得到了这个版本(C#),我很确定它是正确的。告诉我,如果我错了。

public class Node
{
    public int Data;

    public Node Next;

    // O(N) time, O(k) additional space
    public int[] GetRandomData(int k)
    {
        var a = new int[k];
        var rand = new Random();

        a[0] = this.Data;
        int i = 1;

        for (Node cur = this.Next; cur != null; cur = cur.Next, i = i + 1)
        {
            int r = rand.Next(0, i + 1);

            if (r < k)
            {
                if (i < k)
                {
                    a[i] = a[r];
                }

                a[r] = cur.Data;
            }
        }

        if (i < k) throw new ArgumentException("k is bigger than N");
        return a;
    }
}

UPD:好的,所以我的代码与wikipedia中的代码相同,所以它必须在统计上正确。

答案 2 :(得分:-1)

这是sattolo算法的实现

from random import randrange  
def sattoloCycle(items):
    i = len(items)
    while i > 1:
        i = i - 1
        j = randrange(i)  # 0 <= j <= i-1
        items[j], items[i] = items[i], items[j]
    return