This is the original link for the problem in hackerrank: https://www.hackerrank.com/challenges/the-birthday-bar/problem
I have been fighting with this problem in Ruby and I don't know why my counter always returns 1. This is the solution. I hope you can help me to understand what I'm making wrong.
s = [1, 2, 1, 3, 2]
d = 3
m = 2
def birthday(s, d, m)
array = []
cont = 0
sum = 0
m.times {array.push(s.shift)}
(m-1).times do
array.each {|i| sum = sum + i}
if sum == d
cont += 1
end
array.shift
array.push(s.shift)
end
return cont
end
birthday(s, d, m)
Though the following does not answer your question directly, it is a Ruby-like way of solving the problem, especially by making use of the methods Enumerable#each_cons and Enumerable#count.
def birthday(s, d, m)
s.each_cons(m).count { |a| a.sum == d }
end
s = [1, 2, 1, 3, 2]
d = 3
m = 2
birthday(s, d, m)
#=> 2 ([1, 2] and [2, ])
s = [2, 2, 1, 3, 2]
d = 4
m = 2
birthday(s, d, m)
#=> 2 ([2, 2] and [1, 3])
s = [2, 4, 3, 2, 1, 2, 6, 1]
d = 9
m = 3
birthday(s, d, m)
#=> 4 ([2, 4, 3], [4, 3, 2], [1, 2, 6] and [2, 6, 1])
Notice from the doc that when each_cons is used without a block it returns an enumerator:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
enum = s.each_cons(m)
#=> #<Enumerator: [1, 2, 1, 3, 2]:each_cons(2)>
enum will generate elements and pass them to count until there are no more to generate, at which time it raises a StopIteration exception:
enum.next #=> [1, 2]
enum.next #=> [2, 1]
enum.next #=> [1, 3]
enum.next #=> [3, 2]
enum.next #=> StopIteration (iteration reached an end) <exception>
We can write1:
enum.count { |a| a.sum == d }
#=> 2
After enum generates the first value ([1, 2]) the block variable a is assigned its value:
a = enum.next
#=> [1, 2]
and the block calculation is performed. As
a.sum == d
#=> [1, 2].sum == 3 => true
the count is incremented (from zero) by one. enum then passes each of its remaining values to count and the process is repeated. When, for example, [1, 3].sum == 3 => false is executed, the count is not incremented.
1. Note that since I just stepped through all the elements of enum, enum.next would generate another StopIteration exception. To execute enum.count { |a| a.sum == d } I therefore must first redefine the enumerator (enum = s.each_cons(m)) or Enumerator#rewind it: enum.rewind.
Related
I wanted to practise some algorithms... Why doesn't my solution work on leetcode website?!?!
PS: Would be grateful for other resources to learn algorithms and practise interview questions.
# #param {Integer[]} nums
# #param {Integer} target
# #return {Integer[]}
def two_sum(nums, target)
i,j = 0,nums.length-1
output = []
while i < nums.length-1
while j > i
if nums[i] + nums[j] == target
output << i << j
end
j-=1
end
i+=1
end
output
end
Result from the website:
Input:
[3,2,4]
6
Output: []
Expected:[1,2]
Now that your question has been answered, I would like to suggest a more Ruby-like method.
Code
def two_sum(nums, target)
(0...nums.size).to_a.combination(2).find { |i,j| nums[i]+nums[j] == target }
end
Example
nums = [1,5,2,3,4]
target = 8
two_sum(nums, target)
#=> [1,3]
Explanation
For the example above, the steps are as follows:
a = nums.size
#=> 5
b = a.times
#=> #<Enumerator: 5:times>
c = b.to_a
#=> [0, 1, 2, 3, 4]
d = c.combination(2)
#=> #<Enumerator: [0, 1, 2, 3, 4]:combination(2)>
We can see the elements that are generated by the enumerator d by converting it to an array.
d.to_a
#=> [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
d.find { |i,j| nums[i]+nums[j] == target }
#=> [1, 3]
Note that (0...nums.size).to_a could be replaced by any of the following.
[*0...nums.size]
nums.each_index.to_a
nums.size.times.to_a
0.upto(nums.size-1).to_a
Array.new(nums.size) { |i| i }
Array.new(nums.size, &:itself)
Your error is that you don't reinitialize j when it reaches i which means that your algorithm just tries (0,n-1), (0,n-2), …, (0, 2), (0, 1) and then stops.
Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
You have an array. If any two numbers add to zero in the array, return true. It doesn't matter how many pairs there are—as long as there is one pair that adds to zero, return true. If there is a zero, it can only return true if there is more than one.
I wrote two functions, one to check for each, and a final one to combine both, and return false if either aren't met.
def checkZero(array)
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
def checkNegative(array)
for j in 0..array.count
neg = -array[j] #set a negative value of the current value
if array.include?(neg) #check to see whether the negative exists in the array
return true
else
return false
end
end
end
def checkArray(array)
if checkZero(array) == true or checkNegative(array) == true
return true
else
return false
end
end
Then run something like
array = [1,2,3,4,0,1,-1]
checkArray(array)
So far, Ruby isn't returning anything. I just get a blank. I have a feeling my return isn't right.
The problem may be that you didn't output the result.
array = [1,2,3,4,0,1,-1]
puts checkArray(array)
The checkArray method can be written like the following, if performance (O(n^2)) is not a great concern:
def check_array(array)
array.combination(2).any?{|p| p.reduce(:+) == 0}
end
The more efficient (O(n log n)) solution is:
def check_array(array)
array.sort! # `array = array.sort` if you need the original array unchanged
i, j = 0, array.size - 1
while i < j
sum = array[i] + array[j]
if sum > 0
j -= 1
elsif sum < 0
i += 1
else
return true
end
end
return false
end
Here's are a few relatively efficient ways to check if any two values sum to zero:
Solution #1
def checkit(a)
return true if a.count(&:zero?) > 1
b = a.uniq.map(&:abs)
b.uniq.size < b.size
end
Solution #2
def checkit(a)
return true if a.sort_by(&:abs).each_cons(2).find { |x,y| x == -y }
false
end
Solution #3
def checkit(a)
return true if a.count(&:zero?) > 1
pos, non_pos = a.group_by { |n| n > 0 }.values
(pos & non_pos.map { |n| -n }).any?
end
Solution #4
require 'set'
def checkit(a)
a.each_with_object(Set.new) do |n,s|
return true if s.include?(-n)
s << n
end
false
end
Examples
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0, 0]) #=> true
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2,-3, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2, 2,-5,-7, 0]) #=> false
Explanations
The following all refer to the array:
a = [1,3,4,2,2,-3,-5,-7,0]
#1
Zeroes present a bit of a problem, so lets first see if there are more than one, in which case we are finished. Since a.count(&:zero?) #=> 1, a.count(&:zero?) > 1 #=> false, so
return true if a.count(&:zero?) > 1
does not cause us to return. Next, we remove any duplicates:
a.uniq #=> [1, 3, 4, 2, -3, -5, -7, 0]
Then convert all the numbers to their absolute values:
b = a.uniq,map(&:abs) #=> [1, 3, 4, 2, 3, 5, 7, 0]
Lastly see if c contains any dups, meaning the original array contained at least two non-zero numbers with opposite signs:
c.uniq.size < c.size #=> true
#2
b = a.sort_by(&:abs)
#=> [0, 1, 2, 2, 3, -3, 4, -5, -7]
c = b.each_cons(2)
#=> #<Enumerator: [0, 1, 2, 2, 3, -3, 4, -5, -7]:each_cons(2)>
To see the contents of the enumerator:
c.to_a
#=> [[0, 1], [1, 2], [2, 2], [2, 3], [3, -3], [-3, 4], [4, -5], [-5, -7]]
c.find { |x,y| x == -y }
#=> [3, -3]
so true is returned.
#3
return true if a.count(&:zero?) > 1
#=> return true if 1 > 1
h = a.group_by { |n| n > 0 }
#=> {true=>[1, 3, 4, 2, 2], false=>[-3, -5, -7, 0]}
b = h.values
#=> [[1, 3, 4, 2, 2], [-3, -5, -7, 0]]
pos, non_pos = b
pos
#=> [1, 3, 4, 2, 2]
non_pos
#=> [-3, -5, -7, 0]
c = non_pos.map { |n| -n }
#=> [3, 5, 7, 0]
d = pos & c
#=> [3]
d.any?
#=> true
#4
require 'set'
enum = a.each_with_object(Set.new)
#=> #<Enumerator: [1, 3, 4, 2, 2, -3, -5, -7, 0]:each_with_object(#<Set: {}>)>
enum.to_a
#=> [[1, #<Set: {}>],
# [3, #<Set: {}>],
# ...
# [0, #<Set: {}>]]
Values are passed into the block, assigned to the block variables and the block is executed, as follows:
n, s = enum.next
#=> [1, #<Set: {}>]
s.include?(-n)
#=> #<Set: {}>.include?(-1)
#=> false
s << n
#=> #<Set: {1}>
n, s = enum.next
#=> [3, #<Set: {1}>]
s.include?(-3)
#=> false
s << n
#=> #<Set: {1, 3}>
...
n, s = enum.next
#=> [2, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> false
s << n
#=> #<Set: {1, 3, 4, 2}> # no change
n, s = enum.next
#=> [-3, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> true
causing true to be returned.
I can’t reproduce any problem with your code, but you can express the solution very succinctly using combination to get all possible pairs, then summing each pair with reduce, and finally checking if any are zero?:
[1,2,3,4,0,1,-1].combination(2).map { |pair| pair.reduce(:+) }.any?(&:zero?)
This is a bit of a code review. Let's start with the first method:
def checkZero(array)
Ruby naming convention is snake_case rather than camelCase. This should be def check_zero(array)
Now the loop:
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
As #AndrewMarshall said, for is not idiomatic. each is preferable. However, in ruby initializing a variable before a loop is almost never needed thanks to all the methods available to you on Array and Enumerable (which is included in Array). I highly recommend committing these methods to memory. The above can be written
array.any? {|number| number.zero?}
or equivalently
array.any?(&:zero?)
Now, this part:
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
Whenever you have the pattern
if (expr that returns true or false)
return true
else
return false
end
it can be simplified to simply return (expr that returns true or false). And you can even omit the return if it is the last statement of a method.
Putting it all together:
def check_zero(array)
array.any?(&:zero?)
end
def check_zero_sum(array)
array.combination(2).any?{|a,b| a + b == 0}
end
def check_array(array)
check_zero(array) || check_zero_sum(array)
end
(Note I borrowed AndrewMarshall's code for check_zero_sum which I think is easy to follow, but #CarySwoveland's answer will be faster)
Edit
I missed the fact that check_zero isn't even necessary because you want at least a pair, in which case check_zero_sum is all you need.
def check_array(array)
array.combination(2).any?{|a,b| a + b == 0}
end
Enumerable#max_by and Enumerable#min_by return one of the relevant elements (presumably the first one) when there are multiple max/min elements in the receiver. For example, the following:
[1, 2, 3, 5].max_by{|e| e % 3}
returns only 2 (or only 5).
Instead, I want to return all max/min elements and in an array. In the example above, it would be [2, 5] (or [5, 2]). What is the best way to get this?
arr = [1, 2, 3, 5]
arr.group_by{|a| a % 3} # => {1=>[1], 2=>[2, 5], 0=>[3]}
arr.group_by{|a| a % 3}.max.last # => [2, 5]
arr=[1, 2, 3, 5, 7, 8]
mods=arr.map{|e| e%3}
find max
max=mods.max
indices = []
mods.each.with_index{|m, i| indices << i if m.eql?(max)}
arr.select.with_index{|a,i| indices.include?(i)}
find min
min = mods.min
indices = []
mods.each.with_index{|m, i| indices << i if m.eql?(min)}
arr.select.with_index{|a,i| indices.include?(i)}
Sorry for clumsy code, will try to make it short.
Answer by #Sergio Tulentsev is the best and efficient answer, found things to learn there. +1
This is the hash equivalent of #Serio's use of group_by.
arr = [1, 2, 3, 5]
arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |e,h| h[e%3] << e }.max.last
#=> [2, 5]
The steps:
h = arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |e,h| h[e%3] << e }
#=> {1=>[1], 2=>[2, 5], 0=>[3]}
a = h.max
#=> [2, [2, 5]]
a.last
#=> [2, 5]
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]