开发者

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

I need to calculate pe开发者_高级运维rmutations iteratively. The method signature looks like:

int[][] permute(int n)

For n = 3 for example, the return value would be:

[[0,1,2],
 [0,2,1],
 [1,0,2],
 [1,2,0],
 [2,0,1],
 [2,1,0]]

How would you go about doing this iteratively in the most efficient way possible? I can do this recursively, but I'm interested in seeing lots of alternate ways to doing it iteratively.


see QuickPerm algorithm, it's iterative : http://www.quickperm.org/

Edit:

Rewritten in Ruby for clarity:

def permute_map(n)
  results = []
  a, p = (0...n).to_a, [0] * n
  i, j = 0, 0
  i = 1
  results << yield(a)
  while i < n
    if p[i] < i
      j = i % 2 * p[i] # If i is odd, then j = p[i], else j = 0
      a[j], a[i] = a[i], a[j] # Swap
      results << yield(a)
      p[i] += 1
      i = 1
    else
      p[i] = 0
      i += 1
    end
  end
  return results
end


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;
}


Is using 1.9's Array#permutation an option?

>> a = [0,1,2].permutation(3).to_a
=> [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]


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));


I also came across the QuickPerm algorithm referenced in another answer. I wanted to share this answer in addition, because I saw some immediate changes one can make to write it shorter. For example, if the index array "p" is initialized slightly differently, it saves having to return the first permutation before the loop. Also, all those while-loops and if's took up a lot more room.

void permute(char* s, size_t l) {
    int* p = new int[l];
    for (int i = 0; i < l; i++) p[i] = i;
    for (size_t i = 0; i < l; printf("%s\n", s)) {
        std::swap(s[i], s[i % 2 * --p[i]]);
        for (i = 1; p[i] == 0; i++) p[i] = i;
    }
}


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;
}


Here's an implementation in C#, as an extension method:

public static IEnumerable<List<T>> Permute<T>(this IList<T> items)
{
    var indexes = Enumerable.Range(0, items.Count).ToArray();

    yield return indexes.Select(idx => items[idx]).ToList();

    var weights = new int[items.Count];
    var idxUpper = 1;
    while (idxUpper < items.Count)
    {
        if (weights[idxUpper] < idxUpper)
        {
            var idxLower = idxUpper % 2 * weights[idxUpper];
            var tmp = indexes[idxLower];
            indexes[idxLower] = indexes[idxUpper];
            indexes[idxUpper] = tmp;
            yield return indexes.Select(idx => items[idx]).ToList();
            weights[idxUpper]++;
            idxUpper = 1;
        }
        else
        {
            weights[idxUpper] = 0;
            idxUpper++;
        }
    }
}

And a unit test:

[TestMethod]
public void Permute()
{
    var ints = new[] { 1, 2, 3 };
    var orderings = ints.Permute().ToList();
    Assert.AreEqual(6, orderings.Count);
    AssertUtil.SequencesAreEqual(new[] { 1, 2, 3 }, orderings[0]);
    AssertUtil.SequencesAreEqual(new[] { 2, 1, 3 }, orderings[1]);
    AssertUtil.SequencesAreEqual(new[] { 3, 1, 2 }, orderings[2]);
    AssertUtil.SequencesAreEqual(new[] { 1, 3, 2 }, orderings[3]);
    AssertUtil.SequencesAreEqual(new[] { 2, 3, 1 }, orderings[4]);
    AssertUtil.SequencesAreEqual(new[] { 3, 2, 1 }, orderings[5]);
}

The method AssertUtil.SequencesAreEqual is a custom test helper which can be recreated easily enough.


How about a recursive algorithm you can call iteratively? If you'd actually need that stuff as a list like that (you should clearly inline that rather than allocate a bunch of pointless memory). You could simply calculate the permutation on the fly, by its index.

Much like the permutation is carry-the-one addition re-reversing the tail (rather than reverting to 0), indexing the specific permutation value is finding the digits of a number in base n then n-1 then n-2... through each iteration.

public static <T> boolean permutation(List<T> values, int index) {
    return permutation(values, values.size() - 1, index);
}
private static <T> boolean permutation(List<T> values, int n, int index) {
    if ((index == 0) || (n == 0))  return (index == 0);
    Collections.swap(values, n, n-(index % n));
    return permutation(values,n-1,index/n);
}

The boolean returns whether your index value was out of bounds. Namely that it ran out of n values but still had remaining index left over.

And it can't get all the permutations for more than 12 objects. 12! < Integer.MAX_VALUE < 13!

-- But, it's so very very pretty. And if you do a lot of things wrong might be useful.


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;
}


I've used the algorithms from here. The page contains a lot of useful information.

Edit: Sorry, those were recursive. uray posted the link to the iterative algorithm in his answer.

I've created a PHP example. Unless you really need to return all of the results, I would only create an iterative class like the following:

<?php
class Permutator implements Iterator
{
  private $a, $n, $p, $i, $j, $k;
  private $stop;
    
  public function __construct(array $a)
  {
    $this->a = array_values($a);
    $this->n = count($this->a);
  }
    
  public function current()
  {
    return $this->a;
  }
    
  public function next()
  {
    ++$this->k;
    while ($this->i < $this->n)
    {
      if ($this->p[$this->i] < $this->i)
      {
        $this->j = ($this->i % 2) * $this->p[$this->i];
    
        $tmp = $this->a[$this->j];
        $this->a[$this->j] = $this->a[$this->i];
        $this->a[$this->i] = $tmp;
    
        $this->p[$this->i]++;
        $this->i = 1;
        return;
      }
    
      $this->p[$this->i++] = 0;
    }
    
    $this->stop = true;
  }
    
  public function key()
  {
    return $this->k;
  }
    
  public function valid()
  {
    return !$this->stop;
  }
    
  public function rewind()
  {
    if ($this->n) $this->p = array_fill(0, $this->n, 0);
    $this->stop = $this->n == 0;
    $this->i = 1;
    $this->j = 0;
    $this->k = 0;
  }
    
}
    
foreach (new Permutator(array(1,2,3,4,5)) as $permutation)
{
  var_dump($permutation);
}
?>

Note that it treats every PHP array as an indexed array.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