Intereting Posts
Какова цель ini_set () в php? (особенно для отчетов об ошибках) Как обновить значения в моей БД новыми значениями из тегов <select> и <input>? Установка Php-intl на XAMPP для Mac Lion 10.8 API Twitter: не получение электронной почты пользователя – Yii2 отправить ссылку, используя функцию php mail () Получить все перестановки массива PHP? несколько баз данных, рассматривающих проблемы с одним кодом php codebase? определить, открыт ли ресторан в данный момент (например, yelp), используя базу данных, php, js Список друзей facebook ошибка при использовании exec () для вызова скрипта python Строка Reformat JSON с форматами диаграмм php to morris Doctrine2 в ZF2 – DQL дает отличный результат, чем метод findOneBy PHP DOM: Как получить дочерние элементы по имени тега элегантным способом? Обтекание длинного текста в CSS PHP, как вызвать ошибку пользователя с trigger_error в деструкторе объекта, когда скрипт отключается?

Перестановка массива в PHP

Рассмотрим следующий массив:

$a = [['x'], ['y', 'z', 'w'], ['m', 'n']]; 

Как можно создать из него следующий массив:

 $output=[ [[x][y][m]], [[x][z][n]], [[x][w][m]], [[x][y][n]], [[x][z][m]], [[x][w][n]], ]; 

Я ищу более эффективный код, чем мой. (Мой текущий код представлен как ответ ниже)

Вот так. Предполагая, что:

 $array = [['x'], ['y', 'z', 'w'], ['m', 'n']]; 

EDIT: после некоторого тестирования производительности я пришел к выводу, что решение, которое я опубликовал ранее, примерно на 300% медленнее, чем код OP, безусловно, из-за сложной стекирования функций. Итак, вот улучшенная версия подхода OP, которая примерно на 40% быстрее :

 $count = array_map('count', $array); $finalSize = array_product($count); $arraySize = count($array); $output = array_fill(0, $finalSize, []); $i = 0; $c = 0; for (; $i < $finalSize; $i++) { for ($c = 0; $c < $arraySize; $c++) { $output[$i][] = $array[$c][$i % $count[$c]]; } } 

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

«более эффективный код» – такая субъективная вещь …. 😉
Вы можете использовать итераторы вместо массивов, поэтому полный результат не нужно хранить в памяти. С другой стороны, это решение, скорее всего, намного медленнее.

 <?php class PermIterator implements Iterator { protected $mi; protected $finalSize, $pos; public function __construct(array $src) { $mi = new MultipleIterator; $finalSize = 1; foreach ( $src as $a ) { $finalSize *= count($a); $mi->attachIterator( new InfiniteIterator(new ArrayIterator($a)) ); } $this->mi = $mi; $this->finalSize = $finalSize; $this->pos = 0; } public function current() { return $this->mi->current(); } public function key() { return $this->mi->key(); } public function next() { $this->pos+=1; $this->mi->next(); } public function rewind() { $this->pos = 0; $this->mi->rewind(); } public function valid() { return ($this->pos < $this->finalSize) && $this->mi->valid(); } } $src = $a = [['x'], ['y', 'z', 'w'], ['m', 'n']]; $pi = new PermIterator($src); // <- you can pass this one around instead of the array foreach ( $pi as $e ) { echo join(', ', $e), "\n"; } 

печать

 x, y, m x, z, n x, w, m x, y, n x, z, m x, w, n 

Или как массив (объект), где вы можете получить доступ к каждому элементу через целочисленное смещение

 <?php class PermArray implements ArrayAccess { // todo: constraints and error handling - it's just an example protected $source; protected $size; public function __construct($source) { $this->source = $source; $this->size = 1; foreach ( $source as $a ) { $this->size *= count($a); } } public function count() { return $this->size; } public function offsetExists($offset) { return is_int($offset) && $offset < $this->size; } public function offsetGet($offset) { $rv = array(); for ($c = 0; $c < count($this->source); $c++) { $index = ($offset + $this->size) % count($this->source[$c]); $rv[] = $this->source[$c][$index]; } return $rv; } public function offsetSet($offset, $value ){} public function offsetUnset($offset){} } $pa = new PermArray( [['x'], ['y', 'z', 'w'], ['m', 'n']] ); $cnt = $pa->count(); for($i=0; $i<$cnt; $i++) { echo join(', ', $pa[$i]), "\n"; } 
 <?php function array_permutation(array $a) { $count = array_map('count', $a); $finalSize = 1; foreach ($count as $val) { $finalSize *= $val; } $output = []; for ($i = 0; $i < $finalSize; $i++) { $output[$i] = []; for ($c = 0; $c < count($a); $c++) { $index = ($i + $finalSize) % $count[$c]; array_push($output[$i], $a[$c][$index]); } } return $output; } $a = [['x'], ['y', 'z', 'w'], ['m', 'n']]; $output= array_permutation($a); 

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

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

 // the input column sets to be permuted $column_sets = [ [ //set 1 ['Column 1' => 'c1v1'] ], [ //set 2 ['column 2' => 'c2v1', 'Column 3' => 'c3v1'], ['column 2' => 'c2v2', 'Column 3' => 'c3v2'], ], [ //set 3 ['Column 4' => 'c4v1', 'Column 5' => 'c5v1'], ['Column 4' => 'c4v2', 'Column 5' => 'c5v2'], ], [ //set 4 ['Column 6' => 'c6v1', 'Column 7' => 'c7v1'], ['Column 6' => 'c6v2', 'Column 7' => 'c7v2'], ['Column 6' => 'c6v3', 'Column 7' => 'c7v3'], ], [ //set 5 ['Column 8' => 'c8v1', 'Column 9' => 'c9v1'], ['Column 8' => 'c8v2', 'Column 9' => 'c9v2'], ['Column 8' => 'c8v3', 'Column 9' => 'c9v3'], ], ]; // copy the first set into the output to start $output_rows = $column_sets[0]; foreach ($column_sets as $column_set) { // save the current state of the rows for use within this loop $current_output = $output_rows; // calculate the number of permutations necessary to combine the output rows with the current column set $permutations = count($output_rows) * count($column_set); for ($permutation=0; $permutation < $permutations; $permutation++) { // if the current permutation index is grater than the number of rows in the output, // then copy a row from the pre-permutation output rows, repeating as necessary if ($permutation > count($output_rows) - 1) { $output_rows[] = $current_output[$permutation % count($current_output)]; } // copy the columns from the column set foreach ($column_set[0] as $key => $value) { $output_rows[$permutation][$key] = $column_set[intval($permutation / count($current_output)) % count($column_set)][$key]; } } } echo "After Permutaion:\n"; echo implode("\t", array_keys($output_rows[0])) . PHP_EOL; foreach ($output_rows as $row) { echo implode("\t", array_values($row)) . PHP_EOL; } 
 [x][y][z] [x][z][y] [y][x][z] [y][z][x] [z][x][y] [z][y][x] 

Я думаю, что ваша проблема не перестановка, а комбинаторика, потому что если перестановка вашего кода будет выводить то, что написано выше, если задано массив {x, y, z}, то формула для перестановки состоит в том, что n! / (N-1)! в этом случае это 6. так шесть перестановок.