Перестановки различного размера

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

$my_array = array(1,1,2,3);

Возможные перестановки разного размера:

1
1 // * See Note
2
3
1,1
1,2
1,3
// And so forth, for all the sets of size 2
1,1,2
1,1,3
1,2,1
// And so forth, for all the sets of size 3
1,1,2,3
1,1,3,2
// And so forth, for all the sets of size 4

Примечание: мне все равно, есть дубликат или нет. Для целей этого примера все будущие дубликаты опущены.

Что у меня есть в PHP:

function getPermutations($my_array){

$permutation_length = 1;
$keep_going = true;    

while($keep_going){
    while($there_are_still_permutations_with_this_length){
        // Generate the next permutation and return it into an array
        // Of course, the actual important part of the code is what I'm having trouble with.
    }
    $permutation_length++;
    if($permutation_length>count($my_array)){
        $keep_going = false;
    }
    else{
        $keep_going = true;
        }
}

return $return_array;

}

Самое близкое, что я могу придумать, - это перетасовать массив, выбрать первые n элементов, посмотреть, есть ли он уже в массиве результатов, а если нет, добавить его, а затем остановиться, когда математически больше нет возможных перестановок для этой длины . Но это некрасиво и неэффективно.

Приветствуются любые алгоритмы псевдокода.


Кроме того, для супер-пуперских (бесполезных) бонусных очков, есть ли способ получить только одну перестановку с функцией, но сделать так, чтобы ей не приходилось пересчитывать все предыдущие перестановки, чтобы получить следующую?

Например, я передаю ему параметр 3, что означает, что он уже сделал 3 перестановки, и он просто генерирует номер 4 без повторения предыдущих 3? (Передавать ему параметр не обязательно, его можно отслеживать в глобальном или статическом виде).

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


person waiwai933    schedule 11.06.2010    source источник


Ответы (4)


К сожалению, нет PHP-кода, но я могу дать вам алгоритм.

Это можно сделать с небольшим объемом памяти, и, поскольку вы не заботитесь о дубликатах, код также будет простым.

Во-первых: сгенерируйте все возможные подмножества.

Если вы рассматриваете подмножество как битовый вектор, вы можете увидеть, что существует соответствие 1-1 множеству и двоичному числу.

Итак, если в вашем массиве 12 элементов, у вас будет 2 ^ 12 подмножеств (включая пустой набор).

Итак, чтобы создать подмножество, вы начинаете с 0 и продолжаете увеличивать, пока не достигнете 2 ^ 12. На каждом этапе вы считываете установленные биты числа, чтобы получить соответствующее подмножество из массива.

Как только вы получите одно подмножество, вы можете теперь просмотреть его перестановки.

Следующая перестановка (индексов массива, а не самих элементов) может быть сгенерирована в лексикографическом порядке, как здесь: http://www.de-brauwer.be/wiki/wikka.php?wakka=Permutations и может выполняться с минимальным объемом памяти.

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

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

Надеюсь, это поможет. Удачи!

person Community    schedule 11.06.2010

Лучший набор функций, который я придумал, был предоставлен одним из пользователей в комментариях к функции перемешивания на php.net. Вот ссылка Работает очень хорошо.

Надеюсь, это будет полезно.

person Juan Cortés    schedule 11.06.2010

Проблема, похоже, заключается в попытке дать индекс для каждой перестановки и иметь постоянное время доступа. Я не могу придумать алгоритм постоянного времени, но, возможно, вы сможете улучшить его, чтобы он был таким. Этот алгоритм имеет временную сложность O (n), где n - длина вашего набора. Сложность пространства должна быть сведена к O (1).

Предположим, что наш набор равен 1,1,2,3, и нам нужна 10-я перестановка. Также обратите внимание, что мы будем индексировать каждый элемент набора от 0 до 3. В соответствии с вашим порядком это означает, что сначала идут перестановки одного элемента, затем два элемента и так далее. Мы будем вычитать из числа 10, пока не сможем полностью определить 10-ю перестановку.

Сначала идут перестановки отдельных элементов. Их 4, поэтому мы можем рассматривать это как вычитание единицы четыре раза из 10. У нас осталось 6, поэтому ясно, что нам нужно начать рассмотрение двух перестановок элементов. Их 12, и мы можем рассматривать это как вычитание от трех до четырех раз из 6. Мы обнаруживаем, что при втором вычитании 3 у нас остается 0. Это означает, что индексы нашей перестановки должны быть равны 2 (потому что мы вычитали 3 дважды) и 0, потому что 0 - это остаток. Следовательно, наша перестановка должна быть 2,1.

Деление и модуль могут вам помочь.

Если бы мы искали 12-ю перестановку, мы бы столкнулись с ситуацией, когда у нас есть остаток 2. В зависимости от вашего желаемого поведения перестановка 2,2 может быть недействительной. Однако обойти это очень просто, поскольку мы можем тривиально обнаружить, что индексы 2 и 2 (не путать с элементом) одинаковы, поэтому второй должен быть увеличен до 3. Таким образом, 12-я перестановка может тривиально быть рассчитывается как 2,3.

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

person erisco    schedule 11.06.2010

Входные данные: индекс перестановки k, индексированное множество S.

Псевдокод:

L = {S_1}
for i = 2 to |S| do
 Insert S_i before L_{k % i}
 k <- k / i
loop
return L

Этот алгоритм также можно легко изменить для работы с дубликатами.

person Jason Sanchez    schedule 14.06.2010