Find count of sub arrays of zeros in an array - ruby

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;

Related

Ruby: recursive method

def reverse_append(arr, n)
return arr if n < 0
reverse_append(arr, n-1)
arr << n
arr
end
reverse_append([],4) #=> [0, 1, 2, 3, 4]
I can't seem to understand this recursive method. It produces an array from 0 up to n.
Can someone explain this to me?
The method reverse_append([],4) is called
Since 4 >= 0, the return statement does not get called.
The method reverse_append([],3) is called.
Since 3 >= 0, the return statement does not get called.
The method reverse_append([],2) is called.
Since 2 >= 0, the return statement does not get called.
The method reverse_append([],1) is called.
Since 1 >= 0, the return statement does not get called.
The method reverse_append([],0) is called.
Since 0 >= 0, the return statement does not get called.
The method reverse_append([],-1) is called.
Since -1 < 0, the array ([]) is returned.
We pop up one level in our call stack, to where n = 0 and arr = [].
arr << n and arr is returned, so now arr = [0].
We pop up one level in our call stack, to where n = 1 and arr = [0].
arr << n and arr is returned, so now arr = [0, 1].
We pop up one level in our call stack, to where n = 2 and arr = [0, 1].
arr << n and arr is returned, so now arr = [0, 1, 2].
We pop up one level in our call stack, to where n = 3 and arr = [0, 1, 2].
arr << n and arr is returned, so now arr = [0, 1, 2, 3].
We pop up one level in our call stack, to where n = 4 and arr = [0, 1, 2, 3].
arr << n and arr is returned, so now arr = [0, 1, 2, 3, 4].
Finally, the "top-level" method returns, and we have our final result.
Well step through the code with the supplied parameters. The first step is to check if n < 0 which its not. If it isn't 0 reverse append with [], 3 and appends the that array the number and then returns the array.
So it takes the array, adds 4 to it after it has gone through the step of dealing with [], 3, [], 2, [],1 and [], 0. So the first call that will succeed is just returning the array when it gets below 0, next is 0 gets appended, then one, then 2, then 3 and lastly the original call with 4 gets added arr << n.
There's a nice tool you can add to many editors called "Seeing Is Believing", which lets you see what is happening as code runs:
def reverse_append(arr, n)
return arr if n < 0 # => false, false, false, false, true
reverse_append(arr, n-1) # => [], [0], [0, 1], [0, 1, 2]
arr << n # => [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]
arr # => [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]
end
reverse_append([], 3) # => [0, 1, 2, 3]
However, with a name like "reverse_append" it seems like you should see a result that is descending in values:
def reverse_append(arr, n)
return arr if n < 0 # => false, false, false, false, true
reverse_append(arr, n-1) # => [], [0], [1, 0], [2, 1, 0]
arr.unshift n # => [0], [1, 0], [2, 1, 0], [3, 2, 1, 0]
arr # => [0], [1, 0], [2, 1, 0], [3, 2, 1, 0]
end
reverse_append([], 3) # => [3, 2, 1, 0]
In either case, there are a lot of easier ways to generate such an array without relying on recursion:
[*0..3] # => [0, 1, 2, 3]
(0..3).to_a # => [0, 1, 2, 3]
[*0..3].reverse # => [3, 2, 1, 0]
(0..3).to_a.reverse # => [3, 2, 1, 0]

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).

Generating combinations from an array which == a specified amount?

