Php 随机唯一子集生成

Php 随机唯一子集生成,php,algorithm,random,set,combinatorics,Php,Algorithm,Random,Set,Combinatorics,假设我们有1到25个数字,我们必须选择15个数字的集合 如果我是对的话,可能的设置是3268760 在这3268760个选项中,您必须生成100000个 生成100000个唯一且随机的子集的最佳方法是什么 有没有一种方法,一种算法可以做到这一点 如果没有,检测重复项的最佳选项是什么 我计划在PHP上做这件事,但是一个通用的解决方案就足够了, 任何不太“学术”(更实际)的参考都会对我有很大帮助。它们真的必须是随机的吗?还是看似随机 选择:使用Fisher-Yates/Knuth shuffle生成

假设我们有1到25个数字,我们必须选择15个数字的集合

如果我是对的话,可能的设置是3268760

在这3268760个选项中,您必须生成100000个

生成100000个唯一且随机的子集的最佳方法是什么

有没有一种方法,一种算法可以做到这一点

如果没有,检测重复项的最佳选项是什么

我计划在PHP上做这件事,但是一个通用的解决方案就足够了,
任何不太“学术”(更实际)的参考都会对我有很大帮助。

它们真的必须是随机的吗?还是看似随机

选择:使用Fisher-Yates/Knuth shuffle生成一个包含所有25个“shuffle”前15个元素的集合,然后检查您以前是否见过前15个元素的排列。如果是,请忽略,然后重试

重复:您有25个值存在或不存在-这可以简单地散列为整数值(如果第一个元素存在,则添加2^0,如果第二个元素存在,则添加2^1,等等-可以直接表示为25位数字),因此您可以轻松检查是否已经看到它


您会遇到一些冲突,但如果这不是一个性能关键的代码段,那么它可能是可行的

您环境中的随机数生成器(RNG)将为您提供在特定范围内均匀分布的随机数。这种类型的分布通常是需要的,比如说如果你的子集模拟彩票抽奖,但重要的是要提到这一事实,以防你的模型,比如在中学发现的人的年龄

给定此RNG,您可以在1和25之间“绘制”10(或15,如下所示)个数字。这可能需要将生成器生成的随机数相乘(并四舍五入),忽略大于25的数字(即再次绘制),具体取决于与RNG关联的确切API,但再次获取给定范围内的图形是很简单的。当数字再次出现时,您还需要重新绘制

我建议您只获取10个数字,因为这些数字可以从1-25完整序列中删除,以生成一组15。换句话说,放入的15号图纸与取出的10号图纸相同

接下来,您需要断言集合的唯一性。您可以使用散列来唯一地标识每个集合,而不是存储整个集合。这应该少于25位,因此可以存储在32位整数上。然后,您需要有一个高效的存储空间,最多可以存储100000个这些值;除非您想将其存储在数据库中


关于从所有可能的集合中抽取100000个集合的唯一性问题,碰撞的概率似乎相对较低。编辑:哎呀。。。我很乐观。。。这个概率并不是很低,在绘制第50000个后开始碰撞的概率约为1.5%,将有相当多的碰撞,足以保证系统排除它们……

有一种方法可以生成随机的子集样本,保证不会有重复,使用O(1)存储,并且可以随时重新生成。首先,写一个函数到。第二,使用a以随机顺序逐步完成这些组合。只需将数字0…100000输入置换,使用置换的输出作为组合生成器的输入,并处理生成的组合。

这里有一个基于mjv答案的PHP解决方案,我就是这么想的。如果你运行了整整10万组,你确实会看到很多碰撞。然而,我很难设计一个系统来避免它们。相反,我们只是快速地检查它们

我会考虑更好的解决方案。。。在这台笔记本电脑上,我可以在5秒内完成10k组,在20秒内完成20k组。10万需要几分钟

这些集合表示为(32位)整数


只是想澄清一下-你关心集合成员的顺序吗?显然不是。术语集本身意味着一个无序的结构,更能说明问题的是,3268760计数,即C(15,25),表明了相同的情况。对——忽略了3268760的意义—通过存储所排除元素的散列来节省一些散列时间!是的,这不会影响散列的大小,但如果我们基于排除的数字,它的计算速度会更快。即使选择最后一个(100K)子集,也只有1/32的机会选择以前使用过的子集。因此,如果equal的开销比生成候选对象所需的时间相同的假设算法高出不到3%,则丢弃,并且不需要丢弃任何内容。如果您需要160万个子集,那么这将是另一回事。+1用于功能性PHP脚本。该算法在数学上并不像Theran建议的那样优雅,但仍然实用,而且它也可以从ID/散列中重新生成子集。我不认为与碰撞相关的开销是一个因素(正如onebyone所指出的,总体上大约不到2%)。对于一个CWI(编码时…)的努力来说还不错。所有的答案都非常好!给tim接受代码。很遗憾,一个人不能接受超过一个的aswerYep,非常有用的参考资料。谢谢你,瑟兰。词典索引无疑是一种方便的技术。我不太明白伪随机排列的部分,但在第二次阅读时,我相信这会很清楚。
<?PHP
    /* (c) 2009 tim - anyone who finds a use for this is very welcome to use it with no restrictions unless they're making a weapon */

    //how many sets shall we generate?
    $gNumSets = 1000;

    //keep track of collisions, just for fun.
    $gCollisions = 0;

    $starttime = time();

    /**
     * Generate and return an integer with exactly 15 of the lower 25 bits set (1) and the other 10 unset (0)
     */ 
    function genSetHash(){
      $hash = pow(2,25)-1;

      $used = array();

      for($i=0;$i<10;){

        //pick a bit to turn off
        $bit = rand(0,24);

        if (! in_array($bit,$used)){
          $hash =  ( $hash & ~pow(2,$bit) );
          $i++;  
          $used[] = $bit;  
        }
      }
      return  $hash;
    }

    //we store our solution hashes in here.  
    $solutions = array();

    //generate a bunch of solutions.
    for($i=0;$i<$gNumSets;){
      $hash = genSetHash(); 

      //ensure no collisions
      if (! in_array($hash,$solutions)){
        $solutions[] = $hash;
        //brag a little.
        echo("Generated $i random sets in " . (time()-$starttime) . " seconds.\n");
        $i++;
      }else { 
        //there was a collision. There will generally be more the longer the process runs.
        echo "thud.\n"; 
        $gCollisions++;
      }
    }

    // okay, we're done with the hard work.  $solutions contains a bunch of
    // unique, random, ints in the right range.  Everything from here on out
    // is just output.

    //takes an integer with 25 significant digits, and returns an array of 15 numbers between 1 and 25
    function hash2set($hash){
      $set = array();
      for($i=0;$i<24;$i++){  
        if ($hash & pow(2,$i)){
          $set[] = $i+1;
        }
      }
      return $set;
    }

    //pretty-print our sets.
    function formatSet($set){
      return "[ " . implode(',',$set) . ']';
    }

    //if we wanted to print them, 
    foreach($solutions as $hash){
      echo formatSet(hash2set($hash)) . "\n";
    }

    echo("Generated $gNumSets unique random sets in " . (time()-$starttime) . " seconds.\n");

    echo "\n\nDone.  $gCollisions collisions.\n";