I have written the following program to find the permutation of all the elements in an array. The values are created properly but the problem occurs when I try to assign the generated sequence into a new array. The old values will get cleared and the new values are copied as per the array size
def find_perm(nums, answer, set)
if nums.empty?
p set
answer.push(set)
p answer.object_id
p answer
return true
end
for i in (0..nums.length - 1) do
new_nums = nums.clone
new_nums.delete_at(i)
set.push(nums[i])
find_perm(new_nums, answer, set)
set.pop
end
end
def permute(nums)
answer = []
set = []
element = find_perm(nums, answer, set)
return element
end
permute([1,2,3])
This are the observations that I have found out while debugging:
[1, 2, 3]
47167191669680
[[1, 2, 3]]
[1, 3, 2]
47167191669680
[[1, 3, 2], [1, 3, 2]]
[2, 1, 3]
47167191669680
[[2, 1, 3], [2, 1, 3], [2, 1, 3]]
[2, 3, 1]
47167191669680
[[2, 3, 1], [2, 3, 1], [2, 3, 1], [2, 3, 1]]
[3, 1, 2]
47167191669680
[[3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2]]
[3, 2, 1]
47167191669680
[[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]
The problem was each time I was pushing the same set array into the answer array so each element in the answer array will have same reference (same object_id).
Solution: Is to clone the set array during the each push to the answer array so that each element have different reference.
The solution:
def fact(n)
return 1 if n == 1
n*fact(n-1)
end
def find_perm(nums, answer, set)
if nums.empty?
answer.push(set.clone)
end
for i in (0..nums.length - 1) do
new_nums = nums.clone
new_nums.delete_at(i)
set.push(nums[i])
find_perm(new_nums, answer, set)
set.pop
return answer if fact(nums.count) == answer.count
end
end
def permute(nums)
answer = []
set = []
element = find_perm(nums, answer, set)
return element
end
p permute([1,2,3])
I believe the approach you are taking is similar to the following.
Suppose we wish to obtain the permutations of the elements of the array
arr = [1, 2, 3, 4]
Begin with the array
[4]
This array has only a single perumutation:
perms3 = [[4]]
(3 in perms3 denotes the index of 4 in arr.) Now obtain the permuations of
[3, 4]
We see that is
perms2 = [[3, 4], [4, 3]]
We simply take each element of perms3 ([4] is the only one) and create two permuations by inserting 3 before 4 and then 3 after 4;
Now suppose the array were
[2, 3, 4]
Then
perms1 = [[2, 3, 4], [3, 2, 4], [3, 4, 2], [2, 4, 3], [4, 2, 3], [4, 3, 2]]
We create three 3-element arrays from [3, 4], one by inserting 2 before 3, one by inserting 2 between 3 and 4 and one by inserting 2 after 4. Simlarly, three 3-element arrays are generated from [4, 3] in a simlar way. This generates six arrays. (Indeed, 3! #=> 6).
Lastly we generating the 4! #=> 24 permuations of [1, 2, 3, 4] by inserting 1 in four locations of each element of perm1, the first four derived from perms1[0] #=> [2, 3, 4]:
[[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], [2, 3, 4, 1]]
We can do this in code as follows.
def my_permutations(arr)
perms = [[arr.last]]
(arr.size-2).downto(0) do |i|
x = arr[i]
perms = perms.flat_map do |perm|
(0..(perm.size)).map { |i| perm.dup.insert(i, x) }
end
end
perms
end
my_permutations(arr)
#=> [[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], [2, 3, 4, 1], [1, 3, 2, 4],
# [3, 1, 2, 4], [3, 2, 1, 4], [3, 2, 4, 1], [1, 3, 4, 2], [3, 1, 4, 2],
# [3, 4, 1, 2], [3, 4, 2, 1], [1, 2, 4, 3], [2, 1, 4, 3], [2, 4, 1, 3],
# [2, 4, 3, 1], [1, 4, 2, 3], [4, 1, 2, 3], [4, 2, 1, 3], [4, 2, 3, 1],
# [1, 4, 3, 2], [4, 1, 3, 2], [4, 3, 1, 2], [4, 3, 2, 1]]
See Enumerable#flat_map and Array#insert. Note that we need to make a copy of the array perm before invoking insert.
We could of course have gone "forward" in arr (starting with [[1]]), rather than "backward", though the elements of the array of permutations would be ordered differently.
I want to get all non-contiguous sets (i.e. any subset whose elements can't be adjacent in the original set):
go([1,2,3,4,5]) => [1],[1,3,5],[1,3],[1,4],[1,5],[2],[2,4],[2,5],[3]
I am fairly close with:
def go(ns)
return [[]] if ns == [] || ns == nil
return [[ns[0]]] if ns.length < 3
(0..ns.length-1).to_a.map do |i|
dup = go(ns[i+2..-1])
dup.map do |a|
[ns[i]] + a
end
end
end
This gives:
[[[1, [3, 5]], [1, [4]], [1, [5]]], [[2, 4]], [[3, 5]], [[4]], [[5]]]
which is close to the right results (misses [1], I just have the concat of the arrays messed up and can't figure out how to flatten them.
Note the elements can be any numbers, they are just unique numbers in no order, they could be for example
[35, 40, 100, 54, 13]
For a given n > 1 I have computed an array of all arrays containing one or more integers of the form [a, ... , b] where a >= 1, b <= n and for each adjacent pairs of element i, j, j > i+1. If n = 1, [[1]] is returned.
I've done this using recursion. recurse(m,n) computes all such arrays whose first element is m.
Code
def doit(n)
(1..n).reduce([]) { |a,m| a + recurse(m,n) }
end
def recurse(m,n)
return [[m]] if m >= n-1
(m+2..n).reduce([[m]]) { |a,p| a + recurse(p,n).map { |b| [m]+b } }
end
Examples
doit 6
#=> [[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
# [2], [2, 4], [2, 4, 6], [2, 5], [2, 6],
# [3], [3, 5], [3, 6], [4], [4, 6], [5], [6]]
doit 8
#=> [[1], [1, 3], [1, 3, 5], [1, 3, 5, 7], [1, 3, 5, 8], [1, 3, 6],
# [1, 3, 6, 8], [1, 3, 7], [1, 3, 8], [1, 4], [1, 4, 6], [1, 4, 6, 8],
# [1, 4, 7], [1, 4, 8], [1, 5], [1, 5, 7], [1, 5, 8], [1, 6],
# [1, 6, 8], [1, 7], [1, 8],
# [2], [2, 4], [2, 4, 6], [2, 4, 6, 8], [2, 4, 7], [2, 4, 8], [2, 5],
# [2, 5, 7], [2, 5, 8], [2, 6], [2, 6, 8], [2, 7], [2, 8],
# [3], [3, 5], [3, 5, 7], [3, 5, 8], [3, 6], [3, 6, 8], [3, 7], [3, 8],
# [4], [4, 6], [4, 6, 8], [4, 7], [4, 8],
# [5], [5, 7], [5, 8],
# [6], [6, 8],
# [7],
# [8]]
Explanation
I believe the clearest way for me to explain how the recursion works—especially for readers with limited experience using recursion—is to simply execute the code after it has been salted with puts statements. I've indented whenever the method calls itself and outdented whenever the method returns.
INDENT = 6
#pos = 0
def indent; #pos += INDENT; #s = ' '*#pos; end
def outdent; #pos -= INDENT; #s = ' '*#pos; end
def doit(n)
puts "doit: n=#{n}"
(1..n).reduce([]) do |a,m|
puts "a=#{a}"
puts "calling recurse(#{m},#{n})"
indent
a + recurse(m,n)
end
end
def recurse(m,n)
puts "\n#{#s}entered recurse(#{m},#{n})"
if m >= n-1
puts "#{#s}returning #{[[m]]} as m >= n-1\n\n"
outdent
return [[m]]
end
puts "#{#s}begin reduce"
a = (m+2..n).reduce([[m]]) do |a,p|
puts "#{#s} p=#{p}, a=#{a}"
puts "#{#s} calling recurse(#{p},#{n})"
indent
arr = recurse(p,n).map { |b| [m]+b }
puts "#{#s} back to recurse(#{m},#{n}) from recurse(#{p},#{n})"
puts "#{#s} array returned mapped to #{arr}"
a + arr
end
puts "#{#s}return #{a} from recurse(#{m},#{n})\n\n"
outdent
a
end
doit 6
doit: n=6
a=[]
calling recurse(1,6)
entered recurse(1,6)
begin reduce
p=3, a=[[1]]
calling recurse(3,6)
entered recurse(3,6)
begin reduce
p=5, a=[[3]]
calling recurse(5,6)
entered recurse(5,6)
returning [[5]] as m >= n-1
back to recurse(3,6) from recurse(5,6)
array returned mapped to [[3, 5]]
p=6, a=[[3], [3, 5]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(3,6) from recurse(6,6)
array returned mapped to [[3, 6]]
return [[3], [3, 5], [3, 6]] from recurse(3,6)
back to recurse(1,6) from recurse(3,6)
array returned mapped to [[1, 3], [1, 3, 5], [1, 3, 6]]
p=4, a=[[1], [1, 3], [1, 3, 5], [1, 3, 6]]
calling recurse(4,6)
entered recurse(4,6)
begin reduce
p=6, a=[[4]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(4,6) from recurse(6,6)
array returned mapped to [[4, 6]]
return [[4], [4, 6]] from recurse(4,6)
back to recurse(1,6) from recurse(4,6)
array returned mapped to [[1, 4], [1, 4, 6]]
p=5, a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6]]
calling recurse(5,6)
entered recurse(5,6)
returning [[5]] as m >= n-1
back to recurse(1,6) from recurse(5,6)
array returned mapped to [[1, 5]]
p=6, a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(1,6) from recurse(6,6)
array returned mapped to [[1, 6]]
return [[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5],
[1, 6]] from recurse(1,6)
a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6]]
calling recurse(2,6)
entered recurse(2,6)
begin reduce
p=4, a=[[2]]
calling recurse(4,6)
entered recurse(4,6)
begin reduce
p=6, a=[[4]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(4,6) from recurse(6,6)
array returned mapped to [[4, 6]]
return [[4], [4, 6]] from recurse(4,6)
back to recurse(2,6) from recurse(4,6)
array returned mapped to [[2, 4], [2, 4, 6]]
p=5, a=[[2], [2, 4], [2, 4, 6]]
calling recurse(5,6)
entered recurse(5,6)
returning [[5]] as m >= n-1
back to recurse(2,6) from recurse(5,6)
array returned mapped to [[2, 5]]
p=6, a=[[2], [2, 4], [2, 4, 6], [2, 5]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(2,6) from recurse(6,6)
array returned mapped to [[2, 6]]
return [[2], [2, 4], [2, 4, 6], [2, 5], [2, 6]] from recurse(2,6)
a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
[2], [2, 4], [2, 4, 6], [2, 5], [2, 6]]
calling recurse(3,6)
entered recurse(3,6)
begin reduce
p=5, a=[[3]]
calling recurse(5,6)
entered recurse(5,6)
returning [[5]] as m >= n-1
back to recurse(3,6) from recurse(5,6)
array returned mapped to [[3, 5]]
p=6, a=[[3], [3, 5]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(3,6) from recurse(6,6)
array returned mapped to [[3, 6]]
return [[3], [3, 5], [3, 6]] from recurse(3,6)
a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
[2], [2, 4], [2, 4, 6], [2, 5], [2, 6], [3], [3, 5], [3, 6]]
calling recurse(4,6)
entered recurse(4,6)
begin reduce
p=6, a=[[4]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
back to recurse(4,6) from recurse(6,6)
array returned mapped to [[4, 6]]
return [[4], [4, 6]] from recurse(4,6)
a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
[2], [2, 4], [2, 4, 6], [2, 5], [2, 6], [3], [3, 5], [3, 6], [4], [4, 6]]
calling recurse(5,6)
entered recurse(5,6)
returning [[5]] as m >= n-1
a=[[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
[2], [2, 4], [2, 4, 6], [2, 5], [2, 6], [3], [3, 5], [3, 6], [4], [4, 6], [5]]
calling recurse(6,6)
entered recurse(6,6)
returning [[6]] as m >= n-1
#=> [[1], [1, 3], [1, 3, 5], [1, 3, 6], [1, 4], [1, 4, 6], [1, 5], [1, 6],
# [2], [2, 4], [2, 4, 6], [2, 5], [2, 6], [3], [3, 5], [3, 6], [4], [4, 6],
# [5], [6]]
I was studying how to list out all divisors of a number and came across this solution by Marc-Andre here. In his solution, there is one part of the code which does something like this:
array.product(*arrays_of_array) # the asterisk seems to have done sth.
I tried it in irb to try play around but I couldn't make sense of the outputs. I tried:
a=[0,1,2]
b=[3,4]
c=[[5,6],[7,8]]
I understand that array.product(other_array) is a method to list all combinations of the two arrays into one. With this knowledge, I tested out several experiments
a.product(b) => [[0, 3], [0, 4], [1, 3], [1, 4], [2, 3], [2, 4]] / 6 elements
a.product(*b) => TypeError: no implicit conversion of Fixnum into Array
a.product(c) => [[0, [5, 6]], [0, [7, 8]], [1, [5, 6]], [1, [7, 8]], [2, [5, 6]], [2, [7, 8]]] / 6 elements
a.product(*c) => [[0, 5, 7], [0, 5, 8], [0, 6, 7], [0, 6, 8], [1, 5, 7], [1, 5, 8], [1, 6, 7], [1, 6, 8], [2, 5, 7], [2, 5, 8], [2, 6, 7], [2, 6, 8]]
From observation, It seems the asterisk (*) has to be applied to a multi-dimensional array? (i.e. matrix?). Without the asterisk, the product returns 6 elements and the combinations only one level. While with the asterisk, the combination will go 1 level deeper and returns 12 elements, and combine until there is no array within the combinations. Where can I find more examples to study this behaviour of the asterisk?
Edit:
I tried to introduce one more variable
d=[[[9,0],[1,2]],[[3,4],[5,6]]]
a.product(*d) => [[0, [9, 0], [3, 4]], [0, [9, 0], [5, 6]], [0, [1, 2], [3, 4]], [0, [1, 2], [5, 6]], [1, [9, 0], [3, 4]], [1, [9, 0], [5, 6]], [1, [1, 2], [3, 4]], [1, [1, 2], [5, 6]], [2, [9, 0], [3, 4]], [2, [9, 0], [5, 6]], [2, [1, 2], [3, 4]], [2, [1, 2], [5, 6]]]
So the asterisk sign only makes it go one level deeper.
In the context of finding the list of divisors. Can anyone explain what the code exactly does?
require 'prime'
def factors_of(number)
primes, powers = number.prime_division.transpose
exponents = powers.map{|i| (0..i).to_a}
divisors = exponents.shift.product(*exponents).map do |powers|
primes.zip(powers).map{|prime, power| prime ** power}.inject(:*)
end
divisors.sort.map{|div| [div, number / div]}
end
p factors_of(4800) # => [[1, 4800], [2, 2400], ..., [4800, 1]]
*(splat) is used to expand collections.
In your example, with b = [3,4],
a.product(*b)
is equivalent to
a.product(3, 4)
which generates an error because Array#product expects an Array as argument, not two integers.
This question already has an answer here:
Ruby Array Initialization [duplicate]
(1 answer)
Closed 3 years ago.
What is going on in the Array initialization that's causing the disparity in int assignment?
arr = Array.new(3) { Array.new(3) { Array.new(3) } }
3.times do |x|
3.times do |y|
3.times do |z|
arr[x][y][z] = Random.rand(1..9)
end
end
end
puts arr.to_s
#=> [[[3, 3, 1], [4, 9, 6], [2, 4, 7]], [[1, 6, 8], [9, 8, 5], [1, 7, 5]], [[2, 5, 9], [2, 8, 8], [9, 1, 8]]]
#=> [[[2, 4, 4], [6, 8, 9], [6, 2, 7]], [[2, 7, 7], [2, 1, 1], [8, 7, 7]], [[5, 3, 5], [3, 8, 1], [7, 6, 6]]]
#=> [[[4, 9, 1], [1, 6, 8], [9, 2, 5]], [[3, 7, 1], [7, 5, 4], [9, 9, 9]], [[6, 8, 2], [8, 2, 8], [2, 9, 9]]]
arr = Array.new(3, Array.new(3, Array.new(3)))
3.times do |x|
3.times do |y|
3.times do |z|
arr[x][y][z] = Random.rand(1..9)
end
end
end
puts arr.to_s
#=> [[[8, 2, 4], [8, 2, 4], [8, 2, 4]], [[8, 2, 4], [8, 2, 4], [8, 2, 4]], [[8, 2, 4], [8, 2, 4], [8, 2, 4]]]
#=> [[[2, 1, 4], [2, 1, 4], [2, 1, 4]], [[2, 1, 4], [2, 1, 4], [2, 1, 4]], [[2, 1, 4], [2, 1, 4], [2, 1, 4]]]
#=> [[[2, 7, 6], [2, 7, 6], [2, 7, 6]], [[2, 7, 6], [2, 7, 6], [2, 7, 6]], [[2, 7, 6], [2, 7, 6], [2, 7, 6]]]
When you use new(size=0, obj=nil) to initialize the array:
From the doc:
In the first form, if no arguments are sent, the new array will be
empty. When a size and an optional obj are sent, an array is created
with size copies of obj. Take notice that all elements will reference
the same object obj.
If you want multiple copy, then you should use the block version which uses the result of that block each time an element of the array needs to be initialized.