Ruby compute cumulative sum from endpoints recursively? - ruby

Given two numbers, say (14, 18), the problem is to find the sum of all the numbers in this range, 14, 15, 16, 17, 18 recursively. Now, I have done this using loops but I have trouble doing this recursively.
Here is my recursive solution:
def sum_cumulative_recursive(a,b)
total = 0
#base case is a == b, the stopping condition
if a - b == 0
puts "sum is: "
return total + a
end
if b - a == 0
puts "sum is: "
return total + b
end
#case 1: a > b, start from b, and increment recursively
if a > b
until b > a
puts "case 1"
total = b + sum_cumulative_recursive(a, b+1)
return total
end
end
#case 2: a < b, start from a, and increment recursively
if a < b
until a > b
puts "case 2"
total = a + sum_cumulative_recursive(a+1, b)
return total
end
end
end
Here are some sample test cases:
puts first.sum_cumulative_recursive(4, 2)
puts first.sum_cumulative_recursive(14, 18)
puts first.sum_cumulative_recursive(-2,-2)
My solution works for cases where a > b, and a < b, but it doesn't work for a == b.
How can I fix this code so that it works?
Thank you for your time.

def sum_cumulative_recursive(a,b)
return a if a == b
a, b = [a,b].sort
a + sum_cumulative_recursive(a + 1, b)
end
EDIT
Here is the most efficient solution I could see from some informal benchmarks:
def sum_cumulative_recursive(a,b)
return a if a == b
a, b = b, a if a > b
a + sum_cumulative_recursive(a + 1, b)
end
Using:
Benchmark.measure { sum_cumulative_recursive(14,139) }
Benchmark for my initial response: 0.005733
Benchmark for #Ajedi32's response: 0.000371
Benchmark for my new response: 0.000115
I was also surprised to see that in some cases, the recursive solution approaches or exceeds the efficiency of the more natural inject solution:
Benchmark.measure { 10.times { (1000..5000).inject(:+) } }
# => 0.010000 0.000000 0.010000 ( 0.027827)
Benchmark.measure { 10.times { sum_cumulative_recursive(1000,5000) } }
# => 0.010000 0.010000 0.020000 ( 0.019441)
Though you run into stack level too deep errors if you take it too far...

I'd do it like this:
def sum_cumulative_recursive(a, b)
a, b = a.to_i, b.to_i # Only works with ints
return sum_cumulative_recursive(b, a) if a > b
return a if a == b
return a + sum_cumulative_recursive(a+1, b)
end

Here's one way of doing it. I assume this is just an exercise, as the sum of the elements of a range r is of course just (r.first+r.last)*(f.last-r.first+1)/2.
def sum_range(range)
return nil if range.last < range.first
case range.size
when 1 then range.first
when 2 then range.first + range.last
else
range.first + range.last + sum_range(range.first+1..range.last-1)
end
end
sum_range(14..18) #=> 80
sum_range(14..14) #=> 14
sum_range(14..140) #=> 9779
sum_range(14..139) #=> 9639

Another solution would be to have a front-end invocation that fixes out-of-order arguments, then a private recursive back-end which does the actual work. I find this is useful to avoid repeated checks of arguments once you've established they're clean.
def sum_cumulative_recursive(a, b)
a, b = b, a if b < a
_worker_bee_(a, b)
end
private
def _worker_bee_(a, b)
a < b ? (a + _worker_bee_(a+1,b-1) + b) : a == b ? a : 0
end
This variant would cut the stack requirement in half by summing from both ends.
If you don't like that approach and/or you really want to trim the stack size:
def sum_cumulative_recursive(a, b)
if a < b
mid = (a + b) / 2
sum_cumulative_recursive(a, mid) + sum_cumulative_recursive(mid+1, b)
elsif a == b
a
else
sum_cumulative_recursive(b, a)
end
end
This should keep the stack size to O(log |b-a|).

Related