I need to get all the possible number combinations from denom_arr which equal the amt.
denom_arr = [4,3,1]
amt = 10
This case would produce:
[4, 4, 1, 1]
[3, 3, 3, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[4, 3, 1, 1, 1]
[4, 3, 3]
. . . (other cases...)
Problem is the code I wrote is breaking after 1-3 and I'm not sure how to make it loop over the same index to get case 4-6+
set, sets = [], []
i = 0
loop do
i = 0 if denom_arr[i].nil?
loop do
set << denom_arr[i]
break if set.inject(:+) > amt
end
set.pop if set.inject(:+) > amt
if set.inject(:+) == amt
sets << set
set = []
denom_arr.shift
end
i += 1
sets
break if denom_arr.empty?
end
UPDATE
I know this can be done with recursion with memoization/dynamic programming techniques, but I am trying to do this strictly in a loop for the sake of testing a theory.
I would do this recursively
def possible_sums(arr, amt)
return [[]] if amt == 0
return [] if amt < 0
arr.reduce([]) do |sums, e|
sums.concat(
possible_sums(arr, amt-e)
.map { |sum| sum.unshift(e).sort }
)
end.uniq
end
p possible_sums([4,3,1], 10)
# => [
# [1, 1, 4, 4], [3, 3, 4], [1, 1, 1, 3, 4], [1, 1, 1, 1, 1, 1, 4],
# [1, 3, 3, 3], [1, 1, 1, 1, 3, 3], [1, 1, 1, 1, 1, 1, 1, 3],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# ]
Although this is potentially inefficient in that it repeats work, this can be alleviated by using dynamic programming (essentially, memoizing the results of the recursive function).
UPDATE Here is an iterative solution:
def possible_sums_it(arr, amt)
sums = Array.new(amt+1) { [] }
sums[0] << []
(1..amt).each do |i|
arr.each do |e|
if i-e >= 0
sums[i].concat(
sums[i-e].map { |s| [e, *s].sort }
)
end
end
sums[i].uniq!
end
sums[amt]
end
This is in fact the dynamic programming algorithm for the problem.
So if you squint at it just right, you'll see that essentially what it is doing, is calculating all the possible sums for 0 up to amt into the sums array, using what is basically the recursive algorithm, but instead of the recursive call, we lookup a value in sums that we have calculated beforehand.
This works because we know that we won't need sums[i] before sums[j] for j < i.

Ruby - Graph adjacency matrix into variable

I am trying to edit an algorithm found here.
I want the adjacency matrix to be loaded from file (formatting of the file doesn't matter to me, it can be either like this [0,1,1,0] or just 0110) with G = file.read().split("\n")
However, I get an error no implicit conversion of Fixnum into String (TypeError)
And I already know I need to convert this string to ints, but how to do it properly to not lose the formatting required by this DFS method?
I guess it's pretty easy, but I'm a begginer in Ruby (and graphs :v) and can't get it to work...
Edit:
So the code I'm using to read from file to an array of arrays is:
def read_array(file_path)
File.foreach(file_path).with_object([]) do |line, result|
result << line.split.map(&:to_i)
end
end
And the result I get from a file (for example)
01101010
01010101
01010110
10101011
01011111
is this:
=> [[[1101010], [1010101], [1010110], [10101011], [1011111]]]
What I need, however, is:
=> [[[1,1,0,1,0,1,0], [1,0,1,0,1,0,1], [1,0,1,0,1,1,0], [1,0,1,0,1,0,1,1], [1,0,1,1,1,1,1]]]
So that it would work with the algorithm mentioned in the first line of my post (I'll copy it here, if it takes too much place I can delete it and leave link only):
G = [0,1,1,0,0,1,1], # A
[1,0,0,0,0,0,0],
[1,0,0,0,0,0,0],
[0,0,0,0,1,1,0],
[0,0,0,1,0,1,1],
[1,0,0,1,1,0,0],
[1,0,0,0,1,0,0] # G
LABLES = %w(A B C D E F G)
def dfs(vertex)
print "#{LABLES[vertex]} " # visited
edge = 0
while edge < G.size
G[vertex][edge] = 0
edge += 1
end
edge = 0
while edge < G.size
if ( G[edge][vertex] != 0 && edge != vertex)
dfs(edge)
end
edge += 1
end
end
dfs(0)
split's default separator is a whitespace. To make it split every char you need to explicitly say it:
'01101101'.split.map(&:to_i)
# => [ 1101101 ]
'01101101'.split('').map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
you can also use chars to do the same job:
'01101101'.chars.map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
I don't know how your read_array is used, but it can be simplified to:
def read_array(file_path)
File.foreach(file_path).map do |line|
line.chomp.chars.map(&:to_i)
end
end
read_array('my_file.txt')
# => [[1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]
If you still get the extra [, you can either take only the first item:
my_array[0]
Or (if there is more than one item the uber-array) - use flat_map:
uber_array = [[[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1]],
[[1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]]
uber_array.flat_map { |a| a }
# => [[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]

Build efficient array integer incrementer with different caps per number

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
#...

Resources