从数组访问唯一值对而不重复自己

时间:2014-12-12 04:04:59

标签: php arrays math permutation

我试图以随机顺序从数组中访问唯一值对,而不必重复我自己。

例如,如果我有一个阵列集A,B,C,D(通常是偶数个项目,但最多20个),那么第一次通过我可能会对A-B&光盘。但是我想保证下次我这样做时,我会避免重复我的配对,并且我得到A-C&然后B-D和A-D和B-C再次获得A-B和C-D。每一项只应在每一轮中召唤一次。

我开始随机地将数组的顺序混合在一起,然后将两个值组合在一起 - 但我需要一种方法来防止某些配对比其他配对更频繁地发生(理想情况下我希望它们一直增加通过)。

所以我已经开始考虑排列 - 并且设法使用以下代码获得包含所有可能配对的完整数组:

    $this->items = array('A','B','C','D');

    $input = $this->items;
    $input_copy = $input;
    $output = array();
    $i = 0;
    foreach($input as $val) {

        $j = 0;
        foreach($input_copy as $cval) {
            if($j == $i) break;
            print $val.'-'.$cval.'<br/>';
            //$output[] = array($val => $cval);
            $j++;
        }
        $i++;
    }

    //print_r($output);

例如A,B,C,D我得到:

b-a
c-a
c-b
d-a
d-b
d-c

我想循环设置n-1次并将结果捕获到另一个数组中,但我不确定如何从这些唯一选项生成实际订单

换句话说,我想将上面的列表转到下面:

1st run =>
    1=> A-B, 
    2=> C-D, 
2nd run =>
    1=> A-C, 
    2=> B-D,
3rd run =>
    1=> A-D, 
    2=> C-B,

我可以通过$ this-&gt;项目更简单地完成此操作。我也看过Math_Combinatorics PEAR包,但我不确定从哪里开始。

我很感激任何帮助!

2 个答案:

答案 0 :(得分:1)

您可以使用round-robin tournament algorithm

Place elements in two rows. 
Fix one element - in this case A
For next round shift all other elements in circular manner. 
Pair them. 
Repeat N-1 times

A B
D C
-----
A D
C B
----
A C
B D
----

答案 1 :(得分:1)

我假设您要生成每个配对一次,即整个序列的每个分区成对。如果您只想要每对一次,则在另一个答案中处理不同的问题。

递归地思考这个问题:开头你有 n 个元素。从这些中,取出第一个并从剩余的 n -1元素中选择一个伙伴。将这一对从列表中取出并回避剩余的 n -2元素。如果你使每个选择都不偏不倚,剩下的配对也将是公正的。但这并不能保证你不会比必要时更早地重复自己。

如果你真的想确保避免重复配对,你应该先考虑有多少可能的配对。现在我假设 n 是偶数,所以你只有完整的配对。使用一个未配对的元素很容易将其调整为奇数 n 。要获得可能的配对总数,您必须乘以您的选择:

m=(n-1)*(n-3)*(n-5)*...*7*5*3*1

所以它是奇数的产物。那个A001147,也写成double factorial m =( n -1)!!请注意,这些数字增长相当快,所以即使对于温和的 n (例如 n = 16),您可能不必担心重复自己因为有太多可能的配对选择重复是不太可能的。

如果您真的希望确定以避免重复,那么您当然可以简单地生成整个列表并将其随机播放。但正如我刚才指出的那样,该列表也可能变成 huge 。因此,我建议您将此问题分为两个步骤。找到一种方法来生成从0到 m -1的所有数字,每次只需一次,并找到将这些数字转换为配对的方法。对于后者,您可以简单地逐步分解您的数字。在每个步骤中,取index % (n-1)进行当前选择,并选择(int)(index / (n-1))作为递归调用中后续选择的索引。

对于前者,我能想到的最简单的事情就是使用素数为 p &gt; m 的PRNG作为其周期。使用模运算,应该很容易做到。然后简单地丢弃所有大于或等于 m 的值。丢弃意味着您跳到序列中的下一个元素。这将使所有配对按顺序看起来相当随机。如果该序列中的起点应该是随机的,请确保如果您首先选择要丢弃的值,则必须再次初始化,而不是跳到下一个元素。否则,某些元素可能比其他元素更有可能作为起点。