从数组中访问唯一值对,而无需重复自己


Accessing unique value pairs from an array without repeating myself

我正在尝试以随机顺序从数组中访问唯一值对 - 直到我不得不重复自己。

例如,如果我有一个数组集A,B,C,D(通常是偶数个项目,但最多20个(,那么第一次通过我可能会配对A-B和C-D。但我想保证下次我这样做时,我避免重复我的配对,并且在我再次获得 A-B 和 C-D 之前,我会同时获得 A-C 和 B-D、A-D 和 B-C。每个项目在每个回合中只能调用一次。

我首先随机打乱数组的顺序,然后将两个值配对在一起 - 但我需要一种方法来防止某些配对比其他配对更频繁地发生(理想情况下,我希望它们一直平均递增(。

因此,我转向查看排列 - 并设法使用以下代码获得包含所有可能配对的完整数组:

    $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>项目中更简单地做到这一点。我也看过Math_Combinatorics PEAR包,但我不知道从哪里开始。

我将不胜感激任何帮助!

您可以使用循环赛算法

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
----

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

归地思考这个问题:一开始你有 n 个元素。从这些元素中,取第一个并从剩余的 n-1 元素中为其选择一个合作伙伴。将这对从列表中删除,并使用剩余的 n-2 元素回避。如果您使每个选择都无偏不倚,则其余配对也将是无偏见的。但这并不能保证您不会在不必要的时间之前重复自己。

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

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

所以它是奇数的乘积。这是A001147,也写成双阶乘m=(n-1(!.请注意,这些数字增长得相当快,所以即使对于中等的n(如n=16(,你可能也不必担心重复自己,因为有很多可能的配对可供选择,重复的可能性很小。

如果你真的想确保避免重复,你当然可以简单地生成整个列表并打乱它。但正如我刚才指出的,这个清单也可能变得很大。因此,我建议您将此问题分为两个步骤。找到一种方法来生成从 0 到 m-1 的所有数字,每个数字一次,并找到一种将这些数字转换为配对的方法。对于后者,您可以简单地逐步分解您的数字。在每个步骤中,index % (n-1)进行当前选择,并选择(int)(index / (n-1))作为递归调用中后续选择的索引。

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