Having Difficulty understanding this nested loop problem:
You have 10 pebbles (numbered 1-10). They are by default black. You must alter them by painting them white if they are black or painting them black if they are white. There are 10 rounds. Every round, you must alter the pebbles that are multiples of the current round.
The pebbles are by default black.
- 1st round, you alter every pebble (paint them all white).
- 2nd round, you alter every other pebble(you paint pebbles
#2,4,6,8,10 black).
- 3rd round, you alter pebbles #3,6,9.
- 4th round you alter pebbles #4,8.
- ...
- ...
- 10th round, you alter pebble #10.
After the 10th round, which pebbles are painted black and which are painted white?
My solution which doesn't run is below (I attempt to do so by making an array of numbers(turned to strings) and adding "w" if painted white and deleting "w" if painted black.
(I have tried editing it to make it run, however I am new to nested loops and I am just not grasping this concept). I would greatly appreciate it if someone could explain to me what I am doing wrong and give a better solution.
pebbles = (1..10).map {|element| element.to_s}
pebble_colors = (1..10).map {|element| element.to_s}
(1..10).each do |round|
pebbles_to_paint = []
pebbles.each_with_index {|element, index| pebbles_to_paint << index if element % round == 0}
pebbles_to_paint.each do |pebble_number|
if pebble_color[pebble_number].include?("w")
pebble_color[pebble_number].delete!("w")
else
pebble_color[pebble_number] << "w"
end
end
end
Your main problem appears to be in the deciding of which pebbles to paint. The following is not right:
element % round == 0
It should be:
(index+1) % round
You want to compare the pebble's index rather than the current value of the pebble. As well, you need to remember that indexes are 0-based (ie they start counting from 0). You need to have the indexes be 1-based (hence the adding of 1) otherwise the first element would always change and the others would be off by 1.
There was also a typo for pebble_color
, which should be pebble_colors
.
You could definitely re-factor the code to make it shorter, but the following appears to work (just making the minimal changes mentioned above):
pebbles = (1..10).map {|element| element.to_s}
pebble_colors = (1..10).map {|element| element.to_s}
(1..10).each do |round|
pebbles_to_paint = []
pebbles.each_with_index {|element, index| pebbles_to_paint << index if (index+1) % round == 0}
pebbles_to_paint.each do |pebble_number|
if pebble_colors[pebble_number].include?("w")
pebble_colors[pebble_number].delete!("w")
else
pebble_colors[pebble_number] << "w"
end
end
p pebble_colors
end
The output is:
["1w", "2w", "3w", "4w", "5w", "6w", "7w", "8w", "9w", "10w"]
["1w", "2", "3w", "4", "5w", "6", "7w", "8", "9w", "10"]
["1w", "2", "3", "4", "5w", "6w", "7w", "8", "9", "10"]
["1w", "2", "3", "4w", "5w", "6w", "7w", "8w", "9", "10"]
["1w", "2", "3", "4w", "5", "6w", "7w", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7w", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10"]
Because this is about nested loops, I just wanted to add that you don't necessarily have to iterate through all pebbles on each round. (that's 100 iterations for 10 pebbles!)
Instead you can use Range#step
to iterate over each nth element, starting with the round's index:
(1..10).each { |r|
print "%2d:" % r
(r..10).step(r) { |i|
print " #{i}"
}
puts
}
produces:
1: 1 2 3 4 5 6 7 8 9 10
2: 2 4 6 8 10
3: 3 6 9
4: 4 8
5: 5 10
6: 6
7: 7
8: 8
9: 9
10: 10
That's only 27 iterations. A nice side effect is that you don't have to calculate the remainder any more.
Full example:
pebbles = Hash[(1..10).map{|i| [i, :black]}]
toggle = {:black => :white, :white => :black}
(1..10).each { |r|
(r..10).step(r) { |i|
pebbles[i] = toggle[pebbles[i]]
}
}
p pebbles
#=> {1=>:white, 2=>:black, 3=>:black, 4=>:white, 5=>:black, 6=>:black, 7=>:black, 8=>:black, 9=>:white, 10=>:black}
There are a couple of problems in your code.
Fixing these two problems, the program leaves pebble_colors
with the value
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10"]
although I think that isn't quite what you had in mind!
You have three arrays where one will do. All you need is an array of ten colours, starting all black. I would code it like this
pebbles = Array.new(10, :black)
(1..10).each do |round|
pebbles.each_with_index do |pebble, i|
if (i + 1).remainder(round) == 0
pebbles[i] = pebble == :black ? :white : :black
end
end
end
p pebbles
output
[:white, :black, :black, :white, :black, :black, :black, :black, :white, :black]
For make the problem simple, I think it is better calculate all round multiples before update the pebble.
pebbles = Array.new(10)
10.times do |i|
# calculate all multiples for each index
multiples = Array(1..10).select {|j| j % (i + 1) == 0}
# observer that we must have to sub 1 since Ruby use 0 indexed arrays
multiples.map! {|j| j - 1}
# update each pebble
multiples.each do |j|
pebbles[j] = pebbles[j] == 'b' ? 'w' : 'b'
end
end
puts "Black pebbles: #{pebbles.select {|p| p == 'b'}.size}"