Saturday, September 7, 2013

Permutations of a list

Permutations of a list

Find all the permutations of a list. For example, given the list [1,2, 3] compute all of [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1],[3, 1, 2], [3, 2, 1]].

The reasoning here goes like this. Suppose I know how to compute all the permutations of a list that start with the element at position k. Then the list of all permutations can be obtained by calling that function with k = 0, ..., N-1 where N is the length of the list. How do you compute the permutations of a list that start with the element at position k? Well, you compute all the permutations of the reduced list that doesn't contain the kth element and prepend them all with the kth element.

Python

def take(k, lst):
   '''The list slice, lst[0:k].
  
      >>> print (str(take(0, [1, 2, 3])))
      []
      >>> print (str(take(1, [1, 2, 3])))
      [1]
      >>> print (str(take(2, [1, 2, 3])))
      [1, 2]
      >>> print (str(take(3, [1, 2, 3])))
      [1, 2, 3]
      >>> print (str(take(4, [1, 2, 3])))
      [1, 2, 3]
  
   '''
   if(k <= 0):
       return []
   if(len(lst) == 0):
       return []
   return [lst[0]]+take(k-1, lst[1:])
  
def drop(k, lst):
    '''The list slice, lst[k:].
  
       >>> print (str(drop(0, [1, 2, 3])))
       [1, 2, 3]
       >>> print (str(drop(1, [1, 2, 3])))
       [2, 3]
       >>> print (str(drop(2, [1, 2, 3])))
       [3]
       >>> print (str(drop(3, [1, 2, 3])))
       []
       >>> print (str(drop(4, [1, 2, 3])))
       []
  
    '''
    if(k <= 0):
        return lst
    if(len(lst)==0):
        return []
    return drop(k-1, lst[1:])
  
def find_all_permutations(lst):
  '''Find all permutations of a list.
  
     >>> print (str(find_all_permutations([1, 2, 3])))
     [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
  
  '''
  def find_k_permutations(k, lst):
    '''Find all permutations of the given list that start with the
       element at index k.
  
    '''
    x = lst[k]
    listp=take(k, lst)+drop(k+1, lst)
    ps=find_all_permutations(listp)
    s = list (map (lambda perm : [x] + perm, ps))
 
    return s
  
  def loop(k, acc, lst):
      if(k == len(lst)):
          return acc
      else:
          return loop(k+1, acc + find_k_permutations(k, lst), lst)
  
  return ([[]], loop(0, [], lst))[len(lst)!=0]
  
if __name__ == "__main__":
    import doctest
    doctest.testmod()

C++

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>

typedef std::vector<int> perm_t;
typedef std::vector<int>::iterator perm_iterator_t;
typedef std::vector<int>::const_iterator perm_const_iterator_t;

typedef std::vector<perm_t> perm_set_t;
typedef std::vector<perm_t>::iterator perm_set_iterator_t;
typedef std::vector<perm_t>::const_iterator perm_set_const_iterator_t;

//-- print functions

void print_perm (perm_t const& s)
{
  std::cout << "["; 
  std::ostream_iterator<int> out_it (std::cout, ", ");
  std::copy (s.begin(), s.end(), out_it);
  std::cout << "],"; 
}

void print_perm_set (perm_set_t const& ts)
{
  std::cout << "[";
  perm_set_const_iterator_t begin=ts.begin (), end = ts.end ();
  perm_set_const_iterator_t t=begin;
  while (t != end)
    {
      print_perm (*t);
      ++t;
    }
  std::cout << "]";
}

//-- take, drop

perm_t take (int k, perm_t const& lst)
{
  if (k <= 0)
    {
      return perm_t ();
    }
  if (lst.size() == std::size_t (0))
    {
      return perm_t ();
    }

  perm_t t = take (k-1, perm_t (++(lst.begin()), lst.end()));
  perm_t res (t.size()+1);
  res[0] = lst[0];
  for (std::size_t i = 0; i < t.size (); ++i)
    res [i+1] = t[i];

  return res;
}

perm_t drop (int k, perm_t const& lst)
{
  if (k <= 0)
    {
      return lst;
    }

  if (lst.size () == 0)
    {
      return perm_t ();
    }

  return drop (k - 1, perm_t (++(lst.begin()), lst.end()));
}

//-- helpers

perm_set_t find_all_permutations (perm_t const& lst);

//Prepend x to a perm.

struct prepend
{
  int x;

  prepend (int x) : x (x)
  {}

  perm_t operator ()(perm_t const& p) const
  {
    perm_t r (p.size() + 1);
    r[0] = x;
    for (std::size_t i = 0; i < p.size(); ++i)
      r[i + 1] = p[i];

    return r;
  }
};

//Find all permutations of the given list that
//start with the element at index k.

perm_set_t find_k_permutations (int k, perm_t const& lst)
{
  int x = lst[k];
  perm_t left = take (k, lst);
  perm_t right = drop (k + 1, lst);
  perm_t listp;
  std::copy (left.begin(), left.end(), std::back_inserter (listp));
  std::copy (right.begin(), right.end(), std::back_inserter (listp));
  perm_set_t ps = find_all_permutations (listp);
  perm_set_t s;
  std::transform (ps.begin(), ps.end(), std::back_inserter(s), prepend (x));

  return s;
}

perm_set_t loop (int k, perm_set_t acc, perm_t lst)
{
  if (k == lst.size())
    return acc;
  else
    {
      perm_set_t t = find_k_permutations (k, lst);
      std::copy (t.begin (), t.end (), std::back_inserter (acc));
      
      return loop (k+1, acc, lst);
    }
}

perm_set_t find_all_permutations (perm_t const& lst)
{
  if (lst.size() == 0)
    {
      perm_set_t res;
      res.push_back (perm_t ());//The empty sequence.

      return res;
    }

  return loop (0, perm_set_t (), lst);
}

//-- test

int main()
{
  int data[] = {1, 2, 3};
  perm_t input (data, data + sizeof(data)/sizeof(int));

  print_perm_set (find_all_permutations (input));

  return 0;
}