Php 一个子集中的五个唯一的随机数

Php 一个子集中的五个唯一的随机数,php,algorithm,random,Php,Algorithm,Random,我知道类似的问题经常出现,可能没有明确的答案,但我想从可能无限的数字子集(可能是0-20或0-1000000)生成五个唯一的随机数。 唯一的问题是我不想在循环或填充数组时运行 我目前的方法是简单地从一个子集减去最后五个数字生成五个随机数。如果其中任何一个数字彼此匹配,则它们将转到子集末尾各自的位置。因此,如果第四个数字与任何其他数字匹配,它将下注设置为最后一个数字的第四个 有没有人有一种“足够随机”且不涉及昂贵循环或数组的方法 请记住这是一个好奇的问题,而不是一些任务关键型的问题。如果每个人都不

我知道类似的问题经常出现,可能没有明确的答案,但我想从可能无限的数字子集(可能是0-20或0-1000000)生成五个唯一的随机数。
唯一的问题是我不想在循环或填充数组时运行

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

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

请记住这是一个好奇的问题,而不是一些任务关键型的问题。如果每个人都不发布“你为什么有这个问题?”的答案,我会很感激。我只是在寻找想法。

非常感谢

最好的选择是循环,如:

$max = 20;
$numels = 5;
$vals = array();
while (count($vals) < $numels) {
    $cur = rand(0, $max);
    if (!in_array($cur, $vals))
        $vals[] = $cur;
}
您还可以生成一个介于0和max之间的数字,另一个介于0和max-1之间的数字。。。介于0和max-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 123456789(取4) 123456789(给出4) 12345678(取5) 12356789(给出6) 1234567(取6) 1235789(给出8) 123456(取5) 123579(给出7) 例如,上次提取: x=5 x>=4?x==6 x>=6?x==7 x>=8?x==7
一个随机电话就足够了

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

保持从1到(n选择r)的1-1映射到可能的5个元素子集的集合,就完成了。此映射是标准的,可以在web上找到,例如:

例如:

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

{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


我链接到的msdn页面向您展示了一种生成集合的方法,给出了该集合的编号。i、 e.给定8,它返回集合{3,4}。

因为你只是在寻找不同的想法,这里有一个:


调用生成所需的随机数集。

这个问题的一般形式非常有趣。应该从元素池中进行选择(并将它们从池中移除),还是应该在“命中”已获取的元素时进行循环

据我所知,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个数字),我认为循环“在命中一个取数时”是可以的,除非伪随机生成器被破坏。

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


如果我们不知道,请使用.N.

上述C#中Artefactor第二个解决方案的实现,作为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);
    }
}
静态类程序{
公共静态IEnumerable子集(int max){
随机=新随机();
列表选择=新列表();
对于(int space=max;space>0;space--){
int selection=random.Next(空格);

int offset=selections.TakeWhile((n,i)=>n我的意思是我试图避免整个
while
循环方法,在这种方法中,有人在
while
循环中生成数字,直到他们得到唯一的数字。我知道获得重复数字的机会很小,但这是一种好奇,我并不是在寻找一种假设执行时间无限的解决方案。你知道吗意识到花费无限时间的可能性不是很大:pYes,我同意,但就像我说的,我不太担心找到一个可用的解决方案,因为我已经有了。我在寻找最好的解决方案,或者一些我还没有读过或想过的东西。如果我只是想满足于一个有用的解决方案,问一个问题有什么意义“够好了”?我想扩大我的知识面,了解如何解决好几个问题,而不是被困在做同样的事情上“它很有效”解决方案一次又一次。@SHRAMPNEL上校:我已经从这篇文章中学到了一些新方法,所以我认为值得去问,而不是因为尝试而被侮辱。我认为你的循环想法是他特别想要避免的,而你的最后一个想法是对他的问题的正确回答。我不认为你的第二种方法可以防止重复例如,我可以生成10,然后生成9,这样我就得到了10,10。@erisco我(希望如此)修正了。@SHRAMPENL上校,他可能想用一些东西来证明完全正确。@Artefactor:看起来根据大小使用这些方法之一可以提供一个健壮的问题解决方案,正如aioobe指出的Python所做的那样。谢谢你的提示。有趣的答案。尽管我找不到关于这种1-1映射的解释!也许我找不出好的搜索词。能在你的答案中提供一两个url来帮助我们吗
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);
    }
}