Iterating through Permutations
June 17, 2007
In a previous post I described how to use inversion tables to iterate through all the permutations of a set. Here’s a Ruby code implementation. First how to use it:
a = %w( a b c )
p = Permutation.new(a.size)
(0...a.size.factorial).each do |i|
puts p.permute(a).join
p.next!
end
The output is:
abc
acb
bac
bca
cab
cba
The constructor takes the size of the permutation as an argument. It starts with the identity permutation. An optional second argument specifies a later permutation in the sequence.
It’s also possible to specify a permutation by passing the constructor an array. For an n-permutation, the array must contain the integers from 0 to n-1 in some order.
The next method will return nil when it gets to the end of the sequence.
The compose method creates a new permutation from the argument permutation and the object permutation.
Here’s the source:
class Integer
def factorial
(1..self).inject(1) { |n,i| n*i }
end
end
class Permutation
def initialize(arg, n=0)
begin
@size = arg.length
@it = Permutation.to_inversion_table(arg)
@n = @it_n = Permutation.inversion_table_number(@it)
rescue NoMethodError
@size = arg
@n = n
end
end
def permute(a)
b = Array.new
(0..._permutation_array.size).each do |i|
b[_permutation_array[i]] = a[i]
end
r = b.size...a.size
b[r] = a[r]
b
end
def permute!(a)
b = permute(a)
(0...a.size).each { |i| a[i] = b[i] }
end
def next
self.deep_dup.next!
end
def next!
_inversion_table
next_inversion_table
end
def self.to_permutation_array(it)
pa = Array.new
(it.size-1).downto(0) do |i|
pa.insert(it[i],i)
end
pa
end
def self.inversion_table_number(it)
(1..it.size).inject(0) do |m,o|
m += (it.size-o)*(it.size-o).factorial
end
end
def self.to_inversion_table(pa)
t = pa.dup
it = Array.new
(0...pa.size).each do |i|
it[i] = t.index(i)
raise "not a permutation array: #{pa.inspect}" if it[i].nil?
t.delete(i)
end
it
end
def permutation_array
_permutation_array.dup
end
def inversion_table
_inversion_table.dup
end
def compose(p)
a = p._permutation_array
b = _permutation_array
raise "permutation size mismatch" if a.size != b.size
c = Array.new
(0...a.size).each { |i| c[i] = b[a[i]] }
Permutation.new(c)
end
def compose!(p)
@pa = compose(p).permutation_array
@it = Permutation.to_inversion_table(@pa)
@it_n = Permutation.inversion_table_number(@it)
end
protected
def deep_dup
p = self.dup
p.dup_attributes
p
end
def dup_attributes
@it = @it.dup unless @it.nil?
@pa = @pa.dup unless @pa.nil?
end
def _inversion_table
return @it if !@it.nil? and @it_n == @n
if @it.nil?
@it = Array.new(@size,0)
@it_n = 0
end
while (@it_n < @n )
next_inversion_table
end
@it
end
def next_inversion_table
@pa = nil
@it_n += 1
(@size-1).downto(0) do |i|
@it[i] += 1
return if @it[i] <= @size-(i+1)
@it[i] = 0
end
@it_n = 0
@it = nil
end
def _permutation_array
return @pa unless @pa.nil?
@pa = Permutation.to_permutation_array(_inversion_table)
end
end