web-dev-qa-db-fra.com

Imprimer toutes les permutations dans l'ordre lexicographique

Je veux imprimer toutes les permutations de chaîne dans l'ordre lexicographique. J'écris ce code:

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

Et j'ai par exemple la chaîne abc, donc je veux recevoir toutes les permutations dans l'ordre lexicographique comme dans la colonne de gauche, mais le résultat est comme dans la colonne de droite.

"abc"                   "abc"
"acb"                   "acb"
"bac"                   "bac"
"bca"                   "bca"
"cab"            <
"cba"                   "cba"
                 >      "cab"

Quelqu'un peut il m'aider avec ça? J'ai vu des algorithmes, mais ils ont l'air difficile. Je pense que je peux sauvegarder toutes les chaînes générées dans un tableau, puis trier ce tableau, mais je ne peux pas écrire ceci (je suis débutant en C).

15
Alexey Sharov

En C

Il existe une description assez simple d’un algorithme (plus l’implémentation) à geeksforgeeks :

Étant donné une chaîne, imprimez toutes les permutations de celle-ci dans un ordre trié. Pour Par exemple, si la chaîne d'entrée est «ABC», la sortie doit être «ABC, ACB, BAC, BCA, CAB, CBA».

Nous avons discuté d'un programme pour imprimer toutes les permutations dans cet article, , Mais ici, nous devons les imprimer par ordre croissant.

Voici les étapes à suivre pour imprimer les permutations lexicographiques

  1. Triez la chaîne donnée dans un ordre non décroissant et imprimez-la. La première permutation est toujours la chaîne triée par ordre non décroissant.

  2. Commencez à générer la prochaine permutation plus élevée. Faites-le jusqu'à la prochaine permutation supérieure n'est pas possible. Si nous atteignons une permutation où tous les caractères Sont triés dans un ordre non croissant, cette permutation Est la dernière permutation.

Étapes pour générer la permutation suivante:
1. Prenez la permutation précédemment imprimée et trouvez-y le caractère le plus à droite, qui est plus petit que son caractère suivant. Appelons Ce caractère en tant que «premier caractère».

  1. Trouvez maintenant le plafond du ‘premier caractère’. Plafond est le plus petit caractère à droite du "premier caractère", qui est supérieur à au "premier caractère". Appelons le caractère ceil «deuxième caractère ».

  2. Échangez les deux caractères trouvés dans les 2 étapes ci-dessus.

  3. Triez la sous-chaîne (dans l’ordre non décroissant) après l’index initial du «premier caractère».

Je l'ai ré-implémenté ci-dessous:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void swap(char* left, char* right)
{
    char temp = *left;
    *left = *right;
    *right = temp;
}
int compare (const void * a, const void * b)
{
  return ( *(char*)a - *(char*)b );
}
void PrintSortedPermutations(char* inStr)
{
    // Re-implementation of algorithm described here:
    // http://www.geeksforgeeks.org/lexicographic-permutations-of-string/
    int strSize = strlen(inStr);
    // 0. Ensure input container is sorted
    qsort(inStr, strSize, sizeof(char), compare);


    int largerPermFound = 1;
    do{
        // 1. Print next permutation
        printf("%s\n", inStr);
        // 2. Find rightmost char that is smaller than char to its right
        int i;
        for (i = strSize - 2; i >= 0 && inStr[i] >= inStr[i+1]; --i){}

        // if we couldn't find one, we're finished, else we can swap somewhere
        if (i > -1)
        {
            // 3 find character at index j such that 
            // inStr[j] = min(inStr[k]) && inStr[k] > inStr[i] for all k > i
            int j = i+1;
            int k;
            for(k=j;k<strSize && inStr[k];++k)
            {
                if (inStr[k] > inStr[i] && inStr[k] < inStr[j])
                    j = k;
            }

            // 3. Swap chars at i and j
            swap(&inStr[i], &inStr[j]);

            // 4. Sort string to the right of i
            qsort(inStr+i+1, strSize-i-1, sizeof(char), compare);
        }
        else
        {
            largerPermFound = 0;
        }
    }while(largerPermFound);
}

int main(void) {
    char str[] = "abc";

    PrintSortedPermutations(str);
    return 0;
}

Sortie

abc 
acb 
bac 
bca 
taxi 
cba

Démo en direct


En C++

std::next_permutation de la bibliothèque <algorithm> le fera pour vous, assurez-vous simplement de trier votre conteneur en premier:

Valeur de retour

