Алгоритм для створення всіх можливих перестановок списку?


119

Скажіть, у мене є список з n елементів, я знаю, що є n! можливі способи замовлення цих елементів. Що таке алгоритм для створення всіх можливих замовлень цього списку? Наприклад, у мене є список [a, b, c]. Алгоритм повертав би [[a, b, c], [a, c, b,], [b, a, c], [b, c, a], [c, a, b], [c, b , а]].

Я читаю це тут http://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations

Але Вікіпедія ніколи не вміла пояснювати. Я не дуже цього розумію.


5
Я написав обширну відповідь на інше питання про генерацію перестановок одного разу. Я думаю , це буде представляти інтерес для Вас: stackoverflow.com/questions/1506078 / ...
Joren

2
Це може вирішити вашу проблему en.wikipedia.org/wiki/Heap's_algorithm
Фелікс

Відповіді:


96

В основному для кожного елемента зліва направо генеруються всі перестановки решти елементів (і кожен додається до поточних елементів). Це можна робити рекурсивно (або ітеративно, якщо вам подобається біль), поки не буде досягнуто останнього пункту, в якому є лише один можливий порядок.

Отже зі списком [1,2,3,4] генеруються всі перестановки, які починаються з 1, потім усі перестановки, які починаються з 2, потім 3, потім 4.

