Related
I have a list structured like follows:
s = [
[
[[0, 1, 1], [0, 0, 0]],
[[0, 0, 1], [0, 1, 0]],
[[0, 0, 0], [0, 1, 1]],
[[1, 0, 0], [0, 0, 0]],
[[0, 0, 0], [1, 0, 0]],
[[1, 0, 0], [0, 0, 0]],
],
[
[[1, 0, 0], [0, 0, 0]],
[[0, 0, 0], [1, 0, 0]],
[[0, 1, 1], [0, 0, 0]],
[[0, 0, 1], [0, 1, 0]],
[[0, 1, 0], [0, 0, 1]],
[[0, 0, 0], [0, 1, 1]]
],
# And so on
]
For each element at the first index, s[0], if the second sub-array, s[0][i][1], matches a corresponding first element in s[1], then we have a path, or a part of a path. It's complicated so I'll give an example:
s = [
[
[[0, 1, 1], *[0, 0, 0]*], <---- This goes to multiple possibilities
[[0, 0, 1], [0, 1, 0]],
[[0, 0, 0], [0, 1, 1]],
[[1, 0, 0], [0, 0, 0]],
[[0, 0, 0], [1, 0, 0]],
[[1, 0, 0], [0, 0, 0]],
],
[
[[1, 0, 0], [0, 0, 0]],
[*[0, 0, 0]*, [1, 0, 0]], <- here
[[0, 1, 1], [0, 0, 0]],
[[0, 0, 1], [0, 1, 0]],
[[0, 1, 0], [0, 0, 1]],
[*[0, 0, 0]*, [0, 1, 1]] <- and here
],
# And so on
]
How can I find the amount of paths for each given element?
Say I have the following input:
inp = [2, 9, 3]
I need output as all tuples in mixed counting, like this:
outp = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], ..., [1, 8, 2]]
I know algorithm from Knuth vol 4a as direct loop solution, but I've heard ruby has some magic inside.
I am mostly C++ developer. My direct solution now looks like:
inparr = [2, 9, 3]
bmix = Array.new(inparr.size) { |i| 0 }
outp = Array.new
loop do
# some debug output
puts bmix.to_s
#visit next tuple
outp << bmix.clone
digit = inparr.size
while digit > 0 do
digit -= 1
if bmix[digit] + 1 < inparr[digit]
bmix[digit] += 1
break
end
bmix[digit] = 0
end
break if (bmix.select{|x| x != 0}.empty?)
end
How to rewrite it in several simple lines?
inp.
map { |i| (0...i).to_a }.
reduce(&:product).
map(&:flatten)
Used operations: Range, Enumerable#map, Enumerable#reduce, Array#product, Array#flatten.
You could use recursion.
def recurse(inp)
first, *rest = inp
rest.empty? ? [*0..first-1] : (0..first-1).flat_map do |e|
recurse(rest).map { |arr| [e, *arr] }
end
end
recurse [2, 4, 3]
#=> [[0, 0, 0], [0, 0, 1], [0, 0, 2],
# [0, 1, 0], [0, 1, 1], [0, 1, 2],
# [0, 2, 0], [0, 2, 1], [0, 2, 2],
# [0, 3, 0], [0, 3, 1], [0, 3, 2],
# [1, 0, 0], [1, 0, 1], [1, 0, 2],
# [1, 1, 0], [1, 1, 1], [1, 1, 2],
# [1, 2, 0], [1, 2, 1], [1, 2, 2],
# [1, 3, 0], [1, 3, 1], [1, 3, 2]]
If first, *rest = [2,4,3], then first #=> 2 and rest #=> [4,3].
See Enumerable#flat_map and Array#map. a ? b : c is called a ternery expression.
If e #=> 1 and arr #=> [2,1] then [e, *arr] #=> [1,2,1].
I will go to great lengths to avoid the use of Array#flatten. It's irrational, but to me it's an ugly method. That's usually possible using flat_map and/or the splat operator *.
Here's a mix of the 2 existing answers. It might be a bit more concise and readable:
head, *rest = inp.map{ |n| n.times.to_a }
head.product(*rest)
As an example:
inp = [2, 4, 3]
# => [2, 4, 3]
head, *rest = inp.map{ |n| n.times.to_a }
# => [[0, 1], [0, 1, 2, 3], [0, 1, 2]]
head.product(*rest)
# => [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 2, 0], [0, 2, 1], [0, 2, 2], [0, 3, 0], [0, 3, 1], [0, 3, 2], [1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 2, 0], [1, 2, 1], [1, 2, 2], [1, 3, 0], [1, 3, 1], [1, 3, 2]]
i'm attempting to understand why the following code behaves (to my knowledge) oddly.
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3 # Should be one value changed, right?
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
What i thought should happen is that one value of the array in the hash a at key 1 at index 2 should change to 3, but instead all the first values of the entire array change to 3. What's going on here, what am I missing? Here's my Ruby version.
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
EDIT:
I also tried the following
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[6] pry(main)> a = {}
=> {}
[7] pry(main)> a[1] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[8] pry(main)> a[2] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[9] pry(main)> a
=> {1=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[10] pry(main)> a[1][2][0] = 3
=> 3
[11] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
Surely the values should be copies?
All the elements in a[1] refer to the same array.
[0,0] isn't deep-copied when you do [[0,0]] * 7.
Solution: a[1] = Array.new(7) { [0,0] } (thanks #Stefan!)
Here's how to diagnose this problem in the future:
object_id will tell you the "slot" where Ruby keeps an object. Objects should have unique IDs:
foo = []
bar = []
foo.object_id # => 70322472940660
bar.object_id # => 70322472940640
If they don't, though they might be different variable names, they're still pointing to the same object. That might be desirable in more advanced uses, but generally it's not something you want because changing one will change the other leading to confusion, gnashing teeth and possibly the return of "he who should not be named". For instance, we get fooled when we look at:
[[]] * 2 # => [[], []]
because it seems that we're getting back two arrays, which could be assigned using parallel assignment, like:
foo, bar = [[]] * 2
But, when we look at foo and bar to see where they're stored we can see the problem, they're both pointing to the same slot, and so changing one changes the other:
foo.object_id # => 70323794190700
bar.object_id # => 70323794190700
foo << 1
bar # => [1]
This is an ages old problem when writing code; You gotta learn about "pass by value" vs. "pass by reference" and when a language is doing one or the other otherwise this problem will occur. And then you have to learn how to tell the language to avoid the problem.
To create a one-dimensional array, I can write:
arr = Array.new(10) { |z| 0 }
#=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
But I want to know how to initialize an array in more dimensions, e.g.:
#=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
This is just an example. I am trying to understand the general case.
You can easily nest multiple Array.new calls:
Array.new(2, 0)
#=> [0, 0]
Array.new(3) { Array.new(2, 0) }
#=> [[0, 0], [0, 0], [0, 0]]
Array.new(4) { Array.new(3) { Array.new(2, 0) } }
#=> [[[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]]]
Note that the non-block version, i.e. Array.new(2, 0), should only be used for immutable objects, because the same object will be used for all elements. See the Common gotchas section for details.
To build an array of arbitrary dimensions, you could use recursion:
def multi_array(sizes, default = nil)
size, *remaining = sizes
if remaining.empty?
Array.new(size, default)
else
Array.new(size) { multi_array(remaining, default) }
end
end
multi_array([4, 3, 2], 0)
#=> [[[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]]]
You can also do it like:
Array.new(10, 0).each_slice(2).to_a
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
5.times.map{Array.new(2, 0)}
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
If by more dimensions you mean an array of array, you could try:
arr = Array.new(10){|z| z=Array.new(2,0)}
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0,0]]
assuming that your second dimension is 2.
I am trying to understand the general case.
Array.new(n) # where n is natural number 1..n
#=>[nil, nil, nil]
This will create a 1-D Array i:e One dimension array.
Let extends further for a multi-dimensional array i:e Two dimension array .
In ruby, every method accepts a block.
Array.new(3) do
Array.new(3)
end
end
Same can be written In online (macro style) Array.new(3) { Array.new(3) }
[
[nil, nil, nil],
[nil, nil, nil],
[nil, nil, nil]
]
Further, it can extend to initialize each cell value.
Array.new(3) do
Array.new(3) do
0
end
end
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
One line micro style Array.new(3) { Array.new(3) { 0 } }
Let extends further three-dimension array.
Array.new(2) do
Array.new(3) do
Array.new(4) do
0
end
end
end
OR
The same can be written as micro-style syntax
Array.new(2) { Array.new(3) { Array.new(4) } {0} }
[
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
]
Here's another way, though I feel the deep copy at every level could somehow be simplified:
def make_multi(dimensions, default)
dimensions.reverse.each_with_index.reduce([]) { |a,(d,i)|
Array.new(d) { i.zero? ? default : Marshal.load(Marshal.dump(a)) } }
end
arr = make_multi [4,3,2,2], 0
#=> [[[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]]]
arr[0][0][0][0] = 1
arr
#=> [[[[1, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]]]
arr = make_multi [4,3,2,2], {}
#=> [[[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]]]
arr[0][0][0][0] = { a: 1 }
arr
#=> [[[[{:a=>1}, {}...] # all but the first are empty hashes
Try it:)
arr = Array.new(10, 0)
UPD: for multidimensional is:
arr = Array.new(10, Array.new(2, 0))
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
Supposing we have an array with this shape: [2, 5]. The possible index combinations are:
[
[0, 0],
[0, 1],
[0, 2],
[0, 3],
[0, 4],
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[1, 4]
]
If the array has n dimension(s), is there a simple way to generate the indices in Ruby?
This should work:
def coordinates(first, *others)
(0...first).to_a.product(*others.map { |to| (0...to).to_a })
end
coordinates(2, 5)
#=> [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
# [1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]
coordinates(4, 3, 3)
#=> [[0, 0, 0], [0, 0, 1], [0, 0, 2],
# [0, 1, 0], [0, 1, 1], [0, 1, 2],
# [0, 2, 0], [0, 2, 1], [0, 2, 2],
# [1, 0, 0], [1, 0, 1], [1, 0, 2],
# [1, 1, 0], [1, 1, 1], [1, 1, 2],
# [1, 2, 0], [1, 2, 1], [1, 2, 2],
# [2, 0, 0], [2, 0, 1], [2, 0, 2],
# [2, 1, 0], [2, 1, 1], [2, 1, 2],
# [2, 2, 0], [2, 2, 1], [2, 2, 2],
# [3, 0, 0], [3, 0, 1], [3, 0, 2],
# [3, 1, 0], [3, 1, 1], [3, 1, 2],
# [3, 2, 0], [3, 2, 1], [3, 2, 2]]
Here you go. It may not be pretty, but it seems to work.
def gen_indices(dimensions, solutions = [], current=[], level = 0)
if level < dimensions.length
dimensions[level].times do |i|
current << i
if level == dimensions.length - 1
solutions << current.clone
else
gen_indices(dimensions, solutions, current, level + 1)
end
current.pop
end
end
solutions
end
p gen_indices([4,3,2])
Not sure this is totally correct, but it should help you on the right track:
def combinations(dimensions)
dimensions.inject([]){|total, dimension|
dim = (0...dimension).to_a
total.empty? ? dim : total.product(dim)
}.map(&:flatten)
end
p combinations([2, 5]) #=> [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]
p combinations([2, 2, 2]) #=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Try:
a = [[0,1],[0,1,2,3,4]]
res = a[0].product(a[1])