Build efficient array integer incrementer with different caps per number - ruby

I want to program a counter which is represented by an array of numbers, starting with:
[0, 0, 0]
The constraint here is, that each position has a different cap, so it's not necessarily 9 or something else, but it is given. For instance:
[4, 2, 1]
Which would lead to the following incrementation sequence:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[0, 2, 0]
[0, 2, 1]
[1, 0, 0]
.
.
.
Of course I can think of a solution using modulo and adding each carryover onto the next position. But has someone an idea how to implement this efficiently, respectively with nice Ruby syntax without cluttering it too much?
That is my naive implementation:
max = [10, 1, 1, 1, 10]
counter = [0, 0, 0, 0, 0]
i = counter.length-1
while counter != max do
counter[i] = counter[i] + 1
while counter[i] > max[i]
counter[i] = 0
i = i - 1
counter[i] = counter[i] + 1
end
i = counter.length-1
end

I'm not sure about efficiency but here's my shot at it:
start = [0, 0, 0]
cap = [4, 2, 1]
start.zip(cap).map{ |i, c| (i..c).to_a }.reduce(&:product).map &:flatten
Produces something like:
[[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[0, 2, 0],
[0, 2, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
[1, 2, 0],
[1, 2, 1],
[2, 0, 0],
[2, 0, 1]...]

Edit: I was writing this before you made your edit. It seemed like you wanted a counter object, not just to output a list.
1) I would recommend specifying not the limits but (limit+1) of each of the digits. For example, for a [second, minute, hour, day, year] counter it makes more sense (to me) to write [60, 60, 24, 365] instead of [59,59,23,364].
2) You'll have to figure out what to do if your counter overflows the last limit of your array. I added an extra position that counts to infinity.
3) I would also recommend reversing the order of the array, at least in the internal representation to avoid inverting subscripts. If you don't want it like that, you can .reverse the bases in initialize and #digits in to_s
class MyCounter
def initialize bases
#bases = bases
#bases << 1.0/0 # Infinity
#digits = Array.new(bases.size, 0)
prod = 1
#digit_values = [1] + #bases[0..-2].map { |b| prod *= b }
end
attr_reader :digit_values
def to_s
#digits
end
def increment(digit=0)
v = #digits[digit] + 1
if v < #bases[digit]
#digits[digit] = v
else
#digits[digit] = 0
increment(digit+1)
end
self
end
def +(integer)
(#digits.size - 1).step(0,-1).each do |i|
#digits[i] += integer / #digit_values[i]
integer = integer % #digit_values[i]
end
self
end
end
c1 = MyCounter.new [2,3,5]
20.times { c1.increment; p c1 }
c2 = MyCounter.new [2,3,5]
c2 += 20
p c2

Create an array for each cap, with values from 0 upto cap. Take the first array and calculate the Cartesian product with the rest of the arrays.
caps = [4, 2, 1]
arrs = caps.map{|cap| (0..cap).to_a} #=>[[0, 1, 2, 3, 4], [0, 1, 2], [0, 1]]
p arrs.shift.product(*arrs)
# =>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [0, 2, 0], [0, 2, 1], ...
If you don't want a memory-consuming array with the results, then provide a block. product will yield each element to it, one by one.
arrs = caps.map{|cap| (0..cap).to_a}
arrs.shift.product(*arrs){|el| puts el.join} #no resulting array
#000
#001
#010
#011
#...

Related

Find count of sub arrays of zeros in an array

I need to find the count of sub arrays of zeros in an array:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
Result should be: 3 because we have 0, 0, 0 and 0, 0, 0.
Counting the number of zeros (6) will not work.
array.join.squeeze('0').count('0')
#=> 3
We have
s = array.join
#=> "11100111101110001"
t = s.squeeze('0')
#=> "11101111011101"
t.count('0')
#=> 3
Note one could squeeze all the characters, not just the zeroes (squeeze as opposed to squeeze('0')).
Another way:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0]
array.select.with_index(1) { |n,i| n.zero? && array[i] != 0 }.size
#=> 3
When, as here, the last element of array is a zero, array[i] #=> nil when i = array.size (since i goes from 1 to array.size).
chunk_while and count might work:
array
.chunk_while(&:==) # [[1, 1, 1], [0, 0], [1, 1, 1, 1], [0], [1, 1, 1], [0, 0, 0], [1]]
.count { |arr| arr.include?(0) } # 3
Or join, scan and length:
arr
.join # "11100111101110001"
.scan(/(0+)/) # [["00"], ["0"], ["000"]]
.length # 3
You can use Enumerable#chunk and Enumerable#count to handle this as well like so:
Option 1:
arr = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
arr.chunk(&:zero?).count(&:first)
#=> 3
chunk will group consecutive elements together by their return value in the form of [return_value, [*elements]] so in this case chunk(&:zero?) will create groups like so
arr.chunk(&:zero?).to_a
#=> [[false, [1, 1, 1]],
# [true, [0, 0]],
# [false, [1, 1, 1, 1]],
# [true, [0]],
# [false, [1, 1, 1]],
# [true, [0, 0, 0]],
# [false, [1]]]
However chunk does not create a new Array (thus the to_a above to show the structure) instead it creates an Enumerator that stores this block of code and evaluates the return value upon subsequent method calls.
In this case we are then calling count(&:first) since count will only count the elements for which the block returns a truthy value (not nil or false).
Option 2:
arr.chunk {|e| e.zero? || nil}.count
#=> 3
Very similar to Option 1 this works because chunk will drop all elements where the return value is nil meaning
arr.chunk {|e| e.zero? || nil}.to_a
#=> [[true, [0, 0]], [true, [0]], [true, [0, 0, 0]]]
Bonus: (just for fun in case you need to count other consecutive elements)
groups = arr.chunk(&:itself)
.each_with_object(Hash.new {|h,k| h[k] =[]}) do |(e,arr),obj|
obj[e] << arr
end
#=> {1=>[[1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1]],
# 0=>[[0, 0], [0], [0, 0, 0]]}
groups[0].size
#=> 3
Yet another way...
array.each_with_object([]){ |a, ary| ary << a unless ary.last == a }.count(&:zero?)
I am providing this solution to understand the basic solution's idea of this problem.
This is c++ solution for counting number of 0 sub-array. Increment result when we get a 0. And when we get more than one zero then don't count for them.
int n = arr.size();
int cnt = 0;
for(int i = 0; i < n; i++) {
if(i > 0 && arr[i] == arr[i-1]) {
continue;
}
if(arr[i] == 0) {
cnt++;
}
}
cout << cnt << endl;