Finding the sum of all the numbers between a and b: Ruby

Been working on this Kata for quite some time now and still can't figure out what I'm missing. The question is given two integers a and b, which can be positive or negative, find the sum of all the numbers between including them too and return it. If the two numbers are equal return a or b.
So far this is what my solution looks like:
def get_sum(a,b)
sum = [a+=b].sum
if sum == a or b
return a
end
end
and this is the output result:
Test Passed: Value == 1
Test Passed: Value == 3
Expected: 14, instead got: 4
Expected: 127759, instead got: 509
Expected: 44178, instead got: 444
I believe the keyword is all the numbers between but I'm not sure how to write that syntactically.
I've included some examples below for further clarification.
get_sum(1, 0) == 1 # 1 + 0 = 1
get_sum(1, 2) == 3 # 1 + 2 = 3
get_sum(0, 1) == 1 # 0 + 1 = 1
get_sum(1, 1) == 1 # 1 Since both are same
get_sum(-1, 0) == -1 # -1 + 0 = -1
get_sum(-1, 2) == 2 # -1 + 0 + 1 + 2 = 2
https://www.codewars.com/kata/55f2b110f61eb01779000053/train/ruby
You can use formula for Arithmetic progression:
def get_sum(a, b)
a, b = b, a if a > b
(b - a + 1) * (a + b) / 2
end
Active Support(Rails) extension for Range class OR modern(>= 2.4) Ruby do the same.
So, you can use #MBo answer if your Kata site uses either Rails or modern Ruby. Usually such sites specify the environment and the interpreter version.
def get_sum(a, b)
a, b = b, a if a > b
(a..b).sum
end
Your code does not return result except for a=b case. Also - what [a+=b] generates? Array with a single element a+b, so it's sum is just a+b
Make a range and get it's sum.
Added: parameter ordering
def get_sum(a,b)
a, b = b, a if a > b
return (a..b).sum
end
print get_sum(1,3)
print get_sum(2,2)
print get_sum(-1,2)
print get_sum(3,-1)
>> 6 2 2 5
def get_sum(a,b)
sum = [a+=b].sum
if sum == a or b
return a
end
end
Other answers explain what you could write instead, so let's check your code:
a+=b is called first. It's basically a = a + b, so it calculates the sum of both inputs, saves it in a, and returns a.
sum = [a].sum creates an array with one element, and calculates its sum (which is just this one element). So sum = a
a or b is just a when a is truthy (that is, neither false nor nil).
So here's what your code actually does:
def get_sum(a,b)
a = a + b
sum = a
if sum == a
return a
end
end
Which is just:
def get_sum(a,b)
a = a + b
return a
end
Or :
def get_sum(a,b)
a = a + b
end
or :
def get_sum(a,b)
a + b
end
Please, try with below and ref enter link description here:
def get_sum(a,b)
return a if a == b
return (a..b).sum if b > a
return (b..a).sum if a > b
end
Test:
describe "Example Tests" do
Test.assert_equals(get_sum(1,1),1)
Test.assert_equals(get_sum(0,1),1)
Test.assert_equals(get_sum(0,-1),-1)
Test.assert_equals(get_sum(1,2),3)
Test.assert_equals(get_sum(5,-1),14)
end
Outout:
Test Results:
Example Tests
Test Passed: Value == 1
Test Passed: Value == -1
Test Passed: Value == 3
Test Passed: Value == 14
You have passed all of the tests! :)

How to optimize code - it works, but I know I'm missing much learning