true si la fonction peut réorganiser l'objet comme une plus grande permutation lexicographique . Sinon, la fonction retourne false pour indiquer Que la disposition n'est pas plus grande que la précédente, mais la plus basse Possible (triée par ordre croissant).

Par exemple:

std::string myStr = "abc";
std::stable_sort(std::begin(myStr), std::end(myStr));
do {
    for(auto&& element : myStr)
        std::cout << element << " ";
    std::cout << std::endl;
} while (std::next_permutation(std::begin(myStr), std::end(myStr)));

Sortie:

a b c 
a c b 
b a c 
b c a 
taxi 
c b a

Démo en direct

17
AndyG

Je suppose que vous voulez une version récursive, puisque vous êtes débutant.

Voici deux solutions.

Solution 1) 

Puisque vous voulez lexicographie, tout ce que vous avez à faire est de choisir la plus petite possible lorsque vous avez besoin de choisir. C'est tout!

Par exemple, voici une version récursive en python

def permute(done, remaining):
  if not remaining:
    print done
    return

  sorted_rem = sorted(remaining)
  l = len(sorted_rem)

  for i in xrange(0, l):
    c = sorted_rem[i]

    # Move to c to done portion.
    done.append(c)
    remaining.remove(c)

    # Permute the remaining
    permute(done, remaining)

    # Put c back.
    remaining.append(c)
    # Remove from done.
    del done[-1]

permute([], [1,2,3,4])

C'est tout.

Solution 2)

Bien que la solution 1 fonctionne et soit facile à comprendre, je suppose que nous pourrions perdre du temps en triant. Cette solution est plus proche de ce que vous avez.

La récursion est fondamentalement une induction mathématique déguisée, et cette façon de penser est vraiment utile pour comprendre comment écrire des programmes récursifs.

Par exemple, supposons que votre méthode de permutation construise toujours les permutations dans l’ordre lexicographique.

Voici une version récursive, avec cette hypothèse, veuillez lire les commentaires pour comprendre ce qui se passe.

// By induction assumption, permute(a, i, n)
// goes through all the permutations of a[i], ..., a[n-1]
// in lexicographic order, by modifying a itself.
void permute(char *a, int i, int n) {
    if (i == (n-1)) {
       printf("%s\n", a);
      return;
    }

    int j;
    // We pick the n-i posibilities for the position a+i, then recursively
    // compute the permutations of a[i+1], ..., a[n-1]
    // So first pick the smallest possible for a+i, recurse.
    // Then the next possible for a+i, then recurse etc.

    for (j = i; j < n; j++) {
      permute(a, i+1, n);
      // By our induction assumption, at this point, 
      // a[i+1], a[i+2], .., a[n-1]
      // must be the lexicographically the largest possible!

      // So now reverse that portion.
      reverse(a+i+1, a+n-1);

      // Now we need to pick the lexicographically next element for
      // position a+i. This is nothing but the element which is just
      // larger than the current a+i.

      int k = i+1;
      while(k < n && a[i] > a[k]) {
        k++;
      }

      if (k >= n) {
        continue;
      }
      // Choose the next value for a+i.
      swap(a+i, a+k);
    }
    // Notice that the portion a[i+1], ..., a[n-1]  is sorted increasing.
    // when the loop exits. Also a[i] will be the largest element.
    // We need to reverse so that a[i], .., a[n-1] is the lexicographically
    // largest permutation to  maintain the induction (recursion) assumption.
    reverse(a+i+1, a+n-1);
}

Notez la similitude entre cela et la version itérative (spécifiée par les autres et la section ci-dessous), dans laquelle vous inversez un morceau à la fin et échangez deux éléments.


en fait, l'algorithme itératif commun pour générer des permutations dans l'ordre lexicographique est l'algorithme de Narayana Pandita, mentionné par d'autres personnes, mais pas par son nom.

Voir ce lien: http://fr.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order

C'est ce que std :: next de C++ et un hôte d'autres bibliothèques utilisent.

Cet algorithme fonctionne même lorsqu'il y a des éléments répétés et peut en fait être utilisé pour générer des combinaisons! (Initialisez votre tableau avec des zéros et des uns).

4
Programmer Person

L'idée de base est de commencer avec notre chaîne principale sous la forme "" et la chaîne restante sous la forme "abc" (ou selon votre choix).

Maintenant, à la chaîne principale, ajoutez caractère par caractère. De manière à ne pas répéter les caractères utilisés (pour tous les caractères possibles).

Répétez l'opération jusqu'à obtenir la longueur n (longueur de la chaîne principale).