Why does my diagonal matrix calculator not return the total?

My method should take an array of subarrays, find the sum of the first value of the first array, the second value of the second array, the third value of the third array, and so on. Some examples of inputs and expected results are as follows:
exampleArray = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 4
exampleArray = [
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 5
I wrote this:
def diagonalSum(matrix)
total = 0
counter = 0
while matrix.length <= counter + 1 do
total += matrix[counter][counter]
counter += 1
end
total
end
and it returns 0.
It's easiest to convert the array to a matrix and apply Matrix#trace.
require 'matrix'
arr = [[1, 0, 0, 7],
[0, 2, 0, 0],
[0, 0, 3, 0],
[8, 0, 0, 4]]
Matrix[*arr].trace
#=> 10
According to the code you provide, in which the input is an array of arrays, the first advice I could give you is that in Ruby you must avoid using for/while loops and make use of iterators such as each/each_with_index instead (based on this Ruby style guide and the suggestions of #tadman and #Yu Hao).
The each with index iterator takes a Ruby block with the current array of the iteration along with its index position, so you don't need to define your own index variable and update it in every iteration.
Applying this to your code will result in the following:
def diagonal_sum(matrix)
total = 0
matrix.each_with_index do |row, index|
total+=row[index]
end
total
end
Also note that the convention in Ruby is to write variable and method names in snake_case (according to the previous style guide).

Recursion involving an Array for Wonky Coins

Here's the prompt I've been given:
Catsylvanian money is a strange thing: they have a coin for every
denomination (including zero!). A wonky change machine in
Catsylvania takes any coin of value N and returns 3 new coins,
valued at N/2, N/3 and N/4 (rounding down).
Write a method wonky_coins(n) that returns the number of coins you
are left with if you take all non-zero coins and keep feeding them
back into the machine until you are left with only zero-value coins.
Difficulty: 3/5
describe "#wonky_coins" do
it "handles a simple case" do
wonky_coins(1).should == 3
end
it "handles a larger case" do
wonky_coins(5).should == 11
# 11
# => [2, 1, 1]
# => [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
# => [[[0, 0, 0], 0, 0], [0, 0, 0], [0, 0, 0]]
end
it "handles being given the zero coin" do
wonky_coins(0).should == 1
end
end
Maybe it's because of the tests given that involve arrays, but I couldn't get my mind off of them! So my solution so far is as follows:
def wonky_coins(n)
arr = []
arr << n/2 << n/3 << n/4
#base case?
if arr.all?{|coin| coin == 0}
return arr.flatten.length
else
arr.map{|x| wonky_coins(x)}
end
end
p wonky_coins(5)
Except I get [[3,3,3],3,3] as an output if I map it. It's not actually recurring, but even before that, it's giving a strange output that I can't for the life of me understand why the output is this way!
I know it's because I'm using the map method, is it because I'm mutating it while iterating it through wonky_coins again that I'm getting this strange output I can't explain?
I've since looked at the solution and realized that arrays made it needlessly complicated, but I'm still wondering what's going on here??
Here's what Seeing is Believing shows as the code runs:
def wonky_coins(n)
arr = [] # => [], [], [], [], [], [], []
arr << n/2 << n/3 << n/4 # => [2, 1, 1], [1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]
#base case?
if arr.all?{|coin| coin == 0} # => false, false, true, true, true, true, true
return arr.flatten.length # => 3, 3, 3, 3, 3
else
arr.map{|x| wonky_coins(x)} # => [3, 3, 3], [[3, 3, 3], 3, 3]
end
end
p wonky_coins(5) # => [[3, 3, 3], 3, 3]
# >> [[3, 3, 3], 3, 3]
Seeing is Believing is a great tool and can help dig out weirdness in code.

Ruby: Permutation on an Array

Say, I have an array:
a = [1,2]
and
n = 3
I want output like this:
[[1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 2, 2], [2, 1, 1], [2, 1, 2], [2, 2, 1], [2, 2, 2]]
This are all possible combinations of length n of elements from array a.
Most importantly I'm using ruby 1.8.7
a.repeated_combination(n).to_a
Please test in detail before use:
x = [1,0]
n = 3
def perm(a, n)
l = a.length
(l**n).times do |i|
entry = []
o = i
n.times do
v = o % l
entry << a[v]
o /= l
end
yield(i, entry)
end
end
perm(x, n) do |i, entry|
puts "#{i} #{entry.reverse.inspect}"
end
prints
0 [0, 0, 0]
1 [0, 0, 1]
2 [0, 1, 0]
3 [0, 1, 1]
4 [1, 0, 0]
5 [1, 0, 1]
6 [1, 1, 0]
7 [1, 1, 1]

Binary Sequence Combination Generator, in Ruby

Code works, but feels very brute force, suggestions?
Goal of the code is to supply an array length, and then as fast as possible generate all possible unique binary combinations with that array length.
CODE:
class Array
def sequence(i = 0, *a)
return [a] if i == size
self[i].map {|x|
sequence(i+1, *(a + [x]))
}.inject([]) {|m, x| m + x}
end
end
[(0..1),(0..1),(0..1)].sequence
OUTPUTS:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
permutation and repeated_permutation are built in, so you can do:
def sequence(n)
[0, 1].repeated_permutation(n).to_a
end
p sequence(3) #=>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
"All unique binary combinations" with n bits is nothing but (0...2**n), so the only task is to efficiently convert from an integer to its binary representation, and the following is a solution that does not rely on string generation/manipulation:
def sequence(n)
ret = []
(2**n).times do |number|
ret << []
(n - 1).downto(0) do |bit|
ret.last << number[bit]
end
end
ret
end
sequence(3)
# => [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Or, if you prefer a version more oriented on list operations, this is pretty much the same:
def sequence(n)
(0...2**n).map {|number|
(1..n).map {|bit|
number[n-bit]
}
}
end

Resources