Используя PHP, произвольно соединяйте группу элементов, не связывая их с самим собой, без прямых пар

Предположим, у вас есть набор элементов в массиве.

A, B, C, D, E, F, G, H

Используя PHP, как бы вы случайно соедирали буквы вместе, не соединяя их с дубликатом самих себя?

Например:

A->pairedLetter = G B->pairedLetter = C C->pairedLetter = E D->pairedLetter = A E->pairedLetter = B F->pairedLetter = D G->pairedLetter = F 

и так далее…

EDIT: О, а также, если A сопряжен с F, F не может быть сопряжен с A. Таким образом, должно быть так же много отношений, что и элементы.

Solutions Collecting From Web of "Используя PHP, произвольно соединяйте группу элементов, не связывая их с самим собой, без прямых пар"

Как насчет этого ?

 // input array $arr = array('A','B','C','D','E','F'); // result array $res = array(); // get first element and save it $first = $ele1 = array_shift($arr); while(count($arr)) { // get random element $ele2 = array_rand($arr); // associate elements $res[$ele1] = $arr[$ele2]; // random element becomes next element $ele1 = $arr[$ele2]; // delete the random element array_splice($arr, $ele2, 1); } // associate last element woth the first one $res[$ele1] = $first; print_r($res); 

Вывод:

 Array ( [A] => B [B] => F [F] => E [E] => D [D] => C [C] => A ) 

Работает с четным количеством массивов элементов, а также нечетным.

Обновление с использованием алгоритма Криса:

 $arr = array('A','B','C','D','E','F'); shuffle($arr); $res=array(); for($i=0;$i<count($arr);$i++) { $res[$arr[$i]] = $arr[$i+1]; } $res[$arr[count($arr)-1]] = $arr[0]; 

Дублируйте массив. shuffle вызов на один массив. Затем:

  1. Убедитесь, что первые элементы разные (если они переключают первые два элемента одного массива).
  2. array_pop от элемента из каждого массива, чтобы сформировать пару.
  3. Если элементы одинаковые, поп-два от одного массива и пары вверх, затем два с другого.

редактировать

С дополнительным условием вам нужно будет проверить, что каждая пара еще не существует. Если это произойдет (на третьем этапе), вы можете попробовать использовать разные комбинации (выскочить с первого и третьего {pop off first, second, third и push second back on}), но вы можете по-прежнему в конечном итоге иметь недопустимую пару в конце так, чтобы нужно будет начать снова.

Нужно ли быть абсолютно случайным с учетом ваших ограничений? Если бы вы захотели добавить другое ограничение, вы могли бы сделать проблему очень простой. Если бы вы захотели сделать это так, чтобы все сопоставления образовывали один цикл, вы могли просто перетасовать ваш массив, а затем сделать элемент один pont в два, два точки в три, а последний элемент – на первом. Вы гарантируете, что ни один элемент не может poitn на себя (если только массив имеет только один элемент), ни один элемент не укажет на элемент, указывающий на него (если только массив имеет только два элемента), и что ни один элемент не будет указываться больше, чем один предмет.

Вы ограничиваете свои результаты множеством, чтобы потерять определенное количество случайного элемента, но если вы довольны этим своим чрезвычайно симпалом.

 $array = array( 'A','B','C', 'D','E','F', 'G','H','I','J' ); $new = array(); if(count($array) % 2 != 0) { array_pop($array); // Got to remove 1 element to make them even. } foreach($array as $item) { $_t = array_pop($array); //Key if(!isset($new[$item])) { $new[$item] = $_t; $new[$_t] = $item; } } var_dump($new); 

Это напечатает:

 array(10){ ["A"]=> string(1) "J" ["J"]=> string(1) "A" ["B"]=> string(1) "I" ["I"]=> string(1) "B" ["C"]=> string(1) "H" ["H"]=> string(1) "C" ["D"]=> string(1) "G" ["G"]=> string(1) "D" ["E"]=> string(1) "F" ["F"]=> string(1) "E" } 

то, как это работает, он циклически меняет значения и одновременно удаляет значение, поэтому вы всегда имеете сразу два значения, но при этом уменьшаете массив на 1.

то, когда мы устанавливаем новые ключи, они устанавливают оба из них в новый массив, поэтому в следующий раз, когда цикл повторяется, если одно и то же значение возвращается, оно просто отбрасывается 🙂

Я попытался использовать копию исходного массива и использовать array_rand (), но если он найдет тот же элемент, что и итерация, мне нужно снова запустить array_rand (), который может попасть в бесконечный цикл.

Я в тупике, должен быть элегантный способ сделать это.

 $arrayValues = range('A','H'); shuffle($arrayValues); while (count($arrayValues) > 0) { echo array_pop($arrayValues),' is matched with ',array_pop($arrayValues),'<br />'; } 

РЕДАКТИРОВАТЬ

следующие изменения в вопросе

 $arrayValues = range('A','H'); $tmpArrayValues = $arrayValues; $pairs = array(); shuffle($tmpArrayValues); foreach($arrayValues as $arrayValue) { $tmpValue = array_pop($tmpArrayValues); while (($arrayValue == $tmpValue) || ((isset($pairs[$tmpValue])) && ($pairs[$tmpValue] == $arrayValue))) { array_unshift($tmpArrayValues,$tmpValue); $tmpValue = array_pop($tmpArrayValues); } $pairs[$arrayValue] = $tmpValue; } foreach($pairs as $key => $value) { echo $key,' is matched with ',$value,'<br />'; } 

Используя функцию тасования в массиве и проверяя, что сопоставления верны (A не сопоставляется с A и что если A сопоставляется с B, то B не отображается в A). Следующее должно делать то, что вы хотите:

 $values = array('A','B','C','D','E','F','G'); $new_values = array('A','B','C','D','E','F','G'); $random = shuffle($new_values); //randomly shuffle the order of the second array $mappings = array(); $validMappings = false; //continue to retry alternative mappings until a valid result is found while(!$validMappings) { //set the mappings from $values to $new_values for($i = 0; $i < count($values); $i++) { $mappings[$values[$i]] = $new_values[$i]; } $validMappings = true; //check validity of the current mapping foreach ($mappings as $key=>$value) { if($mappings[$key] == $mappings[$value]) { $validMappings = false; break; } } //if false shuffle the new values and test again if(!$validMappings) { $random = shuffle($new_values); } } print_r($mappings);