D'accord, l'explication n'est pas claire mais attention au code. Cela clarifiera tout.

#include<iostream>

void perm(std::string sub, std::string rem, int n)
{
    if(n==0)
        //print if n is zero i.e length achieved
        std::cout<<sub<<"\n";

    for(int i=0; i<n; i++)
        //append a character and pass remaining to rem string
        perm(sub + rem[i] , rem.substr(0,i) + rem.substr(i+1,n-1), n-1);
}

int main()
{
    perm("", "abc", 3);
}

Sortie

abc
acb
bac
bca
cab
cba
1
Shivam K Thakkar

Ceci est volontairement une réponse qui pas répond à cette question. 

Cette autre question a été marquée comme une copie de celle-ci. Cette réponse serait acceptable pour l’autre question même si elle n’a aucun sens ici.

Cela pourrait être une simple implémentation en C récursif pour obtenir toutes les permutations dans l'ordre lexicographique. Il n'est pas optimisé mais facile à comprendre et à mettre en œuvre:

  • obtenir un tableau pour les éléments à permuter
  • à tout moment, parcourez les éléments qui n’ont pas été utilisés jusqu’ici
    • ajouter l'un des éléments itérés au tableau des éléments utilisés
    • récidive
  • lorsque le tableau de l'élément utilisé est plein, imprimez-le.

Mise en œuvre concrète:

#include <stdio.h>

#define SIZE 4

void disp(int *fullarr, int n, int * begin, int pos) {
    int i, j;
    int found;
    if (pos == n) {
        for(i=0; i< n; i++) {
            printf("%2d", begin[i]);
        }
        printf("\n");
        return;
    }
    for (i=0; i<n; i++) {
        found = 0;
        for (j=0; j<pos; j++) {
            if (fullarr[i] == begin[j]) {
                found = 1;
                break;
            }
        }
        if (! found) {
            begin[pos] = fullarr[i];
            disp(fullarr, n, begin, pos+1);
        }
    }
}

int main() {
    int i;
    int fullarr[SIZE], begin[SIZE];
    for (i=0; i<SIZE; i++) {
        fullarr[i] = i;
    }
    disp(fullarr, SIZE, begin, 0);
    return 0;
}
0
Serge Ballesta

IMHO, il serait plus simple de commencer par trier les caractères de la chaîne car le nombre de permutations (n!) est toujours supérieur (ou égal pour n = 1 ou 2) au nombre de caractères.

Et vous n'étiez pas loin de la solution, mais vous devez effectuer une rotation au lieu d'un échange. Voici une légère variation qui renvoie un tableau de chaînes allouées dynamiquement et imprimées de manière principale:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int char_compare(const void *a, const void *b) {
    return (*((char *) a)) - (*((char *) b));
}

int fact(int n) {
    int f = 1;
    while (n > 0) {
        f *= n--;
        if (f < 0) return 0;
    }
    return f;
}

void rotateback(char *a, int i, int j) {
    int k;
    char tmp;
    tmp = a[i];
    for(k=i; k<j; k++) {
        a[k] = a[k+1];
    }
    a[j] = tmp;
}

void rotate(char *a, int i, int j) {
    int k;
    char tmp;
    tmp = a[j];
    for(k=j; k>i; k--) {
        a[k] = a[k-1];
    }
    a[i] = tmp;
}

void permute(char *a, int i, int n, char ***permuts) {
    int j;
    if (i == (n-1)) {
        **permuts = strdup(a);  // store a copy of the string
        *permuts += 1;          // and point to next location
    }
   else {
       for (j = i; j < n; j++) {
           rotate(a, i, j);
           permute(a, i+1, n, permuts);
           rotateback(a, i, j);
       }
   }
}
char ** permutations(const char *str_orig) {
    int i, j;
    size_t n = strlen(str_orig);
    size_t fact_n = fact(n);
    char ** permuts, **work;

    char * str = strdup(str_orig); // get a local copy
    qsort(str, n, 1, char_compare); // and sort it

    permuts = work = calloc(fact_n, sizeof(char *)); // allocate n! pointers

    permute(str, 0, n, &work);
    return permuts;
}

int main() {
    char str[] = "cab";
    int i;

    char **permuts = permutations(str);

    for (i=0; i<fact(strlen(str)); i++) {
        printf("\"%s\"\n", permuts[i]);
        free(permuts[i]);
    }
    free(permuts);
    return 0;
}

La sortie est correctement:

"abc"
"acb"
"bac"
"bca"
"cab"
"cba"
0
Serge Ballesta

