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

后端 未结 10 2605
一向
一向 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:16

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-04 16:19

    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.

    0 讨论(0)
  • 2020-12-04 16:23

    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]]
    
    0 讨论(0)
  • 2020-12-04 16:27

    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 讨论(0)
  • 2020-12-04 16:29

    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
    
    0 讨论(0)
  • 2020-12-04 16:30

    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.

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