Це ефективно зменшує проблему від одного пошуку перестановок списку з чотирьох елементів до списку з трьох елементів. Після зменшення до 2, а потім 1 списку елементів, всі вони знайдуться.
Приклад показу перестановок процесу за допомогою 3 кольорових кульок:
Кульки червоного, зеленого та синього кольорів упорядковували зображення перестановок(від https://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB. svg )


2
Спочатку я теж думав про це, але потім поточний елемент не став би розміщуватися між деякими з наступних. Таким чином, не всі перестановки будуть створені.
fent

@LLer Вибачте, я уточнив свою відповідь від "наступного" до "залишився", щоб уточнити. Хоча це чудово працює. Перевірте це, написавши код і переконавшись, що отримаєте 4! різні результати.
WhirlWind

2
int перестановки (int n, вектор <int> a) {статичний int num_permutations = 0; if (n == (a.size () - 1)) {for (int i = 0; i <a.size (); i ++) cout << a [i] << ""; cout << "\ n"; num_permutations ++; } else {for (int i = n + 1; i <= a.size (); i ++) {перестановки (n + 1, a); if (i <a.size ()) int temp = a [n], a [n] = a [i], a [i] = temp; }} повернути num_permutations; } int main (void) {вектор <int> v; v.push_back (1); ... повернути перестановки (0, v); }
Сомеш

На жаль, не знаєте, як відформатувати код у коментарі ... Випробував код із 7 та отримав 5040. Завдяки @WhirlWind за пропозицію.
Сомеш

Хіба це альго не зміниться, якщо ви можете мати 2 або 3 червоні кульки №1, а не лише 1 кожного кольору?
Олександр Міллс

26

Ось алгоритм в Python, який працює на місці масиву:

def permute(xs, low=0):
    if low + 1 >= len(xs):
        yield xs
    else:
        for p in permute(xs, low + 1):
            yield p        
        for i in range(low + 1, len(xs)):        
            xs[low], xs[i] = xs[i], xs[low]
            for p in permute(xs, low + 1):
                yield p        
            xs[low], xs[i] = xs[i], xs[low]

for p in permute([1, 2, 3, 4]):
    print p

Ви можете спробувати код для себе тут: http://repl.it/J9v


Можете пояснити, будь ласка, частину врожайності? Я не зміг запустити код. Заздалегідь спасибі.
Agniswar Bakshi

Питання про переповнення стека в stackoverflow.com/questions/104420/… заявляє, що існує стандартний модуль бібліотеки у версіях 2.6 далі і має відповідь, що забезпечує 6-рядкове рішення у функції отримання перестановок списку.
Едвард

@Agniswar На перший погляд, оператор виходу використовується для визначення генераторів, замінюючи повернення функції, щоб забезпечити результат своєму виклику без руйнування локальних змінних. На відміну від функції, де кожен виклик починається з нового набору змінних, генератор відновить виконання там, де він був відключений. pythoncentral.io/python-generators-and-yield-keyword
MSS

Це рішення не працюватиме при обробці списку однакових записів.
KaiserKatze

Дякую, що поділились. Це інтуїтивно та ефективно, хоча результат не в лексикографічному порядку.
Сем

16

Тут вже є багато хороших рішень, але я хотів би поділитися тим, як я вирішив цю проблему самостійно, і сподіваюся, що це може бути корисним для того, хто також хотів би отримати своє власне рішення.

Після роздумів над проблемою я прийшов до двох наступних висновків:

  1. Для списку Lрозмірів nбуде рівна кількість рішень, починаючи з L 1 , L 2 ... L n елементів списку. Оскільки в цілому є n!перестановки списку розмірів n, ми отримуємо n! / n = (n-1)!перестановки в кожній групі.
  2. У списку з 2 елементів є лише 2 перестановки => [a,b]і [b,a].

За допомогою цих двох простих ідей я вивів наступний алгоритм:

permute array
    if array is of size 2
       return first and second element as new array
       return second and first element as new array
    else
        for each element in array
            new subarray = array with excluded element
            return element + permute subarray

Ось як я реалізував це в C #:

public IEnumerable<List<T>> Permutate<T>(List<T> input)
{
    if (input.Count == 2) // this are permutations of array of size 2
    {
        yield return new List<T>(input);
        yield return new List<T> {input[1], input[0]}; 
    }
    else
    {
        foreach(T elem in input) // going through array
        {
            var rlist = new List<T>(input); // creating subarray = array
            rlist.Remove(elem); // removing element
            foreach(List<T> retlist in Permutate(rlist))
            {
                retlist.Insert(0,elem); // inserting the element at pos 0
                yield return retlist;
            }

        }
    }
}

16

Відповідь Вікіпедії на "лексикографічний порядок" здається мені абсолютно чіткою у кулінарній книзі. Він посилається на походження алгоритму 14 століття!

Я щойно написав швидку реалізацію в Java алгоритму Вікіпедії як перевірку, і це не було проблем. Але те, що ви маєте у своєму Q як приклад - НЕ "перелік усіх перестановок", а "СПИСОК усіх перестановок", тож wikipedia не буде для вас великою допомогою. Вам потрібна мова, на якій переліки перестановок доцільно побудовані. І повірте, списки довжиною в кілька мільярдів зазвичай не обробляються загальнообов'язковими мовами. Ви дійсно хочете, щоб не строга функціональна мова програмування, в якій списки є першокласним об'єктом, вийшла з матеріалів, не наближаючи машину до теплової загибелі Всесвіту.

Це просто. У стандартній Haskell або будь-якій сучасній мові FP:

-- perms of a list
perms :: [a] -> [ [a] ]
perms (a:as) = [bs ++ a:cs | perm <- perms as, (bs,cs) <- splits perm]
perms []     = [ [] ]

і

-- ways of splitting a list into two parts
splits :: [a] -> [ ([a],[a]) ]
splits []     = [ ([],[]) ]
splits (a:as) = ([],a:as) : [(a:bs,cs) | (bs,cs) <- splits as]

9

Як сказав WhirlWind, ви починаєте спочатку.

Ви поміняєте курсор кожним залишковим значенням, включаючи сам курсор, це все нові екземпляри (я використав int[]і array.clone()в прикладі).

Потім виконайте перестановки у всіх цих різних списках, переконуючись, що курсор знаходиться праворуч.

Коли більше не залишилося значень (курсор знаходиться в кінці), надрукуйте список. Це умова зупинки.

public void permutate(int[] list, int pointer) {
    if (pointer == list.length) {
        //stop-condition: print or process number
        return;
    }
    for (int i = pointer; i < list.length; i++) {
        int[] permutation = (int[])list.clone();.
        permutation[pointer] = list[i];
        permutation[i] = list[pointer];
        permutate(permutation, pointer + 1);
    }
}

8

Рекурсивний завжди потребує певних розумових зусиль для підтримки. А для великої кількості, факториал легко величезний, і переповнення стека легко стане проблемою.

Для невеликих чисел (3 або 4, що зустрічається в основному), кілька петель досить прості і прямі вперед. На жаль, відповіді з циклами не проголосували.

Почнемо з перерахування (а не з перестановки). Просто прочитайте код як псевдо-перл-код.

$foreach $i1 in @list
    $foreach $i2 in @list 
        $foreach $i3 in @list
            print "$i1, $i2, $i3\n"

Перерахування зустрічається частіше, ніж перестановка, але якщо потрібна перестановка, просто додайте умови:

$foreach $i1 in @list
    $foreach $i2 in @list 
        $if $i2==$i1
            next
        $foreach $i3 in @list
            $if $i3==$i1 or $i3==$i2
                next
            print "$i1, $i2, $i3\n"

Тепер, якщо вам справді потрібен загальний метод потенційно для великих списків, ми можемо використовувати метод radix. Спочатку розглянемо проблему перерахування:

$n=@list
my @radix
$for $i=0:$n
    $radix[$i]=0
$while 1
    my @temp
    $for $i=0:$n
        push @temp, $list[$radix[$i]]
    print join(", ", @temp), "\n"
    $call radix_increment

subcode: radix_increment
    $i=0
    $while 1
        $radix[$i]++
        $if $radix[$i]==$n
            $radix[$i]=0
            $i++
        $else
            last
    $if $i>=$n
        last

Приріст Radix - це по суті підрахунок числа (в основі кількості елементів списку).

Тепер, якщо вам потрібна перестановка, просто додайте чеки всередині циклу:

subcode: check_permutation
    my @check
    my $flag_dup=0
    $for $i=0:$n
        $check[$radix[$i]]++
        $if $check[$radix[$i]]>1
            $flag_dup=1
            last
    $if $flag_dup
        next

Редагувати: наведений вище код повинен працювати, але для перестановки radix_increment може бути марним. Отже, якщо час є практичним питанням, ми повинні змінити radix_increment на permute_inc:

subcode: permute_init
    $for $i=0:$n
        $radix[$i]=$i

subcode: permute_inc                                       
    $max=-1                                                
    $for $i=$n:0                                           
        $if $max<$radix[$i]                                
            $max=$radix[$i]                                
        $else                                              
            $for $j=$n:0                                   
                $if $radix[$j]>$radix[$i]                  
                    $call swap, $radix[$i], $radix[$j]     
                    break                                  
            $j=$i+1                                        
            $k=$n-1                                        
            $while $j<$k                                   
                $call swap, $radix[$j], $radix[$k]         
                $j++                                       
                $k--                                       
            break                                          
    $if $i<0                                               
        break                                              

Звичайно, зараз цей код логічно складніший, я залишу для вправ читача.


7

введіть тут опис зображення

// C program to print all permutations with duplicates allowed
#include <stdio.h>
#include <string.h>

/* Function to swap values at two pointers */
void swap(char *x, char *y)
{
    char temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

/* Function to print permutations of string
   This function takes three parameters:
   1. String
   2. Starting index of the string
   3. Ending index of the string. */

void permute(char *a, int l, int r)
{
   int i;
   if (l == r)
     printf("%s\n", a);
   else
   {
       for (i = l; i <= r; i++)
       {
          swap((a+l), (a+i));
          permute(a, l+1, r);
          swap((a+l), (a+i)); //backtrack
       }
   }
}

/* Driver program to test above functions */
int main()
{
    char str[] = "ABC";
    int n = strlen(str);
    permute(str, 0, n-1);
    return 0;
}

Довідка: Geeksforgeeks.org


5

Якщо хтось цікавиться, як це зробити в перестановці в JavaScript.

Ідея / псевдокод

  1. виберіть по одному елементу
  2. перестановіть решту елемента, а потім додайте вибраний елемент до всієї перестановки

наприклад. 'a' + перестановка (bc). перестановка Bc буде bc & cb. Тепер додайте, ці два дадуть abc, acb. аналогічно, вибір b + permute (ac) призведе до bac, bca ... і продовжуватиметься.

тепер подивіться на код

function permutations(arr){

   var len = arr.length, 
       perms = [],
       rest,
       picked,
       restPerms,
       next;

    //for one or less item there is only one permutation 
    if (len <= 1)
        return [arr];

    for (var i=0; i<len; i++)
    {
        //copy original array to avoid changing it while picking elements
        rest = Object.create(arr);

        //splice removed element change array original array(copied array)
        //[1,2,3,4].splice(2,1) will return [3] and remaining array = [1,2,4]
        picked = rest.splice(i, 1);

        //get the permutation of the rest of the elements
        restPerms = permutations(rest);

       // Now concat like a+permute(bc) for each
       for (var j=0; j<restPerms.length; j++)
       {
           next = picked.concat(restPerms[j]);
           perms.push(next);
       }
    }

   return perms;
}

Знайдіть свій час, щоб зрозуміти це. Я отримав цей код ( pertumation в JavaScript )


Я думав про щось подібне, але чи варто вам не додавати вибраний елемент як передній, так і в кінці рештиParams? У цьому випадку для 'abc', якщо ви вибираєте a, перестановки 'bc' - це "bc" і "cb". Коли ви додаєте "a" назад до списку, не слід додавати його на передню частину як "a + bc" + "a + cb", але також в кінці як "bc + a" + "cb + a" список?
Артимус

Ці перестановки ви отримаєте, коли перестановите, починаючи відповідно з "b" і "c". (тобто другий і третій прогони зовнішньої петлі "для")
Райан О'Нілл

3

Ще один у Python, він не на місці, як @ cdiggins's, але я думаю, що це легше зрозуміти

def permute(num):
    if len(num) == 2:
        # get the permutations of the last 2 numbers by swapping them
        yield num
        num[0], num[1] = num[1], num[0]
        yield num
    else:
        for i in range(0, len(num)):
            # fix the first number and get the permutations of the rest of numbers
            for perm in permute(num[0:i] + num[i+1:len(num)]):
                yield [num[i]] + perm

for p in permute([1, 2, 3, 4]):
    print p

3

Я думав написати код для отримання перестановок будь-якого заданого цілого числа будь-якого розміру, тобто надання числа 4567, ми отримаємо всі можливі перестановки до 7654 ... Тож я працював над цим і знайшов алгоритм і, нарешті, його реалізував. Ось - код, написаний на "с". Ви можете просто скопіювати його та запустити будь-який компілятор з відкритим кодом. Але деякі недоліки чекають налагодження. Будь ласка, оцініть.

Код:

#include <stdio.h>
#include <conio.h>
#include <malloc.h>

                //PROTOTYPES

int fact(int);                  //For finding the factorial
void swap(int*,int*);           //Swapping 2 given numbers
void sort(int*,int);            //Sorting the list from the specified path
int imax(int*,int,int);         //Finding the value of imax
int jsmall(int*,int);           //Gives position of element greater than ith but smaller than rest (ahead of imax)
void perm();                    //All the important tasks are done in this function


int n;                         //Global variable for input OR number of digits

void main()
{
int c=0;

printf("Enter the number : ");
scanf("%d",&c);
perm(c);
getch();
}

void perm(int c){
int *p;                     //Pointer for allocating separate memory to every single entered digit like arrays
int i, d;               
int sum=0;
int j, k;
long f;

n = 0;

while(c != 0)               //this one is for calculating the number of digits in the entered number
{
    sum = (sum * 10) + (c % 10);
    n++;                            //as i told at the start of loop
    c = c / 10;
}

f = fact(n);                        //It gives the factorial value of any number

p = (int*) malloc(n*sizeof(int));                //Dynamically allocation of array of n elements

for(i=0; sum != 0 ; i++)
{
    *(p+i) = sum % 10;                               //Giving values in dynamic array like 1234....n separately
    sum = sum / 10;
}

sort(p,-1);                                         //For sorting the dynamic array "p"

for(c=0 ; c<f/2 ; c++) {                        //Most important loop which prints 2 numbers per loop, so it goes upto 1/2 of fact(n)

    for(k=0 ; k<n ; k++)
        printf("%d",p[k]);                       //Loop for printing one of permutations
    printf("\n");

    i = d = 0;
    i = imax(p,i,d);                            //provides the max i as per algo (i am restricted to this only)
    j = i;
    j = jsmall(p,j);                            //provides smallest i val as per algo
    swap(&p[i],&p[j]);

    for(k=0 ; k<n ; k++)
        printf("%d",p[k]);
    printf("\n");

    i = d = 0;
    i = imax(p,i,d);
    j = i;
    j = jsmall(p,j);
    swap(&p[i],&p[j]);

    sort(p,i);
}
free(p);                                        //Deallocating memory
}

int fact (int a)
{
long f=1;
while(a!=0)
{
    f = f*a;
    a--;
}
return f;
}


void swap(int *p1,int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
return;
}


void sort(int*p,int t)
{
int i,temp,j;
for(i=t+1 ; i<n-1 ; i++)
{
    for(j=i+1 ; j<n ; j++)
    {
        if(*(p+i) > *(p+j))
        {
            temp = *(p+i);
            *(p+i) = *(p+j);
            *(p+j) = temp;
        }
    }
}
}


int imax(int *p, int i , int d)
{
    while(i<n-1 && d<n-1)
{
    if(*(p+d) < *(p+d+1))
    {   
        i = d;
        d++;
    }
    else
        d++;
}
return i;
}


int jsmall(int *p, int j)
{
int i,small = 32767,k = j;
for (i=j+1 ; i<n ; i++)
{
    if (p[i]<small && p[i]>p[k])
    {     
       small = p[i];
       j = i;
    }
}
return j;
}

3
void permutate(char[] x, int i, int n){
    x=x.clone();
    if (i==n){
        System.out.print(x);
        System.out.print(" ");
        counter++;}
    else
    {
        for (int j=i; j<=n;j++){
     //   System.out.print(temp); System.out.print(" ");    //Debugger
        swap (x,i,j);
      //  System.out.print(temp); System.out.print(" "+"i="+i+" j="+j+"\n");// Debugger
        permutate(x,i+1,n);
    //    swap (temp,i,j);
    }
    }
}

void swap (char[] x, int a, int b){
char temp = x[a];
x[a]=x[b];
x[b]=temp;
}

Я створив цю. засновані на дослідженні занадто пермутатно (qwe, 0, qwe.length-1); Тільки щоб ви знали, ви можете це робити з назад або без нього


3

Ось іграшковий метод Ruby, який працює так #permutation.to_a, може бути більш розбірливим для божевільних людей. Це привіт повільно, але також 5 ліній.

def permute(ary)
  return [ary] if ary.size <= 1
  ary.collect_concat.with_index do |e, i|
    rest = ary.dup.tap {|a| a.delete_at(i) }
    permute(rest).collect {|a| a.unshift(e) }
  end
end

3

Я написав це рекурсивне рішення в ANSI C. Кожне виконання функції Permutate забезпечує одну різну перестановку, поки всі не будуть завершені. Глобальні змінні можуть також використовуватися для фактів та кількості змінних.

#include <stdio.h>
#define SIZE 4

void Rotate(int vec[], int size)
{
    int i, j, first;

    first = vec[0];
    for(j = 0, i = 1; i < size; i++, j++)
    {
        vec[j] = vec[i];
    }
    vec[j] = first;
}

int Permutate(int *start, int size, int *count)
{
    static int fact;

    if(size > 1)
    {
        if(Permutate(start + 1, size - 1, count))
        {
            Rotate(start, size);
        }
        fact *= size;
    }
    else
    {
        (*count)++;
        fact = 1;
    }

    return !(*count % fact);
}

void Show(int vec[], int size)
{
    int i;

    printf("%d", vec[0]);
    for(i = 1; i < size; i++)
    {
        printf(" %d", vec[i]);
    }
    putchar('\n');
}

int main()
{
    int vec[] = { 1, 2, 3, 4, 5, 6 }; /* Only the first SIZE items will be permutated */
    int count = 0;

    do
    {
        Show(vec, SIZE);
    } while(!Permutate(vec, SIZE, &count));

    putchar('\n');
    Show(vec, SIZE);
    printf("\nCount: %d\n\n", count);

    return 0;
}

3

Версія Java

/**
 * @param uniqueList
 * @param permutationSize
 * @param permutation
 * @param only            Only show the permutation of permutationSize,
 *                        else show all permutation of less than or equal to permutationSize.
 */
public static void my_permutationOf(List<Integer> uniqueList, int permutationSize, List<Integer> permutation, boolean only) {
    if (permutation == null) {
        assert 0 < permutationSize && permutationSize <= uniqueList.size();
        permutation = new ArrayList<>(permutationSize);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
    }
    for (int i : uniqueList) {
        if (permutation.contains(i)) {
            continue;
        }
        permutation.add(i);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        } else if (permutation.size() == permutationSize) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
        if (permutation.size() < permutationSize) {
            my_permutationOf(uniqueList, permutationSize, permutation, only);
        }
        permutation.remove(permutation.size() - 1);
    }
}

Напр

public static void main(String[] args) throws Exception { 
    my_permutationOf(new ArrayList<Integer>() {
        {
            add(1);
            add(2);
            add(3);

        }
    }, 3, null, true);
}

вихід:

  [1, 2, 3]
  [1, 3, 2]
  [2, 1, 3]
  [2, 3, 1]
  [3, 1, 2]
  [3, 2, 1]

3

в PHP

$set=array('A','B','C','D');

function permutate($set) {
    $b=array();
    foreach($set as $key=>$value) {
        if(count($set)==1) {
            $b[]=$set[$key];
        }
        else {
            $subset=$set;
            unset($subset[$key]);
            $x=permutate($subset);
            foreach($x as $key1=>$value1) {
                $b[]=$value.' '.$value1;
            }
        }
    }
    return $b;
}

$x=permutate($set);
var_export($x);

3

Ось код у Python для друку всіх можливих перестановок списку:

def next_perm(arr):
    # Find non-increasing suffix
    i = len(arr) - 1
    while i > 0 and arr[i - 1] >= arr[i]:
        i -= 1
    if i <= 0:
        return False

    # Find successor to pivot
    j = len(arr) - 1
    while arr[j] <= arr[i - 1]:
        j -= 1
    arr[i - 1], arr[j] = arr[j], arr[i - 1]

    # Reverse suffix
    arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
    print arr
    return True

def all_perm(arr):
    a = next_perm(arr)
    while a:
        a = next_perm(arr)
    arr = raw_input()
    arr.split(' ')
    arr = map(int, arr)
    arr.sort()
    print arr
    all_perm(arr)

Я використовував алгоритм лексикографічного порядку, щоб отримати всі можливі перестановки, але рекурсивний алгоритм є більш ефективним. Код рекурсивного алгоритму можна знайти тут: Перестановки рекурсії Python


3
public class PermutationGenerator
{
    private LinkedList<List<int>> _permutationsList;
    public void FindPermutations(List<int> list, int permutationLength)
    {
        _permutationsList = new LinkedList<List<int>>();
        foreach(var value in list)
        {
            CreatePermutations(value, permutationLength);
        }
    }

    private void CreatePermutations(int value, int permutationLength)
    {
        var node = _permutationsList.First;
        var last = _permutationsList.Last;
        while (node != null)
        {
            if (node.Value.Count < permutationLength)
            {
                GeneratePermutations(node.Value, value, permutationLength);
            }
            if (node == last)
            {
                break;
            }
            node = node.Next;
        }

        List<int> permutation = new List<int>();
        permutation.Add(value);
        _permutationsList.AddLast(permutation);
    }

    private void GeneratePermutations(List<int> permutation, int value, int permutationLength)
    {
       if (permutation.Count < permutationLength)
        {
            List<int> copyOfInitialPermutation = new List<int>(permutation);
            copyOfInitialPermutation.Add(value);
            _permutationsList.AddLast(copyOfInitialPermutation);
            List<int> copyOfPermutation = new List<int>();
            copyOfPermutation.AddRange(copyOfInitialPermutation);
            int lastIndex = copyOfInitialPermutation.Count - 1;
            for (int i = lastIndex;i > 0;i--)
            {
                int temp = copyOfPermutation[i - 1];
                copyOfPermutation[i - 1] = copyOfPermutation[i];
                copyOfPermutation[i] = temp;

                List<int> perm = new List<int>();
                perm.AddRange(copyOfPermutation);
                _permutationsList.AddLast(perm);
            }
        }
    }

    public void PrintPermutations(int permutationLength)
    {
        int count = _permutationsList.Where(perm => perm.Count() == permutationLength).Count();
        Console.WriteLine("The number of permutations is " + count);
    }
}

це корисна відповідь
Аяз Аліфов

2

У Скалі

    def permutazione(n: List[Int]): List[List[Int]] = permutationeAcc(n, Nil)



def permutationeAcc(n: List[Int], acc: List[Int]): List[List[Int]] = {

    var result: List[List[Int]] = Nil
    for (i ← n if (!(acc contains (i))))
        if (acc.size == n.size-1)
            result = (i :: acc) :: result
        else
            result = result ::: permutationeAcc(n, i :: acc)
    result
}

2

це версія java для перестановки

public class Permutation {

    static void permute(String str) {
        permute(str.toCharArray(), 0, str.length());
    }

    static void permute(char [] str, int low, int high) {
        if (low == high) {
            System.out.println(str);
            return;
        }

        for (int i=low; i<high; i++) {
            swap(str, i, low);
            permute(str, low+1, high);
            swap(str, low, i);
        }

    }

    static void swap(char [] array, int i, int j) {
        char t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}

2

Ось реалізація для ColdFusion (потрібна CF10 через аргумент злиття в ArrayAppend ()):

public array function permutateArray(arr){

    if (not isArray(arguments.arr) ) {
        return ['The ARR argument passed to the permutateArray function is not of type array.'];    
    }

    var len = arrayLen(arguments.arr);
    var perms = [];
    var rest = [];
    var restPerms = [];
    var rpLen = 0;
    var next = [];

    //for one or less item there is only one permutation 
    if (len <= 1) {
        return arguments.arr;
    }

    for (var i=1; i <= len; i++) {
        // copy the original array so as not to change it and then remove the picked (current) element
        rest = arraySlice(arguments.arr, 1);
        arrayDeleteAt(rest, i);

         // recursively get the permutation of the rest of the elements
         restPerms = permutateArray(rest);
         rpLen = arrayLen(restPerms);

        // Now concat each permutation to the current (picked) array, and append the concatenated array to the end result
        for (var j=1; j <= rpLen; j++) {
            // for each array returned, we need to make a fresh copy of the picked(current) element array so as to not change the original array
            next = arraySlice(arguments.arr, i, 1);
            arrayAppend(next, restPerms[j], true);
            arrayAppend(perms, next);
        }
     }

    return perms;
}

На основі рішення js KhanSharp вище.


Деякі пояснення загальної стратегії вашого алгоритму були б хорошим способом покращити цю відповідь.
Річард

То чому ж потік? Це робоча реалізація.
earachefl

@Richard, загальна стратегія була пояснена вище Whirlwind та іншими. Я не бачу вашого коментаря до всіх інших відповідей, розміщених як реалізація без пояснень.
earachefl

1

Я знаю, що це дуже давня і навіть поза темою в сьогоднішньому стартовому потоці, але я все ж хотів зробити свій дружній відповідь на javascript з тієї простої причини, що вона працює у вашому браузері.

Я також додав debuggerточку розриву директиви, щоб ви могли переглядати код (потрібний хром), щоб побачити, як працює цей алгоритм. Відкрийте консоль розробника в хромі ( F12у вікнах чи CMD + OPTION + Iна Mac) та натисніть «Виконати фрагмент коду». Це реалізує той самий точний алгоритм, який @WhirlWind представив у своїй відповіді.

Ваш веб-переглядач повинен призупинити виконання в debuggerдирективі. Використовуйте F8для продовження виконання коду.

Перегляньте код і подивіться, як він працює!

function permute(rest, prefix = []) {
  if (rest.length === 0) {
    return [prefix];
  }
  return (rest
    .map((x, index) => {
      const oldRest = rest;
      const oldPrefix = prefix;
      // the `...` destructures the array into single values flattening it
      const newRest = [...rest.slice(0, index), ...rest.slice(index + 1)];
      const newPrefix = [...prefix, x];
      debugger;

      const result = permute(newRest, newPrefix);
      return result;
    })
    // this step flattens the array of arrays returned by calling permute
    .reduce((flattened, arr) => [...flattened, ...arr], [])
  );
}
console.log(permute([1, 2, 3]));


1

У наступному Java-рішенні ми користуємося перевагою того, що рядки незмінні, щоб уникнути клонування набору результатів при кожній ітерації.

Вхід буде String, скажімо, "abc", а вихід буде усіма можливими перестановками:

abc
acb
bac
bca
cba
cab

Код:

public static void permute(String s) {
    permute(s, 0);
}

private static void permute(String str, int left){
    if(left == str.length()-1) {
        System.out.println(str);
    } else {
        for(int i = left; i < str.length(); i++) {
            String s = swap(str, left, i);
            permute(s, left+1);
        }
    }
}

private static String swap(String s, int left, int right) {
    if (left == right)
        return s;

    String result = s.substring(0, left);
    result += s.substring(right, right+1);
    result += s.substring(left+1, right);
    result += s.substring(left, left+1);
    result += s.substring(right+1);
    return result;
}

Цей же підхід може бути застосований до масивів (замість рядка):

public static void main(String[] args) {
    int[] abc = {1,2,3};
    permute(abc, 0);
}
public static void permute(int[] arr, int index) {
    if (index == arr.length) {
        System.out.println(Arrays.toString(arr));
    } else {
        for (int i = index; i < arr.length; i++) {
            int[] permutation = arr.clone();
            permutation[index] = arr[i];
            permutation[i] = arr[index];
            permute(permutation, index + 1);
        }
    }
}

1

Це моє рішення на Java:

public class CombinatorialUtils {

    public static void main(String[] args) {
        List<String> alphabet = new ArrayList<>();
        alphabet.add("1");
        alphabet.add("2");
        alphabet.add("3");
        alphabet.add("4");

        for (List<String> strings : permutations(alphabet)) {
            System.out.println(strings);
        }
        System.out.println("-----------");
        for (List<String> strings : combinations(alphabet)) {
            System.out.println(strings);
        }
    }

    public static List<List<String>> combinations(List<String> alphabet) {
        List<List<String>> permutations = permutations(alphabet);
        List<List<String>> combinations = new ArrayList<>(permutations);

        for (int i = alphabet.size(); i > 0; i--) {
            final int n = i;
            combinations.addAll(permutations.stream().map(strings -> strings.subList(0, n)).distinct().collect(Collectors.toList()));
        }
        return combinations;
    }

    public static <T> List<List<T>> permutations(List<T> alphabet) {
        ArrayList<List<T>> permutations = new ArrayList<>();
        if (alphabet.size() == 1) {
            permutations.add(alphabet);
            return permutations;
        } else {
            List<List<T>> subPerm = permutations(alphabet.subList(1, alphabet.size()));
            T addedElem = alphabet.get(0);
            for (int i = 0; i < alphabet.size(); i++) {
                for (List<T> permutation : subPerm) {
                    int index = i;
                    permutations.add(new ArrayList<T>(permutation) {{
                        add(index, addedElem);
                    }});
                }
            }
        }
        return permutations;
    }
}

1

Ви не можете реально говорити про вирішення проблеми пермультації в рекурсії, не розміщуючи реалізацію на (діалектній) мові, яка була новатором ідеї . Отже, для повноти, ось один із способів, який можна зробити в Схемі.

(define (permof wd)
  (cond ((null? wd) '())
        ((null? (cdr wd)) (list wd))
        (else
         (let splice ([l '()] [m (car wd)] [r (cdr wd)])
           (append
            (map (lambda (x) (cons m x)) (permof (append l r)))
            (if (null? r)
                '()
                (splice (cons m l) (car r) (cdr r))))))))

зателефонувавши, (permof (list "foo" "bar" "baz"))ми отримаємо:

'(("foo" "bar" "baz")
  ("foo" "baz" "bar")
  ("bar" "foo" "baz")
  ("bar" "baz" "foo")
  ("baz" "bar" "foo")
  ("baz" "foo" "bar"))

Я не буду вникати в деталі алгоритму, оскільки це було достатньо пояснено в інших публікаціях. Ідея та ж.

Однак, рекурсивні проблеми, як правило, набагато складніше моделювати та думати в деструктивних середовищах, таких як Python, C та Java, тоді як у Lisp або ML це можна коротко виразити.


0

Ось рекурсивне рішення в PHP. Пост WhirlWind точно описує логіку. Варто зазначити, що генерація всіх перестановок працює у факторний час, тому може бути корисною замість цього використовувати ітеративний підхід.

public function permute($sofar, $input){
  for($i=0; $i < strlen($input); $i++){
    $diff = strDiff($input,$input[$i]);
    $next = $sofar.$input[$i]; //next contains a permutation, save it
    $this->permute($next, $diff);
  }
}

Функція strDiff займає два рядки, s1і s2, повертає нову рядок із усім, що s1не містить елементів у s2(дублює значення). Отже, strDiff('finish','i')=> 'fnish'(друге "я" не видаляється).


0

Ось алгоритм на R, на випадок, коли комусь потрібно уникати завантаження додаткових бібліотек, як мені довелося.

permutations <- function(n){
    if(n==1){
        return(matrix(1))
    } else {
        sp <- permutations(n-1)
        p <- nrow(sp)
        A <- matrix(nrow=n*p,ncol=n)
        for(i in 1:n){
            A[(i-1)*p+1:p,] <- cbind(i,sp+(sp>=i))
        }
        return(A)
    }
}

Приклад використання:

> matrix(letters[permutations(3)],ncol=3)
     [,1] [,2] [,3]
[1,] "a"  "b"  "c" 
[2,] "a"  "c"  "b" 
[3,] "b"  "a"  "c" 
[4,] "b"  "c"  "a" 
[5,] "c"  "a"  "b" 
[6,] "c"  "b"  "a" 

0
#!/usr/bin/env python
import time

def permutations(sequence):
  # print sequence
  unit = [1, 2, 1, 2, 1]

  if len(sequence) >= 4:
    for i in range(4, (len(sequence) + 1)):
      unit = ((unit + [i - 1]) * i)[:-1]
      # print unit
    for j in unit:
      temp = sequence[j]
      sequence[j] = sequence[0]
      sequence[0] = temp
      yield sequence
  else:
    print 'You can use PEN and PAPER'


# s = [1,2,3,4,5,6,7,8,9,10]
s = [x for x in 'PYTHON']

print s

z = permutations(s)
try:
  while True:
    # time.sleep(0.0001)
    print next(z)
except StopIteration:
    print 'Done'

['P', 'Y', 'T', 'H', 'O', 'N']
['Y', 'P', 'T', 'H', 'O', 'N']
['T', 'P', 'Y', 'H', 'O', 'N']
['P', 'T', 'Y', 'H', 'O', 'N']
['Y', 'T', 'P', 'H', 'O', 'N']
['T', 'Y', 'P', 'H', 'O', 'N']
['H', 'Y', 'P', 'T', 'O', 'N']
['Y', 'H', 'P', 'T', 'O', 'N']
['P', 'H', 'Y', 'T', 'O', 'N']
['H', 'P', 'Y', 'T', 'O', 'N']
['Y', 'P', 'H', 'T', 'O', 'N']
['P', 'Y', 'H', 'T', 'O', 'N']
['T', 'Y', 'H', 'P', 'O', 'N']
['Y', 'T', 'H', 'P', 'O', 'N']
['H', 'T', 'Y', 'P', 'O', 'N']
['T', 'H', 'Y', 'P', 'O', 'N']
['Y', 'H', 'T', 'P', 'O', 'N']
['H', 'Y', 'T', 'P', 'O', 'N']
['P', 'Y', 'T', 'H', 'O', 'N']
.
.
.
['Y', 'T', 'N', 'H', 'O', 'P']
['N', 'T', 'Y', 'H', 'O', 'P']
['T', 'N', 'Y', 'H', 'O', 'P']
['Y', 'N', 'T', 'H', 'O', 'P']
['N', 'Y', 'T', 'H', 'O', 'P']

Рішення показує, що ви не перестановили рядок відповідно до вимоги. Друга перестановка мала бути PYTHNO
Рахул Кадукар

0

Це рекурсивний код для Java, ідея - мати префікс, який додасть решту символів:

public static void permutation(String str) { 
    permutation("", str); 
}

private static void permutation(String prefix, String str) {
    int n = str.length();
    if (n == 0) System.out.println(prefix);
    else {
        for (int i = 0; i < n; i++)
            permutation(prefix + str.charAt(i), str);
    }
}

Приклад:

Вхід = "ABC"; Вихід:

ABC ACB BAC BCA CAB CBA


1
Хороша ідея, але я думаю, ви також повинні видалити charAt (i) з strрекурсивного дзвінка, інакше він не припиниться.
Кришталь

1
Якщо ви збираєтесь скопіювати та вставити, вам потрібно (1) надати атрибуцію та (2) переконатися в правильності будь-яких змін. Для атрибуції це perm1 від introcs.cs.princeton.edu/java/23recursion/… . Крім того, ваше редагування невірно: str.substring (0, i) + str.substring (i + 1, n) не те саме, що str, тому що попереднє опускає символ у позиції i.
кевін

0

Просто для завершення, C ++

#include <iostream>
#include <algorithm>
#include <string>

std::string theSeq = "abc";
do
{
  std::cout << theSeq << endl;
} 
while (std::next_permutation(theSeq.begin(), theSeq.end()));

...

abc
acb
bac
bca
cab
cba

0

Ось нерекурсивне рішення в C ++, яке забезпечує наступну перестановку у порядку зростання, аналогічно функціоналу, що надається std :: next_permutation:

void permute_next(vector<int>& v)
{
  if (v.size() < 2)
    return;

  if (v.size() == 2)
  { 
    int tmp = v[0];
    v[0] = v[1];
    v[1] = tmp;
    return;
  }

  // Step 1: find first ascending-ordered pair from right to left
  int i = v.size()-2;
  while(i>=0)
  { 
    if (v[i] < v[i+1])
      break;
    i--;
  }
  if (i<0) // vector fully sorted in descending order (last permutation)
  {
    //resort in ascending order and return
    sort(v.begin(), v.end());
    return;
  }

  // Step 2: swap v[i] with next higher element of remaining elements
  int pos = i+1;
  int val = v[pos];
  for(int k=i+2; k<v.size(); k++)
    if(v[k] < val && v[k] > v[i])
    {
      pos = k;
      val = v[k];
    }
  v[pos] = v[i];
  v[i] = val;

  // Step 3: sort remaining elements from i+1 ... end
  sort(v.begin()+i+1, v.end());
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.