来自子集的五个唯一的随机数

时间:2010-06-09 05:02:56

标签: php algorithm random

我知道类似的问题出现了很多,而且可能没有明确的答案,但我想从一个可能无限的数字子集中生成五个唯一的随机数(可能是0-20或0-1,000,000)。 /> 唯一的问题是我不想运行while循环或填充数组。

我目前的方法是简单地从子集中减去最后五个数字生成五个随机数。如果任何数字彼此匹配,则它们将在子集的末尾到达它们各自的位置。因此,如果第四个数字与任何其他数字匹配,它将下注设置为最后一个数字的第4个。

有没有人有一个“足够随机”的方法,并且不涉及昂贵的循环或数组?

请记住这是一种好奇心,而不是一些关键任务问题。如果每个人都没有发帖“你为什么会遇到这个问题?”我将不胜感激。答案。我只是在寻找想法 非常感谢!

6 个答案:

答案 0 :(得分:8)

一个随机号码就足够了。

如果要在1-n范围内选择5个唯一数字的子集,则在1到(n选择r)中选择一个随机数。

保持从1到(n选择r)的1-1映射到可能的5个元素子集的集合,您就完成了。此映射是标准的,可以在Web上找到,例如:http://msdn.microsoft.com/en-us/library/aa289166%28VS.71%29.aspx

举个例子:

考虑从五个数字生成两个数字的子集的问题:

{1,...,5}的可能的2个元素子集是

1. {1,2}
2. {1,3}
3. {1,4}
4. {1,5}

5. {2,3}
6. {2,4}
7. {2,5}

8. {3,4}
9. {3,5}

10. {4,5}

现在5选2是10。

所以我们从1到10中选择一个随机数。假设我们得到8.现在我们在上面的序列中生成第8个元素:得到{3,4},所以你想要的两个数字是3和4。 / p>

我链接到的msdn页面显示了一个生成集合的方法,给定了数字。即给定8,它返回集合{3,4}。

答案 1 :(得分:4)

您最好的选择是循环,如:

$max = 20;
$numels = 5;
$vals = array();
while (count($vals) < $numels) {
    $cur = rand(0, $max);
    if (!in_array($cur, $vals))
        $vals[] = $cur;
}

对于小范围,您可以使用array_rand

$max = 20;
$numels = 5;
$range = range(0, $max);
$vals = array_rand($range, $numels);

您还可以生成介于0和最大值之间的数字,另一个介于0和最大值1之间,...介于0和最大值4之间。然后你将x加到第n个生成的数字,其中x是以这种方式计算的数字:

  • 获取第n次迭代中生成的数字并将其分配给x
  • 如果它大于或等于第一次迭代中生成的那个,则递增它
  • 如果此新数字大于或等于第二次迭代中生成(并更正)的数字,则将其递增
  • ...
  • 如果这个新数字大于或等于第(n-1)次迭代中生成(和修正)的数字

映射是这样的:

1 2 3 4 5 6 7 8 9 (take 4)
1 2 3 4 5 6 7 8 9 (gives 4)

1 2 3 4 5 6 7 8 (take 5)
1 2 3 5 6 7 8 9 (gives 6)

1 2 3 4 5 6 7 (take 6)
1 2 3 5 7 8 9 (gives 8)

1 2 3 4 5 6 (take 5)
1 2 3 5 7 9 (gives 7)

example, last extraction:
x = 5
x >= 4? x == 6
x >= 6? x == 7
x >= 8? x == 7

答案 2 :(得分:2)

这个问题的一般形式非常有趣。是应该从元素池中选择(并从池中删除它们)还是应该在“点击”已经被捕获的元素时进行循环?

据我所知,random.sample 的python库实现在两种方法之间选择运行时,具体取决于输入列表大小的比例和要选择的元素数量

来自源代码的评论:

    # When the number of selections is small compared to the
    # population, then tracking selections is efficient, requiring
    # only a small set and an occasional reselection.  For
    # a larger number of selections, the pool tracking method is
    # preferred since the list takes less space than the
    # set and it doesn't suffer from frequent reselections.

在OP提到的特定情况下(选择5个数字),我认为循环“在点击被采用的数字时”是可以的,除非伪随机生成器被破坏。

答案 3 :(得分:0)

因为你只是在寻找不同的想法:

呼叫Random.org以生成您需要的一组随机数。

答案 4 :(得分:0)

如果您知道大小N,则保持每个数字的概率为5 / N,生成0到1之间的随机数,如果小于5 / N,则保留该项目。当我们有5个项目时停止。

如果我们不知道N使用resorvoir sampling

答案 5 :(得分:0)

Artefacto在C#上面的第二个解决方案的实现,作为ICollection上的帮助和扩展方法:

static class Program {

    public static IEnumerable<int> Subset(int max) {
        Random random = new Random();
        List<int> selections = new List<int>();
        for (int space = max; space > 0; space--) {
            int selection = random.Next(space);
            int offset = selections.TakeWhile((n, i) => n <= selection + i).Count();
            selections.Insert(offset, selection + offset);
            yield return selection + offset;
        }
    }

    public static IEnumerable<T> Random<T>(this ICollection<T> collection) {
        return Subset(collection.Count).Select(collection.ElementAt);
    }

    static void Main(string[] args) {
        Subset(10000).Take(10).ToList().ForEach(Console.WriteLine);
        "abcdefghijklmnopqrstuvwxyz".ToArray().Random().Take(5).ToList().ForEach(Console.WriteLine);
    }
}