D
Daniel Sheppard
For a little project I've been doing, I've been playing around with
permutations of arrays, and came across a couple of interesting methods
that might want to find their way into facets or one of those other
libraries that are floating around. I'm not sure if it has any
application outside of trying to save disk/memory space, but here goes.
Part of what I want to do involves storing information about all the
different permutations of an array - the problem with this is that I'm
writing the same data repeatedly, just in different orderings. Take for
example, an array of 4 integers, sorted into all the permutations:
[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
..
[4, 2, 3, 1]
[3, 4, 2, 1]
[4, 3, 2, 1]
There's the same array 24 different times. Instead, using my new magic
"permutation_number" and "permute" methods, a unique number between 0
and 23 can be assigned to each sort ordering and that can be stored
instead. So, instead of storing "N! * N * fields" bytes, I only need to
store "N! * integers + N * fields". I can also seek into a binary file
based on the sort key to pull out data... which is nice. I don't know if
there's some famous mathematical formula that proves this, that gives
these methods a better name, or if they should be called "sort_key" and
"sort_by_key" or something.
class Array =20
=20 def permute(number)
=20 out =3D self[0..0]
=20 nextfactor =3D factor =3D 1
=20 each_with_index {|x,i|
=20 case i
=20 when 0: next
=20 else=20
=20 nextfactor =3D factor * (i+1)
=20 out.insert(number % nextfactor / factor, x)
=20 factor =3D nextfactor
=20 end
=20 } =20
=20 out
=20 end
=20 def permutation_number(original_array=3Dself.sort)
=20 m =3D 1
=20 v =3D 0
=20 last_indicies =3D Hash.new(0)
=20 original_array.each_with_index {|x,i|
=20 next if i=3D=3D0
=20 m *=3D i
=20 done =3D original_array[0..i]
=20 v +=3D m * self.select {|y| done.include?(y)}.rindex(x)
=20 }
=20 v
=20 end
end
[3,1,2,4].permutation_number
=3D> 19
[1,2,3,4].permute(19)
=3D> [4,3,2,1]
It basically works by building up an array from the original. There is 1
place for the first item, 2 available for the second, 3 for the third,
etc. So, if you look at the number 19:
3 * 3! =3D 18, leaves 1
0 * 2! =3D 0, leaves 1
1 * 1! =3D 1, leaves 0
so we then build up the array, starting with [1].
We insert the next item at position 1, giving [1,2]
we insert the next item at position 0, giving [3,1,2]
we insert the next item at position 3, giving [3,1,2,4]
Like magic it is.
Also, using the same technique, I rewrote the Array.each_permutation
method from Nano/Facets. This method runs about 25-30% slower, but it
uses O(N) memory instead of O(N!) memory, and wont fall over from a
stack overflow if you give it an array that's too long (though it will
eat your cpu until the end of eternity. Factorials over 10 get pretty
damn big).
class Array
=20 def each_permutation()
=20 perm_count =3D (1...size).inject(0) { |s,x| s + x * x.factorial=
=20}
=20 weights =3D Array.new(size-1) {|i| (i+1).factorial }
=20 s =3D weights.size
=20 x,i,v,pos =3D nil
=20 0.upto(perm_count) do |v|
=20 out =3D self[0..0]
=20 each_with_index {|x,i|
=20 case i
=20 when 0: next
=20 when s: out.insert(v / weights[i-1], x)
=20 else out.insert(v % weights / weights[i-1], x)
=20 end
=20 } =20
=20 yield out
=20 end =20
=20 end
end
#########################################################################=
############
This email has been scanned by MailMarshal, an email content filter.
#########################################################################=
############
permutations of arrays, and came across a couple of interesting methods
that might want to find their way into facets or one of those other
libraries that are floating around. I'm not sure if it has any
application outside of trying to save disk/memory space, but here goes.
Part of what I want to do involves storing information about all the
different permutations of an array - the problem with this is that I'm
writing the same data repeatedly, just in different orderings. Take for
example, an array of 4 integers, sorted into all the permutations:
[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
..
[4, 2, 3, 1]
[3, 4, 2, 1]
[4, 3, 2, 1]
There's the same array 24 different times. Instead, using my new magic
"permutation_number" and "permute" methods, a unique number between 0
and 23 can be assigned to each sort ordering and that can be stored
instead. So, instead of storing "N! * N * fields" bytes, I only need to
store "N! * integers + N * fields". I can also seek into a binary file
based on the sort key to pull out data... which is nice. I don't know if
there's some famous mathematical formula that proves this, that gives
these methods a better name, or if they should be called "sort_key" and
"sort_by_key" or something.
class Array =20
=20 def permute(number)
=20 out =3D self[0..0]
=20 nextfactor =3D factor =3D 1
=20 each_with_index {|x,i|
=20 case i
=20 when 0: next
=20 else=20
=20 nextfactor =3D factor * (i+1)
=20 out.insert(number % nextfactor / factor, x)
=20 factor =3D nextfactor
=20 end
=20 } =20
=20 out
=20 end
=20 def permutation_number(original_array=3Dself.sort)
=20 m =3D 1
=20 v =3D 0
=20 last_indicies =3D Hash.new(0)
=20 original_array.each_with_index {|x,i|
=20 next if i=3D=3D0
=20 m *=3D i
=20 done =3D original_array[0..i]
=20 v +=3D m * self.select {|y| done.include?(y)}.rindex(x)
=20 }
=20 v
=20 end
end
[3,1,2,4].permutation_number
=3D> 19
[1,2,3,4].permute(19)
=3D> [4,3,2,1]
It basically works by building up an array from the original. There is 1
place for the first item, 2 available for the second, 3 for the third,
etc. So, if you look at the number 19:
3 * 3! =3D 18, leaves 1
0 * 2! =3D 0, leaves 1
1 * 1! =3D 1, leaves 0
so we then build up the array, starting with [1].
We insert the next item at position 1, giving [1,2]
we insert the next item at position 0, giving [3,1,2]
we insert the next item at position 3, giving [3,1,2,4]
Like magic it is.
Also, using the same technique, I rewrote the Array.each_permutation
method from Nano/Facets. This method runs about 25-30% slower, but it
uses O(N) memory instead of O(N!) memory, and wont fall over from a
stack overflow if you give it an array that's too long (though it will
eat your cpu until the end of eternity. Factorials over 10 get pretty
damn big).
class Array
=20 def each_permutation()
=20 perm_count =3D (1...size).inject(0) { |s,x| s + x * x.factorial=
=20}
=20 weights =3D Array.new(size-1) {|i| (i+1).factorial }
=20 s =3D weights.size
=20 x,i,v,pos =3D nil
=20 0.upto(perm_count) do |v|
=20 out =3D self[0..0]
=20 each_with_index {|x,i|
=20 case i
=20 when 0: next
=20 when s: out.insert(v / weights[i-1], x)
=20 else out.insert(v % weights / weights[i-1], x)
=20 end
=20 } =20
=20 yield out
=20 end =20
=20 end
end
#########################################################################=
############
This email has been scanned by MailMarshal, an email content filter.
#########################################################################=
############