class MoreMath::Permutation
def self.for(collection, rank = 0)
collection should respond to size, [], and []=. The Permutation
collection as the default Permutation#project data object. A
A permutation instance of size collection.size is created with
def self.for(collection, rank = 0) perm = new(collection.size, rank) perm.instance_variable_set(:@collection, collection) perm end
def self.from_cycles(cycles, max = 0)
cycles
. This is for example the result of aCreates a new Permutation instance from the Array of Arrays
def self.from_cycles(cycles, max = 0) indices = Array.new(max) cycles.each do |cycle| cycle.empty? and next for i in 0...cycle.size indices[ cycle[i - 1] ] = cycle[i] end end indices.each_with_index { |r, i| r or indices[i] = i } from_value(indices) end
def self.from_value(indices)
in the range of
0
and indices.size - 1
. This isindices
, that should consist of a permutation of FixnumsCreates a new Permutation instance from the Array
def self.from_value(indices) obj = new(indices.size) obj.instance_eval do self.rank = rank_indices(indices) end obj end
def <=>(other)
and the Permutation#rank.
Compares to Permutation instances according to their Permutation#size
def <=>(other) size <=> other.size.zero? || rank <=> other.rank end
def compose(other)
p1 * -p1
Or a little nicer to look at:
=> #
p1.compose(p2)
# => #
p2 = p1.invert
# => #
p1 = Permutation.new(5, 42)
Example:
the identity permutation, the permutation with rank 0.
composed with it's inverted permutation yields
a new Permutation. Note that a permutation
Compose this Permutation instance and the other to
def compose(other) size == other.size or raise ArgumentError, "permutations of unequal sizes cannot be composed!" indices = self.value composed = other.value.map { |i| indices[i] } klon = clone klon.rank = rank_indices(composed) klon end
def cycles
perm.cycles
# => #
perm = Permutation.new(7, 23)
Example:
new Permutation instance with the Permutation.from_cycles method.
The return value of this method can be used to create a
Returns the cycles representation of this Permutation instance.
def cycles perm = value result = [[]] seen = {} current = nil loop do current or current = perm.find { |x| !seen[x] } break unless current if seen[current] current = nil result << [] else seen[current] = true result[-1] << current current = perm[current] end end result.pop result.select { |c| c.size > 1 }.map do |c| min_index = c.index(c.min) c[min_index..-1] + c[0...min_index] end end
def eql?(other)
value, that is both Permutation instances have the same Permutation#size
Returns true if this Permutation instance and the other have the same
def eql?(other) self.class == other.class && size == other.size && rank == other.rank end
def even?
def even? signum == 1 end
def factorial(n)
def factorial(n) @@fcache.size.upto(n) { |i| @@fcache[i] = i * @@fcache[i - 1] } @@fcache[n] end
def hash
def hash size.hash ^ rank.hash end
def initialize(size, rank = 0)
rank
).Creates a new Permutation instance of
size
def initialize(size, rank = 0) @size, self.rank = size, rank @last = factorial(size) - 1 end
def invert
Returns the inverted Permutation of this Permutation instance.
def invert clone.invert! end
def invert!
Switchtes this Permutation instance to the inverted permutation.
def invert! indices = unrank_indices(rank) inverted = Array.new(size) for i in 0...size inverted[indices[i]] = i end self.rank = rank_indices(inverted) self end
def odd?
def odd? signum == -1 end
def project(data = @collection)
perm.project("abcdef")
# => #
perm = Permutation.new(6, 312)
Example:
it is used as a data object.
with Permutation.for the collection used to create
the #[] method. If this Permutation inbstance was created
into the
data
object that should respond toReturns the projection of this instance's Permutation#value
def project(data = @collection) data or raise ArgumentError, "a collection is required to project" raise ArgumentError, "data size is != #{size}!" if data.size != size projection = data.clone value.each_with_index { |i, j| projection[j] = data[i] } projection end
def rank=(m)
rank
.Permutation#value method of this instance is the permutation ranked with
instance. That implies that the indices produced by a call to the
Assigns
m
to the rank attribute of this Permutation
def rank=(m) @rank = m % factorial(size) end
def rank_indices(p)
def rank_indices(p) result = 0 for i in 0...size result += p[i] * factorial(size - i - 1) for j in (i + 1)...size p[j] -= 1 if p[j] > p[i] end end result end
def signum
transpositions (cycles of length 2), or even if it can be represented of
A permutation is odd if it can be represented by an odd number of
an even permutation.
It's -1 if this permutation is odd and 1 if it's
Returns the signum of this Permutation instance.
def signum s = 1 cycles.each do |c| c.size % 2 == 0 and s *= -1 end s end
def unrank_indices(m)
def unrank_indices(m) result = Array.new(size, 0) for i in 0...size f = factorial(i) x = m % (f * (i + 1)) m -= x x /= f result[size - i - 1] = x x -= 1 for j in (size - i)...size result[j] += 1 if result[j] > x end end result end
def value
perm.value
# => #
perm = Permutation.new(6, 312)
Example:
of this permutation that is ranked with Permutation#rank.
Returns the indices in the range of 0 to Permutation#size - 1
def value unrank_indices(@rank) end