Reversed sequence in Ruby - 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

Related

array.map! starting at index other than 0

I am trying to mutate an array using map!, but starting index 1 and leaving index 0 unchanged:
test = [1, 2, 3, 4]
test[1..-1].map!{ |n| n += 2 }
puts test
I would expect this to return
1
4
5
6
But it instead returns the unmutated array. Can someone explain why this does not work? Thanks
test = [1, 2, 3, 4]
test.map!.with_index { |n, i| i == 0 ? n : n + 2 }
# => [1, 4, 5, 6]
You can't use test[1..] because it creates a new array. I recommend you avoid mutated array operations if possible (https://www.rubypigeon.com/posts/avoid-mutation-functional-style-in-ruby/).

ruby method returns wrong elements with odd/even index ruby 2.6.0

def even_odd_array
number = 5169294814153321
array_odd_index = []
array_even_index = []
array_of_chars = number.to_s.chars.map(&:to_i)
array_of_chars.each { |x| array_of_chars.index(x) % 2 == 0 ? array_even_index << x : array_odd_index << x } <----- this returns wrong arrays
#array_odd_index, array_even_index = array_of_chars.each_slice(2).to_a.transpose
p array_even_index
p array_odd_index
end
array_even_index [5, 6, 2, 4, 4, 5, 3, 3, 2]
array_odd_index [1, 9, 9, 8, 1, 1, 1]
what's wrong with it and are there any other ways to make it?
The problem with your actual code is that index returns the index of the first element it finds in the receiver. As 1 is 4 times in number it'll return the index of the first 1 in number from left to right, same for all other repeated numbers.
An easy solution; use each_with_index which allows you to iterate over each element in the receiver plus yielding the current index of that element, so you can check if the index is even or not, deciding where to push the element:
array_of_chars.each_with_index do |x, index|
if index.even?
array_even_index << x
else
array_odd_index << x
end
end
Or you can use partition plus with_index for that:
array_even_index, array_odd_index = 5169294814153321.digits.reverse.partition.with_index { |_, index| index.even? }
p array_even_index # [5, 6, 2, 4, 1, 1, 3, 2]
p array_odd_index # [1, 9, 9, 8, 4, 5, 3, 1]

Algorithm for array with `while` or `until` loop

I have:
array = [1, 4, -1, 3, 2]
I want a new array that follows the following logic:
First element is located at index 0, so it is 1.
Second element is located at index 1 (because value for index 0 was 1).
Third element is located at index 4, so it is 2.
And so on until the loop meets value -1, which is the last value, and it should brake.
The new array should be:
[1, 4, 2, -1]
I have:
def task(a)
array = []
a.each_with_index do |v, i|
result = a[i]
until a[i] == -1
array << a[result]
end
end
puts result
end
As others say, you need to change the index in your loop. Also, if you want -1 in the result, you should exit at bottom. And with_index will give you indices in order, which is not what you want here. This will do what you want:
def task(a)
i = 0
array = []
begin
i = a[i]
array << i
end until i == -1
array
end
p task([1, 4, -1, 3, 2])
# => [1, 4, 2, -1]
until a[i] == -1
array << a[result]
end
This code is looping eternally - there is nothing to change i .
As discussed in the comments, you are looping through the array which is not what you require.
You could use a recursive method to handle jumping from one element to another based on previous value. Consider the following:
arr = [1, 4, -1, 3, 2]
def task(arr, n=0, result=[])
if arr[n] == -1
return result + [-1]
end
r = arr[n]
task(arr, r, result + [r])
end
puts task(arr)
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
first_element = input_array.first
last_element = input_array[last_valid_index]
middle_elements = (1..last_valid_index).map { |i| input_array[input_array[i-1]]}
output_array = [first_element] + middle_elements + [last_element]
p output_array
# => [1, 4, 2, -1]
you could to most of it on one line like so, but I think the more verbose version is more self documenting.
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
output_array = [input_array.first] + (1..last_valid_index).map { |i| input_array[input_array[i-1]]} + [input_array[last_valid_index]]
p output_array
# => [1, 4, 2, -1]
I'd suggest this option, just to avoid infinite loops or index out range:
i, ary = 0, [array[0]]
array.size.times do
break if array[i] == -1 or array[i] > array.size - 1
i = array[i]
ary << array[i]
end
ary #=> [1, 4, 2, -1]
An infinite loop happens for example when array = [1, 4, -1, 0, 3].
Index out of range can happen when array = [1, 4, 6, 3, 2]

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.

Random permutation iterator

Need to augment Enumerable module with new iterator, that returns elements of collection in random order. The only information about collection - it responds to each. No other assumptions about elements.
I have a solution - to wrap elements into Array and then use sample method:
def each_permuted
tmp = []
self.each do |w|
tmp << w
end
tmp.sample(tmp.length).each do |w|
yield w
end
end
Don't like it, because here we go through collection twice(even three times counting tmp.sample random permutation).
Is it possible with single go through?
I doubt that it is possible to do with signle go through. Take a look at this page: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm
I implemented the algorithm named "the inside-out algorithm" in the article (it goes through collection twice):
def each_permuted
generator = Random.new
tmp = []
self.each do |w|
r = generator.rand(tmp.size + 1)
if r == tmp.size
tmp << w
else
tmp << tmp[r]
tmp[r] = w
end
end
tmp.each do |w|
yield w
end
end
Tests:
1.9.3p327 :064 > [1,2,3,4,5,6].each_permuted { |x| p x }
1
5
2
6
3
4
=> [1, 5, 2, 6, 3, 4]
1.9.3p327 :065 > [1,2,3,4,5,6].each_permuted { |x| p x }
4
3
2
5
6
1
=> [4, 3, 2, 5, 6, 1]
1.9.3p327 :066 > [1,2,3,4,5,6].each_permuted { |x| p x }
4
5
2
1
3
6
=> [4, 5, 2, 1, 3, 6]
def each_permuted &pr; shuffle.each(&pr) end

Resources