У меня есть ассоциативный массив:

$arr = [];
$arr['One'] = 1;
$arr['Two'] = 2;
$arr['Three'] = 3;
$arr['Four'] = 4;
$arr['Five'] = 5;
$arr['Six'] = 6;

Из которого я хотел бы генерировать пары перестановок с ключами:

$keys = array_keys($arr);
$result = generatePermutations($keys);

Где $result будет массивом уникальных пар:

//as per example, $result =
$result = [[['One','Two'],['Three','Four'],['Five','Six']],
           [['One','Three'],['Two','Four'],['Five','Six']],
           [['One','Four'],['Two','Three'],['Five','Six']],
           [['One','Five'],['Two','Three'],['Four','Six']],
           [['One','Two'],['Three','Five'],['Four','Six']],
           [['One','Three'],['Two','Five'],['Four','Six']],
           [['One','Six'],['Two','Three'],['Four','Five']],
           [['One','Two'],['Three','Six'],['Four','Five']],
           etc..
          ];

Я нашел несколько способов генерировать перестановки, но большинство из них не фокусировались конкретно на парах, и многие из них помещали все перестановки в один массив.

0
Banana 3 Сен 2017 в 11:13

5 ответов

Лучший ответ

Я собираюсь назвать это «рабочим решением» в лучшем случае. Конечно, возможно, что у меня есть избыточные фильтры или потраченные впустую итерации, но я разрабатывал и отлаживал это слишком много часов сейчас, и я больше не острый. Если / когда я найду способы улучшить этот блок кода (или кто-то предложит предложение), я обновлю свой ответ.

Код: (Демонстрация)

function pairedPerms($arr){
    $val1=$arr[0];
    $pairs_per_set=sizeof($arr)/2;
    foreach($arr as $v1){  // $arr is preserved/static
        $arr=array_slice($arr,1);  // modify/reduce second foreach's $arr
        foreach($arr as $v2){
            if($val1==$v1){
                $first[]=[$v1,$v2];  // unique pairs as 2-d array containing first element
            }else{
                $other[]=[$v1,$v2]; // unique pairs as 2-d array not containing first element
            }            
        }
    }

    for($i=0; $i<$pairs_per_set; ++$i){  // add one new set of pairs per iteration
        if($i==0){
            foreach($first as $pair){
                $perms[]=[$pair]; // establish an array of sets containing just one pair
            }
        }else{
            $expanded_perms=[];
            foreach($perms as $set){
                $values_in_set=[];  // clear previous data from exclusion array
                array_walk_recursive($set,function($v)use(&$values_in_set){$values_in_set[]=$v;}); // exclude pairs containing these values
                $candidates=array_filter($other,function($a)use($values_in_set){
                    return !in_array($a[0],$values_in_set) && !in_array($a[1],$values_in_set);
                });
                if($i<$pairs_per_set-1){
                    $candidates=array_slice($candidates,0,sizeof($candidates)/2);  // omit duplicate causing candidates
                }
                foreach($candidates as $cand){
                    $expanded_perms[]=array_merge($set,[$cand]); // add one set for every new qualifying pair
                }
            }
            $perms=$expanded_perms;  // overwrite earlier $perms data with new forked data
        }
    }
    return $perms;
}
//$arr=['One'=>1,'Two'=>2];
//$arr=['One'=>1,'Two'=>2,'Three'=>3,'Four'=>4];
$arr=['One'=>1,'Two'=>2,'Three'=>3,'Four'=>4,'Five'=>5,'Six'=>6];
//$arr=['One'=>1,'Two'=>2,'Three'=>3,'Four'=>4,'Five'=>5,'Six'=>6,'Seven'=>7,'Eight'=>8];
$result=pairedPerms(array_keys($arr));
//var_export($result);

echo "[\n";
foreach($result as $sets){
    echo "\t[ ";
    foreach($sets as $pairs){
        echo "[",implode(',',$pairs),"]";
    }
    echo " ]\n";
}
echo "]";

Выход:

