Are there any better methods to do permutation of string?

前端 未结 20 1350
醉话见心
醉话见心 2020-11-27 11:15
void permute(string elems, int mid, int end)
{
    static int count;
    if (mid == end) {
        cout << ++count << \" : \" << elems << end         


        
相关标签:
20条回答
  • 2020-11-27 11:49

    This is not the best logic, but then, i am a beginner. I'll be quite happy and obliged if anyone gives me suggestions on this code

    #include<iostream.h>
    #include<conio.h>
    #include<string.h>
    int c=1,j=1;
    
    
    int fact(int p,int l)
    {
    int f=1;
    for(j=1;j<=l;j++)
    {
    f=f*j;
    if(f==p)
    return 1;
    
    }
    return 0;
    }
    
    
    void rev(char *a,int q)
    {
    int l=strlen(a);
    int m=l-q;
    char t;
    for(int x=m,y=0;x<q/2+m;x++,y++)
    {
    t=a[x];
    a[x]=a[l-y-1];
    a[l-y-1]=t;
    }
    c++;
    cout<<a<<"  ";
    }
    
    int perm(char *a,int f,int cd)
    {
    if(c!=f)
    {
    int l=strlen(a);
    rev(a,2);
    cd++;
    if(c==f)return 0;
    if(cd*2==6)
    {
    for(int i=1;i<=c;i++)
    {
    if(fact(c/i,l)==1)
    {
    rev(a,j+1);
    rev(a,2);
    break;
    }
    }
    cd=1;
    }
    rev(a,3);
    perm(a,f,cd);
    }
    return 0;
    }
    
    void main()
    {
    clrscr();
    char *a;
    cout<<"\n\tEnter a Word";
    cin>>a;
    int f=1;
    
    for(int o=1;o<=strlen(a);o++)
    f=f*o;
    
    perm(a,f,0);
    getch();
    }
    
    0 讨论(0)
  • 2020-11-27 11:52

    In particular, you want std::next_permutation.

    void permute(string elems, int mid, int end)
    {
      int count = 0;
      while(next_permutation(elems.begin()+mid, elems.end()))
        cout << << ++count << " : " << elems << endl;
    }
    

    ... or something like that...

    0 讨论(0)
  • 2020-11-27 11:52

    I don't think this is better, but it does work and does not use recursion:

    #include <iostream>
    #include <stdexcept>
    #include <tr1/cstdint>
    
    ::std::uint64_t fact(unsigned int v)
    {
       ::std::uint64_t output = 1;
       for (unsigned int i = 2; i <= v; ++i) {
          output *= i;
       }
       return output;
    }
    
    void permute(const ::std::string &s)
    {
       using ::std::cout;
       using ::std::uint64_t;
       typedef ::std::string::size_type size_t;
    
       static unsigned int max_size = 20;  // 21! > 2^64
    
       const size_t strsize = s.size();
    
       if (strsize > max_size) {
          throw ::std::overflow_error("This function can only permute strings of size 20 or less.");
       } else if (strsize < 1) {
          return;
       } else if (strsize == 1) {
          cout << "0 : " << s << '\n';
       } else {
          const uint64_t num_perms = fact(s.size());
          // Go through each permutation one-by-one
          for (uint64_t perm = 0; perm < num_perms; ++perm) {
             // The indexes of the original characters in the new permutation
             size_t idxs[max_size];
    
             // The indexes of the original characters in the new permutation in
             // terms of the list remaining after the first n characters are pulled
             // out.
             size_t residuals[max_size];
    
             // We use div to pull our permutation number apart into a set of
             // indexes.  This holds what's left of the permutation number.
             uint64_t permleft = perm;
    
             // For a given permutation figure out which character from the original
             // goes in each slot in the new permutation.  We start assuming that
             // any character could go in any slot, then narrow it down to the
             // remaining characters with each step.
             for (unsigned int i = strsize; i > 0; permleft /= i, --i) {
                uint64_t taken_char = permleft % i;
                residuals[strsize - i] = taken_char;
    
                // Translate indexes in terms of the list of remaining characters
                // into indexes in terms of the original string.
                for (unsigned int o = (strsize - i); o > 0; --o) {
                   if (taken_char >= residuals[o - 1]) {
                      ++taken_char;
                   }
                }
                idxs[strsize - i] = taken_char;
             }
             cout << perm << " : ";
             for (unsigned int i = 0; i < strsize; ++i) {
                cout << s[idxs[i]];
             }
             cout << '\n';
          }
       }
    }
    

    The fun thing about this is that the only state it uses from permutation to permutation is the number of the permutation, the total number of permutations, and the original string. That means it can be easily encapsulated in an iterator or something like that without having to carefully preserve the exact correct state. It can even be a random access iterator.

    Of course ::std::next_permutation stores the state in the relationships between elements, but that means it can't work on unordered things, and I would really wonder what it does if you have two equal things in the sequence. You can solve that by permuting indexes of course, but that adds slightly more complication.

    Mine will work with any random access iterator range provided it's short enough. And if it isn't, you'll never get through all the permutations anyway.

    The basic idea of this algorithm is that every permutation of N items can be enumerated. The total number is N! or fact(N). And any given permutation can be thought of as a mapping of source indices from the original sequence into a set of destination indices in the new sequence. Once you have an enumeration of all permutations the only thing left to do is map each permutation number into an actual permutation.

    The first element in the permuted list can be any of the N elements from the original list. The second element can be any of the N - 1 remaining elements, and so on. The algorithm uses the % operator to pull apart the permutation number into a set of selections of this nature. First it modulo's the permutation number by N to get a number from [0,N). It discards the remainder by dividing by N, then it modulo's it by the size of the list - 1 to get a number from [0,N-1) and so on. That is what the for (i = loop is doing.

    The second step is translating each number into an index into the original list. The first number is easy because it's just a straight index. The second number is an index into a list that contains every element but the one removed at the first index, and so on. That is what the for (o = loop is doing.

    residuals is a list of indices into the successively smaller lists. idxs is a list of indices into the original list. There is a one-one mapping between values in residuals and idxs. They each represent the same value in different 'coordinate spaces'.

    The answer pointed to by the answer you picked has the same basic idea, but has a much more elegant way of accomplishing the mapping than my rather literal and brute force method. That way will be slightly faster than my method, but they are both about the same speed and they both have the same advantage of random access into permutation space which makes a whole number of things easier, including (as the answer you picked pointed out) parallel algorithms.

    0 讨论(0)
  • 2020-11-27 11:52

    Here's one I just rustled up!!

    void permute(const char* str, int level=0, bool print=true) {
    
        if (print) std::cout << str << std::endl;
    
        char temp[30];
        for (int i = level; i<strlen(str); i++) {
    
            strcpy(temp, str);
    
            temp[level] = str[i];
            temp[i] = str[level];
    
            permute(temp, level+1, level!=i);
        }
    }
    
    int main() {
        permute("1234");
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 11:56

    The Knuth random shuffle algorithm is worth looking into.

    // In-place shuffle of char array
    void shuffle(char array[], int n)
    {
        for ( ; n > 1; n--)
        {
            // Pick a random element to move to the end
            int k = rand() % n;  // 0 <= k <= n-1  
    
            // Simple swap of variables
            char tmp = array[k];
            array[k] = array[n-1];
            array[n-1] = tmp;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:56

    The only way to significantly improve performance is to find a way to avoid iterating through all the permutations in the first place!

    Permuting is an unavoidably slow operation (O(n!), or worse, depending on what you do with each permutation), unfortunately nothing you can do will change this fact.

    Also, note that any modern compiler will flatten out your recursion when optimisations are enabled, so the (small) performance gains from hand-optimising are reduced even further.

    0 讨论(0)
提交回复
热议问题