Distributions using nested loops - ruby

I would like to write a program which generates all distributions for a given n.
For example, if I enter n equal to 7, the returned result will be:
7
6 1
5 2
5 1 1
4 3
4 2 1
4 1 1 1
3 3 1
3 2 2
3 2 1 1
3 1 1 1 1
2 2 2 1
2 2 1 1 1
2 1 1 1 1 1
1 1 1 1 1 1 1
I wrote the following code:
def sum(a, n)
for i in 1..a.length
a.each do |a|
z = a+i
if z == n
print i
puts a
end
end
end
end
def distribution(n)
numbers_container = []
for i in 1..n-1
numbers_container<<i
end
sum(numbers_container,n)
end
puts "Enter n"
n = gets.chomp.to_i
distribution(n)
I'm stuck in the part where the program needs to check the sum for more than two augends. I don't have an idea how can I write a second loop.

I suggest you use recursion.
Code
def all_the_sums(n, mx=n, p=[])
return [p] if n.zero?
mx.downto(1).each_with_object([]) { |i,a|
a.concat(all_the_sums(n-i, [n-i,i].min, p + [i])) }
end
Example
all_the_sums(7)
#=> [[7],
# [6, 1],
# [5, 2], [5, 1, 1],
# [4, 3], [4, 2, 1], [4, 1, 1, 1],
# [3, 3, 1], [3, 2, 2], [3, 2, 1, 1], [3, 1, 1, 1, 1],
# [2, 2, 2, 1], [2, 2, 1, 1, 1], [2, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1]]
Explanation
The argument mx is to avoid the generation of permuations of results. For example, one sequence is [4,2,1]. There are six permutations of the elements of this array (e.g., [4,1,2], [2,4,1] and so on), but we want just one.
Now consider the calculations performed by:
all_the_sums(3)
Each eight-space indentation below reflects a recursive call to the method.
We begin with
n = 3
mx = 3
p = []
return [[]] if 3.zero? #=> no return
# first value passed block by 3.downto(1)..
i = 3
a = []
# invoke all_the_sums(0, [0,3].min, []+[3])
all_the_sums(0, 0, [3])
return [[3]] if 0.zero? #=> return [[3]]
a.concat([[3]]) #=> [].concat([[3]]) => [[3]]
# second value passed block by 3.downto(1)..
i = 2
a = [[3]]
# invoke all_the_sums(1, [1,2].min, []+[2])
all_the_sums(1, 1, [2])
return [[2]] if 1.zero? #=> do not return
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [2]+[1])
all_the_sums(0, 0, [2,1])
return [[2,1]] if 0.zero? #=> [[2,1]] returned
a.concat([[2,1]]) #=> [].concat([[2,1]]) => [[2,1]]
return a #=> [[2,1]]
a.concat([[2,1]]) #=> [[3]].concat([[2,1]]) => [[3],[2,1]]
# third and last value passed block by 3.downto(1)..
i = 1
a = [[3],[2,1]]
# invoke all_the_sums(2, [2,1].min, [1])
all_the_sums(2, 1, [1])
return [] if 2.zero? #=> [] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(1, [1,1].min, [1]+[1])
all_the_sums(1, 1, [1,1])
return [1,1] if 1.zero? #=> [1,1] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [1,1]+[1]])
all_the_sums(0, 0, [1,1,1])
return [1,1,1] if 1.zero?
#=> return [1,1,1]
a.concat([[1,1,1]]) #=> [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [].concat([[1,1,1]]) => [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [[3],[2,1]].concat([[1,1,1]])
return a #=> [[3],[2,1],[1,1,1]]

You can use unary with parameters to have infinite amounts of parameters:
def test_method *parameters
puts parameters
puts parameters.class
end
test_method("a", "b", "c", "d")
So, parameters inside the block becomes an array of parameters. You can then easly loop through them:
parameters.each { |par| p par }
Also, don't use for loops for this as they are less readable than using each methods.
[1..n-1].each do i
# body omitted
end

I think you be able to work it out if you tried to call sum recursively. After this bit:
print i
puts a
Try calling sum again, like this:
sum((1..a).to_a, a)
It won't solve it, but it might lead you in the right direction.

Related

Reversed sequence in Ruby

