How would you calculate all possible permutations of 0 through N iteratively?

后端 未结 10 2606
一向
一向 2020-12-04 15:47

I need to calculate permutations iteratively. The method signature looks like:

int[][] permute(int n)

For n = 3 for example, the r

相关标签:
10条回答
  • 2020-12-04 16:32

    The algorithm for stepping from one permutation to the next is very similar to elementary school addition - when an overflow occurs, "carry the one".

    Here's an implementation I wrote in C:

    #include <stdio.h>
    
    //Convenience macro.  Its function should be obvious.
    #define swap(a,b) do { \
            typeof(a) __tmp = (a); \
            (a) = (b); \
            (b) = __tmp; \
        } while(0)
    
    void perm_start(unsigned int n[], unsigned int count) {
        unsigned int i;
        for (i=0; i<count; i++)
            n[i] = i;
    }
    
    //Returns 0 on wraparound
    int perm_next(unsigned int n[], unsigned int count) {
        unsigned int tail, i, j;
    
        if (count <= 1)
            return 0;
    
        /* Find all terms at the end that are in reverse order.
           Example: 0 3 (5 4 2 1) (i becomes 2) */
        for (i=count-1; i>0 && n[i-1] >= n[i]; i--);
        tail = i;
    
        if (tail > 0) {
            /* Find the last item from the tail set greater than
                the last item from the head set, and swap them.
                Example: 0 3* (5 4* 2 1)
                Becomes: 0 4* (5 3* 2 1) */
            for (j=count-1; j>tail && n[j] <= n[tail-1]; j--);
    
            swap(n[tail-1], n[j]);
        }
    
        /* Reverse the tail set's order */
        for (i=tail, j=count-1; i<j; i++, j--)
            swap(n[i], n[j]);
    
        /* If the entire list was in reverse order, tail will be zero. */
        return (tail != 0);
    }
    
    int main(void)
    {
        #define N 3
        unsigned int perm[N];
    
        perm_start(perm, N);
        do {
            int i;
            for (i = 0; i < N; i++)
                printf("%d ", perm[i]);
            printf("\n");
        } while (perm_next(perm, N));
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-04 16:32

    I have implemented the algorithm in Javascript.

    var all = ["a", "b", "c"];
    console.log(permute(all));
    
    function permute(a){
      var i=1,j, temp = "";
      var p = [];
      var n = a.length;
      var output = [];
    
      output.push(a.slice());
      for(var b=0; b <= n; b++){
        p[b] = b;
      }
    
      while (i < n){
        p[i]--;
        if(i%2 == 1){
          j = p[i];
        }
        else{
          j = 0;
        }
        temp = a[j];
        a[j] = a[i];
        a[i] = temp;
    
        i=1;
        while (p[i] === 0){
          p[i] = i;
          i++;
        }
        output.push(a.slice());
      }
      return output;
    }
    
    0 讨论(0)
  • 2020-12-04 16:34

    I found Joey Adams' version to be the most readable, but I couldn't port it directly to C# because of how C# handles the scoping of for-loop variables. Hence, this is a slightly tweaked version of his code:

    /// <summary>
    /// Performs an in-place permutation of <paramref name="values"/>, and returns if there 
    /// are any more permutations remaining.
    /// </summary>
    private static bool NextPermutation(int[] values)
    {
        if (values.Length == 0)
            throw new ArgumentException("Cannot permutate an empty collection.");
    
        //Find all terms at the end that are in reverse order.
        //  Example: 0 3 (5 4 2 1) (i becomes 2)
        int tail = values.Length - 1;
        while(tail > 0 && values[tail - 1] >= values[tail])
            tail--;
    
        if (tail > 0)
        {
            //Find the last item from the tail set greater than the last item from the head 
            //set, and swap them.
            //  Example: 0 3* (5 4* 2 1)
            //  Becomes: 0 4* (5 3* 2 1)
            int index = values.Length - 1;
            while (index > tail && values[index] <= values[tail - 1])
                index--;
    
            Swap(ref values[tail - 1], ref values[index]);
        }
    
        //Reverse the tail set's order.
        int limit = (values.Length - tail) / 2;
        for (int index = 0; index < limit; index++)
            Swap(ref values[tail + index], ref values[values.Length - 1 - index]);
    
        //If the entire list was in reverse order, tail will be zero.
        return (tail != 0);
    }
    
    private static void Swap<T>(ref T left, ref T right)
    {
        T temp = left;
        left = right;
        right = temp;
    }
    
    0 讨论(0)
  • 2020-12-04 16:36

    Below is my generics version of the next permutation algorithm in C# closely resembling the STL's next_permutation function (but it doesn't reverse the collection if it is the max possible permutation already, like the C++ version does)

    In theory it should work with any IList<> of IComparables.

        static bool NextPermutation<T>(IList<T> a) where T: IComparable
        {
            if (a.Count < 2) return false;
            var k = a.Count-2;
    
            while (k >= 0 && a[k].CompareTo( a[k+1]) >=0) k--;
            if(k<0)return false;
    
            var l = a.Count - 1;
            while (l > k && a[l].CompareTo(a[k]) <= 0) l--;
    
            var tmp = a[k];
            a[k] = a[l];
            a[l] = tmp;
    
            var i = k + 1;
            var j = a.Count - 1;
            while(i<j)
            {
                tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
                i++;
                j--;
            }
    
            return true;
        }
    

    And the demo/test code:

            var src = "1234".ToCharArray();
            do
            {
                Console.WriteLine(src);
            } 
            while (NextPermutation(src));
    
    0 讨论(0)
提交回复
热议问题