The exercise I'm working on asks "Write a method, coprime?(num_1, num_2), that accepts two numbers as args. The method should return true if the only common divisor between the two numbers is 1."
I've written a method to complete the task, first by finding all the factors then sorting them and looking for duplicates. But I'm looking for suggestions on areas I should consider to optimize it.
The code works, but it is just not clean.
def factors(num)
return (1..num).select { |n| num % n == 0}
end
def coprime?(num_1, num_2)
num_1_factors = factors(num_1)
num_2_factors = factors(num_2)
all_factors = num_1_factors + num_2_factors
new = all_factors.sort
dups = 0
new.each_index do |i|
dups += 1 if new[i] == new[i+1]
end
if dups > 1
false
else
true
end
end
p coprime?(25, 12) # => true
p coprime?(7, 11) # => true
p coprime?(30, 9) # => false
p coprime?(6, 24) # => false
You could use Euclid's algorithm to find the GCD, then check whether it's 1.
def gcd a, b
while a % b != 0
a, b = b, a % b
end
return b
end
def coprime? a, b
gcd(a, b) == 1
end
p coprime?(25, 12) # => true
p coprime?(7, 11) # => true
p coprime?(30, 9) # => false
p coprime?(6, 24) # => false```
You can just use Integer#gcd:
def coprime?(num_1, num_2)
num_1.gcd(num_2) == 1
end
You don't need to compare all the factors, just the prime ones. Ruby does come with a Prime class
require 'prime'
def prime_numbers(num_1, num_2)
Prime.each([num_1, num_2].max / 2).map(&:itself)
end
def factors(num, prime_numbers)
prime_numbers.select {|n| num % n == 0}
end
def coprime?(num_1, num_2)
prime_numbers = prime_numbers(num_1, num_2)
# & returns the intersection of 2 arrays (https://stackoverflow.com/a/5678143)
(factors(num_1, prime_numbers) & factors(num_2, prime_numbers)).length == 0
end

Harmonic series

1.upto(sums) do |n|
puts harmonic_sum(n)
end
is there a way to label whatever is outputed so if the user enters 6 it counts them as 1 2 3 4 5 6?
The harmonic series isn't implemented in Ruby standard library, but Rational is :
def harmonic_sum(n)
(1..n).inject(Rational(0,1)) {|r, i| r + Rational(1,i) }
end
puts harmonic_sum(5)
#=> 137/60
puts harmonic_sum(50)
#=> 13943237577224054960759/3099044504245996706400
puts harmonic_sum(10_000).to_f
#=> 9.787606036044382
NOTE: Your code is fine BTW. Here are some slight modifications and a few TODOS ;)
class Fraction
attr_reader :numerator, :denominator
def initialize(n, d)
#numerator = n
#denominator = d
end
def to_f
numerator.to_f/denominator
end
#TODO: Add *
#TODO: Define - and / with + and *
#TODO: Check that rhs is a Fraction, convert self to float otherwise.
def +(rhs)
n = #numerator*rhs.denominator + #denominator*rhs.numerator
d = #denominator*rhs.denominator
n, d = reduce(n, d)
return Fraction.new(n, d)
end
def to_s
"#{#numerator} / #{#denominator}"
end
private
def reduce(n, d)
r = gcd(n, d)
return n / r, d / r
end
def gcd(a, b)
if a % b == 0
return b
else
return gcd(b, a % b)
end
end
end
def harmonic_sum(n)
(1..n).inject(Fraction.new(0,1)) {|r, i| r + Fraction.new(1,i) }
end
puts harmonic_sum(100)
#=> 14466636279520351160221518043104131447711 / 2788815009188499086581352357412492142272

Finding the sum of the digits of a factorial

factorial_sum(5) should return 3. The error I'm getting is that "inject is an undefined method". I was also wondering if it's possible to combine the two functions. I wasn't sure as I am just starting out on recursion. Thanks!
def factorial_sum(x)
factorial = factorial(x)
factorial.to_s.split('').collect { |i| i.to_i }
sum = factorial.inject { |sum, n| sum + n }
end
def factorial(x)
if x < 0
return "Negative numbers don't have a factorial"
elsif x == 0
1
else
factorial = x * factorial(x - 1)
end
end
puts factorial_sum(5)
factorial.to_s.split('').collect { |i| i.to_i }
This line is a no-op. You build a list and then throw it away. You probably meant factorial = ...
I have to say though that this would be pretty easy to find with a little effort and some print statements...
By the way, here's a slightly more concise way:
(1..x).reduce(:*).to_s.chars.map(&:to_i).reduce(:+)
A direct way without temporarily converting it into strings, and without recursion.
s, q = 0, 120
while q > 0
q, r = q.divmod(10)
s += r
end
s # => 3