How do I return an array of integers from n to 1 where n>0? I wrote this code:
def reverse_seq(num)
reverse_seq = []
[].reverse { |num| num > 0; num += 1 }
return []
end
Thanks!
You could create an enumerator via downto that goes from n down to 1 and turn that into an array:
n = 5
n.downto(1).to_a
#=> [5, 4, 3, 2, 1]
or you could call Array.new with a block and calculate each value based on its index:
n = 5
Array.new(n) { |i| n - i }
#=> [5, 4, 3, 2, 1]
or you could traverse a n..1 range by passing -1 to step:
n = 5
(n..1).step(-1).to_a
#=> [5, 4, 3, 2, 1]
Or
(1..5).to_a.reverse
#=> [5, 4, 3, 2, 1]
Or if you want to iterate over those elements in a next step anyway, use reverse_each
(1..5).reverse_each { |i| puts i }
#=> 5
4
3
2
1
As of 2.7 you can also use Enumerator#produce which is my new favorite way to create sequences.
For your use case:
def reverse_seq(num)
Enumerator.produce(num) {|prev| prev.positive? ? prev.pred : raise(StopIteration) }
end

Birthday Chocolate HACKERRANK RUBY

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.

How to handle negative iterator passed in function?

I am working on a manual rotate function in Ruby. But I ran into issue there are negative offsets passed in some examples. Is it possible to iterate from a negative number up to a specified index(not sure what that index would be)?
def my_rotate(arr, offset=1)
if offset < 1
for i in offset
arr.push(arr.shift)
end
else
for i in 1..offset
arr.push(arr.shift)
end
end
arr
end
Following with your code, you can use Array#pop and Array#unshift (which are the opposites of Array#push and Array#shift):
def my_rotate(array, offset=1)
arr = array.dup
if offset < 1
for i in 1..offset.abs
arr.unshift(arr.pop)
end
else
for i in 1..offset
arr.push(arr.shift)
end
end
arr
end
Notice the change in line 5 for i in 1..offset.abs to be able to loop the array, and the addition of line 2 arr = array.dup to prevent the original array from being mutated.
This is pretty much how Array#rotate does it (in C).
Code
class Array
def my_rotate(n=1)
n %= self.size
self[n..-1].concat(self[0,n])
end
end
Examples
arr = [1,2,3,4]
arr.my_rotate 0 #=> [1,2,3,4]
arr.my_rotate #=> [2, 3, 4, 1]
arr.my_rotate 1 #=> [2, 3, 4, 1]
arr.my_rotate 4 #=> [1, 2, 3, 4]
arr.my_rotate 5 #=> [2, 3, 4, 1]
arr.my_rotate 9 #=> [2, 3, 4, 1]
arr.my_rotate -1 #=> [4, 1, 2, 3]
arr.my_rotate -4 #=> [1, 2, 3, 4]
arr.my_rotate -5 #=> [4, 1, 2, 3]
arr.my_rotate -9 #=> [4, 1, 2, 3]
Explanation
The line
n %= self.size
which Ruby's parser expands to
n = n % self.size
converts n to an integer between 0 and self.size - 1. Moreover, it does so for both positive and negative values of n.
The line
self[n..-1].concat(self[0,n])
appends the first n elements of arr to an array comprised of the last arr.size - n elements of arr. The resulting array is then returned by the method.
If you do not wish to add this method to the class Array you could of course define it def my_rotate(arr, n)....

Number of possible equations of K numbers whose sum is N in ruby