[
    [ [One,Two][Three,Four][Five,Six] ]
    [ [One,Two][Three,Five][Four,Six] ]
    [ [One,Two][Three,Six][Four,Five] ]
    [ [One,Three][Two,Four][Five,Six] ]
    [ [One,Three][Two,Five][Four,Six] ]
    [ [One,Three][Two,Six][Four,Five] ]
    [ [One,Four][Two,Three][Five,Six] ]
    [ [One,Four][Two,Five][Three,Six] ]
    [ [One,Four][Two,Six][Three,Five] ]
    [ [One,Five][Two,Three][Four,Six] ]
    [ [One,Five][Two,Four][Three,Six] ]
    [ [One,Five][Two,Six][Three,Four] ]
    [ [One,Six][Two,Three][Four,Five] ]
    [ [One,Six][Two,Four][Three,Five] ]
    [ [One,Six][Two,Five][Three,Four] ]
]
1
mickmackusa 7 Сен 2017 в 06:49

Итерируйте их дважды, чтобы сгенерировать уникальные комбинации, затем переберите комбинации, чтобы сформировать уникальные пары:

<?php
$arr = [];
$arr['One'] = 1;
$arr['Two'] = 2;
$arr['Three'] = 3;
$arr['Four'] = 4;

function generatePermutations($array) {
    $permutations = [];
    $pairs = [];
    $i = 0;
    foreach ($array as $key => $value) {
        foreach ($array as $key2 => $value2) {
            if ($key === $key2) continue;
            $permutations[] = [$key, $key2];
        }
        array_shift($array);
    }
    foreach ($permutations as $key => $value) {
        foreach ($permutations as $key2=>$value2) {
            if (!in_array($value2[0], $value) && !in_array($value2[1], $value)) {
                $pairs[] = [$value, $value2];
            }
        }
        array_shift($permutations);
    }
    return $pairs;
}
print_r(generatePermutations($arr));

демонстрация

2
ishegg 3 Сен 2017 в 09:34
 <?php
    $arr = [];
    $arr['One'] = 1;
    $arr['Two'] = 2;
    $arr['Three'] = 3;
    $arr['Four'] = 4;

    foreach($arr as $key1=>$val1) {
        foreach($arr as $key2=>$val2) {
            if($val1>$val2) continue;
            if($key1 !== $key2) {
                echo "[$key1, $key2], ";
            }
        }
    }
1
vladatr 3 Сен 2017 в 10:03

В питоне это так же просто, как

result = itertools.permutations(keys, 2)

Если вы хотите сделать это с нуля алгоритмически, вы можете реализовать рекурсивную функцию, подобную этой. Я написал что-то довольно наивное в Python, извините, это не PHP.

// permutation function
def permutations(my_list, target_len, curr_perm):
  // base case
  if len(curr_perm) = target_len:
    print(curr_perm)
    return

  for item in my_list:
    // don't add duplicates
    if item in curr_perm:
      continue

    next_perm = curr_perm.copy()
    next_perm.append(item)
    // recursive call 
    permutations(my_list, target_len, next_perm)


// generate permutations of length 2
permutations(['one', 'two', 'three'], 2, [])
-2
Reuben Crimp 3 Сен 2017 в 08:54

Чтобы упростить проблему, вы можете разделить ее на две части.

Во-первых, сгенерируйте все комбинации. Для этого вы можете использовать следующую функцию (идея взята из сообщения Тома Батлера ) :

function getCombinations(array $array)
{
    $num = count($array); 
    $total = pow(2, $num);

    for ($i = 1; $i < $total; $i++) {
        $combination = [];
        for ($j = 0; $j < $num; $j++) {
            if (pow(2, $j) & $i) {
                $combination[$j] = $array[$j];
            }
        }

        yield $combination;
    }
}

Затем вы можете отфильтровать все комбинации и оставить только те, в которых есть два элемента:

$keys = array_keys($arr);
$result = array_filter(
    iterator_to_array(getCombinations($keys)),
    function ($combination) {
        return count($combination) === 2;
    }
);

Вот демонстрационная версия.

1
sevavietl 3 Сен 2017 в 10:00