Une autre tournure sur les permutations de chaînes lexicales consiste à stocker la permutation dans un tableau alloué dynamiquement de pointeurs sur chaînes et à passer le tableau à qsort pour fournir une sortie dans l'ordre lexical. Comme les permutations croissent de manière exponentielle, il est particulièrement important de vérifier l'épuisement de la mémoire après chaque allocation. La taille de la chaîne ci-dessous est limitée à 16 caractères, ce qui peut entraîner une saturation de la mémoire en fonction de la quantité de mémoire disponible.

Mise à jour transmettre l'adresse du tableau contenant les permutations de chaîne était nécessaire pour que la réallocation fonctionne dans la fonction récursive.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXS 128
#define MAXC 16

size_t maxs;

void swap (char *x, char *y);
int cmp_pa (const void * a, const void * b);
char **realloc_char (char **sp, size_t *n);
void permute_pa (char ***pa, size_t *idx, char *a, int i, int n);

int main (int argc, char **argv)
{
    size_t i = 0;
    size_t idx = 0;
    size_t len = 0;
    char a[MAXC] = {0};
    char **pa = NULL;

    maxs = MAXS;                            /* initialize realloc counter   */

    if (argc > 1)                           /* set string to permute        */
        strcpy (a, argv[1]);
    else
        strcpy (a, "abc");

    len = strlen (a);                       /* lenght to permute or MAXC    */
    if (len > MAXC) len = MAXC;

    if (!(pa = calloc (MAXS, sizeof *pa)))  /* allocate MAXS pointers       */
        return 1;

    permute_pa (&pa, &idx, a, 0, len - 1);  /* call permute function        */

    printf ("\n no of permutations : %zu\n\n", idx);
    printf (" unsorted permutations of %s\n\n", a);

    for (i = 0; i < idx; i++)
        printf (" %s\n", pa[i]);

    qsort (pa, idx, sizeof *pa, cmp_pa);    /* sort array of permutations   */

    printf ("\n sorted permutations of %s\n\n", a);

    for (i = 0; i < idx; i++)
        printf (" %s\n", pa[i]);

    for (i = 0; i < idx; i++)               /* free all allocated memory    */
        free (pa[i]);
    free (pa);

    return 0;
}

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

/* qsort compare function */
int cmp_pa (const void * a, const void * b)
{   return strcmp (*(char**)a, *(char**)b); }

/* realloc an array of pointers to strings setting memory to 0. */
char **realloc_char (char **sp, size_t *n)
{
    char **tmp = realloc (sp, 2 * *n * sizeof *sp);
    if (!tmp) {
        fprintf (stderr, "Error: struct reallocation failure.\n");
        // return NULL;
        exit (EXIT_FAILURE);
    }
    sp = tmp;
    memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
    *n *= 2;

    return sp;
}

/* Function to store permutations of string in array of pointers-to-string
This function takes five parameters:
1. allocated array of pointers-to-string
2. pointer to array index
3. string to permute
4. starting index of the string (zero based)
5. ending index of the string. (zero based)
*/
void permute_pa (char ***pa, size_t *idx, char *a, int i, int n)
{
    int j;
    if (i == n) {
        (*pa)[*idx] = strdup (a);
        if (!(*pa)[*idx]) {
            fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
            exit (EXIT_FAILURE);
        }
        (*idx)++;
        if (*idx == maxs)
            *pa = realloc_char (*pa, &maxs);
    }
    else {
        for (j = i; j <= n; j++) {
            swap ((a+i), (a+j));
            permute_pa (pa, idx, a, i+1, n);
            swap ((a+i), (a+j));
        }
    }
}

Sortie

$ ./bin/str_permute_Lex

 no of permutations : 6

 unsorted permutations of abc

 abc
 acb
 bac
 bca
 cba
 cab

 sorted permutations of abc

 abc
 acb
 bac
 bca
 cab
 cba

Vérification des erreurs de mémoire

$ valgrind ./bin/str_permute_Lex
==29709== Memcheck, a memory error detector
==29709== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==29709== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==29709== Command: ./bin/str_permute_Lex
==29709==

 no of permutations : 6

 <snip>

==29709==
==29709== HEAP SUMMARY:
==29709==     in use at exit: 0 bytes in 0 blocks
==29709==   total heap usage: 7 allocs, 7 frees, 1,048 bytes allocated
==29709==
==29709== All heap blocks were freed -- no leaks are possible
==29709==
==29709== For counts of detected and suppressed errors, rerun with: -v
==29709== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
0
David C. Rankin