I have to create a program in ruby on rails so that it will take less time to solve the particular condition. Now i am to getting the less response time for k=4 but response time is more in case of k>5
Problem:
Problem is response time.
When value of k is more than 5 (k>5) response time is too late for given below equation.
Input: K, N (where 0 < N < ∞, 0 < K < ∞, and K <= N)
Output: Number of possible equations of K numbers whose sum is N.
Example Input:
N=10 K=3
Example Output:
Total unique equations = 8
1 + 1 + 8 = 10
1 + 2 + 7 = 10
1 + 3 + 6 = 10
1 + 4 + 5 = 10
2 + 2 + 6 = 10
2 + 3 + 5 = 10
2 + 4 + 4 = 10
3 + 3 + 4 = 10
For reference, N=100, K=3 should have a result of 833 unique sets
Here is my ruby code
module Combination
module Pairs
class Equation
def initialize(params)
#arr=[]
#n = params[:n]
#k = params[:k]
end
#To create possible equations
def create_equations
return "Please Enter value of n and k" if #k.blank? && #n.blank?
begin
Integer(#k)
rescue
return "Error: Please enter any +ve integer value of k"
end
begin
Integer(#n)
rescue
return "Error: Please enter any +ve integer value of n"
end
return "Please enter k < n" if #n < #k
create_equations_sum
end
def create_equations_sum
aar = []
#arr = []
#list_elements=(1..#n).to_a
(1..#k-1).each do |i|
aar << [*0..#n-1]
end
traverse([], aar, 0)
return #arr.uniq #return result
end
#To check sum
def generate_sum(*args)
new_elements = []
total= 0
args.flatten.each do |arg|
total += #list_elements[arg]
new_elements << #list_elements[arg]
end
if total < #n
new_elements << #n - total
#arr << new_elements.sort
else
return
end
end
def innerloop(arrayOfCurrentValues)
generate_sum(arrayOfCurrentValues)
end
#Recursive method to create dynamic nested loops.
def traverse(accumulated,params, index)
if (index==params.size)
return innerloop(accumulated)
end
currentParam = params[index]
currentParam.each do |currentElementOfCurrentParam|
traverse(accumulated+[currentElementOfCurrentParam],params, index+1)
end
end
end
end
end
run the code using
params = {:n =>100, :k =>4}
c = Combination::Pairs::Equation.new(params)
c.create_equations
Here are two ways to compute your answer. The first is simple but not very efficient; the second, which relies on an optimization technique, is much faster, but requires considerably more code.
Compact but Inefficient
This is a compact way to do the calculation, making use of the method Array#repeated_combination:
Code
def combos(n,k)
[*(1..n-k+1)].repeated_combination(3).select { |a| a.reduce(:+) == n }
end
Examples
combos(10,3)
#=> [[1, 1, 8], [1, 2, 7], [1, 3, 6], [1, 4, 5],
# [2, 2, 6], [2, 3, 5], [2, 4, 4], [3, 3, 4]]
combos(100,4).size
#=> 832
combos(1000,3).size
#=> 83333
Comment
The first two calculations take well under one second, but the third took a couple of minutes.
More efficient, but increased complexity
Code
def combos(n,k)
return nil if k.zero?
return [n] if k==1
return [1]*k if k==n
h = (1..k-1).each_with_object({}) { |i,h| h[i]=[[1]*i] }
(2..n-k+1).each do |i|
g = (1..[n/i,k].min).each_with_object(Hash.new {|h,k| h[k]=[]}) do |m,f|
im = [i]*m
mxi = m*i
if m==k
f[mxi].concat(im) if mxi==n
else
f[mxi] << im if mxi + (k-m)*(i+1) <= n
(1..[(i-1)*(k-m), n-mxi].min).each do |j|
h[j].each do |a|
f[mxi+j].concat([a+im]) if
((a.size==k-m && mxi+j==n) ||
(a.size<k-m && (mxi+j+(k-m-a.size)*(i+1))<=n))
end
end
end
end
g.update({ n=>[[i]*k] }) if i*k == n
h.update(g) { |k,ov,nv| ov+nv }
end
h[n]
end
Examples
p combos(10,3)
#=> [[3, 3, 4], [2, 4, 4], [2, 3, 5], [1, 4, 5],
# [2, 2, 6], [1, 3, 6], [1, 2, 7], [1, 1, 8]]
p combos(10,4)
#=> [[2, 2, 3, 3], [1, 3, 3, 3], [2, 2, 2, 4], [1, 2, 3, 4], [1, 1, 4, 4],
# [1, 2, 2, 5], [1, 1, 3, 5], [1, 1, 2, 6], [1, 1, 1, 7]]
puts "size=#{combos(100 ,3).size}" #=> 833
puts "size=#{combos(100 ,5).size}" #=> 38224
puts "size=#{combos(1000,3).size}" #=> 83333
Comment
The calculation combos(1000,3).size took about five seconds, the others were all well under one second.
Explanation
This method employs dynamic programming to compute a solution. The state variable is the largest positive integer used to compute arrays with sizes no more than k whose elements sum to no more than n. Begin with the largest integer equal to one. The next step is compute all combinations of k or fewer elements that include the numbers 1 and 2, then 1, 2 and 3, and so on, until we have all combinations of k or fewer elements that include the numbers 1 through n. We then select all combinations of k elements that sum to n from the last calculation.
Suppose
k => 3
n => 7
then
h = (1..k-1).each_with_object({}) { |i,h| h[i]=[[1]*i] }
#=> (1..2).each_with_object({}) { |i,h| h[i]=[[1]*i] }
#=> { 1=>[[1]], 2=>[[1,1]] }
This reads, using the only the number 1, [[1]] is the array of all arrays that sum to 1 and [[1,1]] is the array of all arrays that sum to 2.
Notice that this does not include the element 3=>[[1,1,1]]. That's because, already having k=3 elments, if cannot be combined with any other elements, and sums to 3 < 7.
We next execute:
enum = (2..n-k+1).each #=> #<Enumerator: 2..5:each>
We can convert this enumerator to an array to see what values it will pass into its block:
enum.to_a #=> [2, 3, 4, 5]
As n => 7 you may be wondering why this array ends at 5. That's because there are no arrays containing three positive integers, of which at least one is a 6 or a 7, whose elements sum to 7.
The first value enum passes into the block, which is represented by the block variable i, is 2. We will now compute a hash g that includes all arrays that sum to n => 7 or less, have at most k => 3 elements, include one or more 2's and zero or more 1's. (That's a bit of a mouthful, but it's still not precise, as I will explain.)
enum2 = (1..[n/i,k].min).each_with_object(Hash.new {|h,k| h[k]=[]})
#=> (1..[7/2,3].min).each_with_object(Hash.new {|h,k| h[k]=[]})
#=> (1..3).each_with_object(Hash.new {|h,k| h[k]=[]})
Enumerable#each_with_object creates an initially-empty hash that is represented by the block variable f. The default value of this hash is such that:
f[k] << o
is equivalent to
(f[k] |= []) << o
meaning that if f does not have a key k,
f[k] = []
is executed before
f[k] << o
is performed.
enum2 will pass the following elements into its block:
enum2.to_a #=> => [[1, {}], [2, {}], [3, {}]]
(though the hash may not be empty when elements after the first are passed into the block). The first element passed to the block is [1, {}], represented by the block variables:
m => 1
f => Hash.new {|h,k| h[k]=[]}
m => 1 means we will intially construct arrays that contain one (i=) 2.
im = [i]*m #=> [2]*1 => [2]
mxi = m*i #=> 2*1 => 2
As (m == k) #=> (1 == 3) => false, we next execute
f[mxi] << im if mxi + (k-m)*(i+1) <= n
#=> f[2] << [2] if 2 + (3-1)*(1+1) <= 7
#=> f[2] << [2] if 8 <= 7
This considers whether [2] should be added to f[2] without adding any integers j < i = 2. (We have yet to consider the combining of one 2 with integers less than 2 [i.e., 1].) As 8 <= 7, we do not add [2] to f[2]. The reason is that, for this to be part of an array of length k=3, it would be of the form [2,x,y], where x > 2 and y > 2, so 2+x+y >= 2+3+3 = 8 > n = 7. Clear as mud?
Next,
enum3 = (1..[(i-1)*(k-m), n-mxi].min).each
#=> = (1..[2,5].min).each
#=> = (1..2).each
#=> #<Enumerator: 1..2:each>
which passes the values
enum3.to_a #=> [1, 2]
into its block, represented by the block variable j, which is the key of the hash h. What we will be doing here is combine one 2 (m=1) with arrays of elements containing integers up to 1 (i.e., just 1) that sum to j, so the elements of the resulting array will sum to m * i + j => 1 * 2 + j => 2 + j.
The reason enum3 does not pass values of j greater than 2 into its block is that h[l] is empty for l > 2 (but its a little more complicated when i > 2).
For j => 1,
h[j] #=> [[1]]
enum4 = h[j].each #=> #<Enumerator: [[1]]:each>
enum4.to_a #=> [[1]]
a #=> [1]
so
f[mxi+j].concat([a+im]) if
((a.size==k-m && mxi+j==n) || (a.size<k-m && (mxi+j+(k-m-a.size)*(i+1))<=n))
#=> f[2+1].concat([[1]+[2]) if ((1==2 && 2+1==7) || (1<=3-1 && (2+1+(1)*(3)<=7))
#=> f[3].concat([1,2]) if ((false && false) || (1<=2 && (6<=7))
#=> f[3] = [] << [[1,2]] if (false || (true && true)
#=> f[3] = [[1,2]] if true
So the expression on the left is evaluated. Again, the conditional expressions are a little complex. Consider first:
a.size==k-m && mxi+j==n
which is equivalent to:
([2] + f[j]).size == k && ([2] + f[j]).reduce(:+) == n
That is, include the array [2] + f[j] if it has k elements that sum to n.
The second condition considers whether the array the arrays [2] + f[j] with fewer than k elements can be "completed" with integers l > i = 2 and have a sum of n or less.
Now, f #=> {3=>[[1, 2]]}.
We now increment j to 2 and consider arrays [2] + h[2], whose elements will total 4.
For j => 2,
h[j] #=> [[1, 1]]
enum4 = h[j].each #=> #<Enumerator: [[1, 1]]:each>
enum4.to_a #=> [[1, 1]]
a #=> [1, 1]
f[mxi+j].concat([a+im]) if
((a.size==k-m && mxi+j==n) || (a.size<k-m && (mxi+j+(k-m-a.size)*(i+1)<=n))
#=> f[4].concat([1, 1, 2]) if ((2==(3-1) && 2+2 == 7) || (2+2+(3-1-2)*(3)<=7))
#=> f[4].concat([1, 1, 2]) if (true && false) || (false && true))
#=> f[4].concat([1, 1, 2]) if false
so this operation is not performed (since [1,1,2].size => 3 = k and [1,1,2].reduce(:+) => 4 < 7 = n.
We now increment m to 2, meaning that we will construct arrays having two (i=) 2's. After doing so, we see that:
f={3=>[[1, 2]], 4=>[[2, 2]]}
and no other arrays are added when m => 3, so we have:
g #=> {3=>[[1, 2]], 4=>[[2, 2]]}
The statement
g.update({ n=>[i]*k }) if i*k == n
#=> g.update({ 7=>[2,2,2] }) if 6 == 7
adds the element 7=>[2,2,2] to the hash g if the sum of its elements equals n, which it does not.
We now fold g into h, using Hash#update (aka Hash#merge!):
h.update(g) { |k,ov,nv| ov+nv }
#=> {}.update({3=>[[1, 2]], 4=>[[2, 2]]} { |k,ov,nv| ov+nv }
#=> {1=>[[1]], 2=>[[1, 1]], 3=>[[1, 2]], 4=>[[2, 2]]}
Now h contains all the arrays (values) whose keys are the array totals, comprised of the integers 1 and 2, which have at most 3 elements and sum to at most 7, excluding those arrays with fewer than 3 elements which cannot sum to 7 when integers greater than two are added.
The operations performed are as follows:
i m j f
h #=> { 1=>[[1]], 2=>[[1,1]] }
2 1 1 {3=>[[1, 2]]}
2 1 2 {3=>[[1, 2]]}
2 2 1 {3=>[[1, 2]], 4=>[[2, 2]]}
{3=>[[1, 2]], 4=>[[2, 2]]}
3 1 1 {}
3 1 2 {}
3 1 3 {}
3 1 4 {7=>[[2, 2, 3]]}
3 2 1 {7=>[[2, 2, 3], [1, 3, 3]]}
g before g.update: {7=>[[2, 2, 3], [1, 3, 3]]}
g after g.update: {7=>[[2, 2, 3], [1, 3, 3]]}
h after h.update(g): {1=>[[1]],
2=>[[1, 1]],
3=>[[1, 2]],
4=>[[2, 2]],
7=>[[2, 2, 3], [1, 3, 3]]}
4 1 1 {}
4 1 2 {}
4 1 3 {7=>[[1, 2, 4]]}
g before g.update: {7=>[[1, 2, 4]]}
g after g.update: {7=>[[1, 2, 4]]}
h after h.update(g): {1=>[[1]],
2=>[[1, 1]],
3=>[[1, 2]],
4=>[[2, 2]],
7=>[[2, 2, 3], [1, 3, 3], [1, 2, 4]]}
5 1 1 {}
5 1 2 {7=>[[1, 1, 5]]}
g before g.update: {7=>[[1, 1, 5]]}
g after g.update: {7=>[[1, 1, 5]]}
h after h.update(g): {1=>[[1]],
2=>[[1, 1]],
3=>[[1, 2]],
4=>[[2, 2]],
7=>[[2, 2, 3], [1, 3, 3], [1, 2, 4], [1, 1, 5]]}
And lastly,
h[n].select { |a| a.size == k }
#=> h[7].select { |a| a.size == 3 }
#=> [[2, 2, 3], [1, 3, 3], [1, 2, 4], [1, 1, 5]]
#Cary's answer is very in-depth and impressive, but it appears to me that there is a much more naive solution, which proved to be much more efficient as well - good old recursion:
def combos(n,k)
if k == 1
return [n]
end
(1..n-1).flat_map do |i|
combos(n-i,k-1).map { |r| [i, *r].sort }
end.uniq
end
This solution simply reduces the problem each level by taking decreasing the target sum by each number between 1 and the previous target sum, while reducing k by one. Now make sure you don't have duplicates (by sort and uniq) - and you have your answer...
This is great for k < 5, and is much faster than Cary's solution, but as k gets larger, I found that it makes much too many iterations, sort and uniq took a very big toll on the calculation.
So I made sure that won't be needed, by making sure I get only sorted answers - each recursion should check only numbers larger than those already used:
def combos(n,k,min = 1)
if n < k || n < min
return []
end
if k == 1
return [n]
end
(min..n-1).flat_map do |i|
combos(n-i,k-1, i).map { |r| [i, *r] }
end
end
This solution is on par with Cary's on combos(100, 7):
user system total real
My Solution 2.570000 0.010000 2.580000 ( 2.695615)
Cary's 2.590000 0.000000 2.590000 ( 2.609374)
But we can do better: caching! This recursion does many calculations again and again, so caching stuff we already did will save us a lot of work when dealing with long sums:
def combos(n,k,min = 1, cache = {})
if n < k || n < min
return []
end
cache[[n,k,min]] ||= begin
if k == 1
return [n]
end
(min..n-1).flat_map do |i|
combos(n-i,k-1, i, cache).map { |r| [i, *r] }
end
end
end
This solution is mighty fast and passes Cary's solution for large n by light-years:
Benchmark.bm do |bm|
bm.report('Uri') { combos(1000, 3) }
bm.report('Cary') { combos_cary(1000, 3) }
end
user system total real
Uri 0.200000 0.000000 0.200000 ( 0.214080)
Cary 7.210000 0.000000 7.210000 ( 7.220085)
And is on par with k as high as 9, and I believe it is still less complicated than his solution.
You want the number of integer partitions of n into exactly k summands. There is a (computationally) somewhat ugly recurrence for that number.
The idea is this: let P(n,k) be the number of ways to partition n into k nonzero summands; then P(n,k) = P(n-1,k-1) + P(n-k,k). Proof: every partition either contains a 1 or it doesn't contain a 1 as one of the summands. The first case P(n-1,k-1) calculates the number of cases where there is a 1 in the sum; take that 1 away from the sum and partition the remaining n-1 into the now available k-1 summands. The second case P(n-k,k) considers the case where every summand is strictly greater than 1; to do that, reduce all of the k summands by 1 and recurse from there. Obviously, P(n,1) = 1 for all n > 0.
Here's a link that mentions that probably, no closed form is known for general k.

ruby get next value on each loop

Can I get the next value in an each loop?
(1..5).each do |i|
#store = i + (next value of i)
end
where the answer would be..
1 + 2 + 2 + 3 + 3 + 4 + 4 + 5 + 5 = 29
And also can I get the next of the next value?
From as early as Ruby 1.8.7, the Enumerable module has had a method each_cons that does almost exactly what you want:
each_cons(n) { ... } → nil
each_cons(n) → an_enumerator
Iterates the given block for each array of consecutive <n> elements. If no block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) { |a| p a }
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
The only problem is that it doesn't repeat the last element. But that's trivial to fix. Specifically, you want
store = 0
range = 1..5
range.each_cons(2) do |i, next_value_of_i|
store += i + next_value_of_i
end
store += range.end
p store # => 29
But you could also do this:
range = 1..5
result = range.each_cons(2).reduce(:+).reduce(:+) + range.end
p result # => 29
Alternatively, you may find the following to be more readable:
result = range.end + range.each_cons(2)
.reduce(:+)
.reduce(:+)
Like this:
range = 1..5
store = 0
range.each_with_index do |value, i|
next_value = range.to_a[i+1].nil? ? 0 : range.to_a[i+1]
store += value + next_value
end
p store # => 29
There may be better ways, but this works.
You can get the next of the next value like this:
range.to_a[i+2]
One approach that wouldn't use indexes is Enumerable#zip:
range = 11..15
store = 0 # This is horrible imperative programming
range.zip(range.to_a[1..-1], range.to_a[2..-1]) do |x, y, z|
# nil.to_i equals 0
store += [x, y, z].map(&:to_i).inject(:+)
end
store

Resources