more ruby way of doing project euler #2

I'm trying to learn Ruby, and am going through some of the Project Euler problems. I solved problem number two as such:
def fib(n)
return n if n < 2
vals = [0, 1]
n.times do
vals.push(vals[-1]+vals[-2])
end
return vals.last
end
i = 1
s = 0
while((v = fib(i)) < 4_000_000)
s+=v if v%2==0
i+=1
end
puts s
While that works, it seems not very ruby-ish—I couldn't come up with any good purely Ruby answer like I could with the first one ( puts (0..999).inject{ |sum, n| n%3==0||n%5==0 ? sum : sum+n }).
For a nice solution, why don't you create a Fibonacci number generator, like Prime or the Triangular example I gave here.
From this, you can use the nice Enumerable methods to handle the problem. You might want to wonder if there is any pattern to the even Fibonacci numbers too.
Edit your question to post your solution...
Note: there are more efficient ways than enumerating them, but they require more math, won't be as clear as this and would only shine if the 4 million was much higher.
As demas' has posted a solution, here's a cleaned up version:
class Fibo
class << self
include Enumerable
def each
return to_enum unless block_given?
a = 0; b = 1
loop do
a, b = b, a + b
yield a
end
end
end
end
puts Fibo.take_while { |i| i < 4000000 }.
select(&:even?).
inject(:+)
My version based on Marc-André Lafortune's answer:
class Some
#a = 1
#b = 2
class << self
include Enumerable
def each
1.upto(Float::INFINITY) do |i|
#a, #b = #b, #a + #b
yield #b
end
end
end
end
puts Some.take_while { |i| i < 4000000 }.select { |n| n%2 ==0 }
.inject(0) { |sum, item| sum + item } + 2
def fib
first, second, sum = 1,2,0
while second < 4000000
sum += second if second.even?
first, second = second, first + second
end
puts sum
end
You don't need return vals.last. You can just do vals.last, because Ruby will return the last expression (I think that's the correct term) by default.
fibs = [0,1]
begin
fibs.push(fibs[-1]+fibs[-2])
end while not fibs[-1]+fibs[-2]>4000000
puts fibs.inject{ |sum, n| n%2==0 ? sum+n : sum }
Here's what I got. I really don't see a need to wrap this in a class. You could in a larger program surely, but in a single small script I find that to just create additional instructions for the interpreter. You could select even, instead of rejecting odd but its pretty much the same thing.
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
puts fib.take_while{|i| i < 4000000}
.reject{|x| x.odd?}
.inject(:+)
That's my approach. I know it can be less lines of code, but maybe you can take something from it.
class Fib
def first
#p0 = 0
#p1 = 1
1
end
def next
r =
if #p1 == 1
2
else
#p0 + #p1
end
#p0 = #p1
#p1 = r
r
end
end
c = Fib.new
f = c.first
r = 0
while (f=c.next) < 4_000_000
r += f if f%2==0
end
puts r
I am new to Ruby, but here is the answer I came up with.
x=1
y=2
array = [1,2]
dar = []
begin
z = x + y
if z % 2 == 0
a = z
dar << a
end
x = y
y = z
array << z
end while z < 4000000
dar.inject {:+}
puts "#{dar.sum}"
def fib_nums(num)
array = [1, 2]
sum = 0
until array[-2] > num
array.push(array[-1] + array[-2])
end
array.each{|x| sum += x if x.even?}
sum
end

Resources