Partial Fibonacci Sum, how to improve performance? - ruby

I need to improve the performance of this algorithm. I believe the answer lies in the application of the pisano period.
This algorithm must return the last digit of the sum of fib numbers from f(m) to f(n).
Here is what I have so far:
def fib(n)
a = []
a << 0 << 1
(n+1).times do |i|
a << a[-1] + a[-2]
end
a[n]
end
def fib_partial_sum(m, n)
if n == m
fib(m) % 10
else
m = fib(m + 1) - 1
n = fib(n + 2) - 1
(n - m) % 10
end
end
if __FILE__ == $0
m, n = gets.split().map(&:to_i)
puts "#{fib_partial_sum(m, n)}"
end
The last digit of any fib num repeats every 60 numbers. Therefore, we can do this, n, m = n % 60, m % 60. An improvement, but not quite there yet, fails on input 567717153638 567717153638):
def fib(n)
a = []
a << 0 << 1
(n+1).times do |i|
a << a[-1] + a[-2]
end
a[n]
end
def fib_partial_sum(m, n)
if n == m
fib(m)
else
m = m % 60
n = n % 60
m = fib(m + 1) - 1
n = fib(n + 2) - 1
(n - m) % 10
end
end
if __FILE__ == $0
m, n = gets.split().map(&:to_i)
puts "#{fib_partial_sum(m, n)}"
end

Here is a nice solution to the problem, it passes all time and memory constraints.
This way you never have to calculate a fib num greater that f(60). It can handle very large inputs efficiently.
def fib(n)
a, b = 0, 1
(n-1).times do
a, b = b, (a + b) % 10
end
b
end
def fib_partial_sum(m, n)
if n == m
fib(m % 60)
else
m = m % 60
n = n % 60
m = fib(m + 1) - 1
n = fib(n + 2) - 1
(n - m) % 10
end
end
if __FILE__ == $0
m, n = gets.split().map(&:to_i)
puts "#{fib_partial_sum(m, n)}"
end
(Max time used: 0.05/5.00, max memory used: 8699904/536870912.)

The following requires only a single pass of the numbers between zero and at most [n,m+60].min, where m..n is the range of interest, and has a minimal memory requirement. It makes use of #nloveladyallen's observation that the last digit of Fibonacci numbers has a periodicity of 60.
Code
def fib_last(m,n)
n -= 60*((n-m)/60)
fib_sum(m,n) % 10
end
def fib_sum(m,n)
return nil if m < 0 || m > n
return n if n < 2
next_to_last, last = fib(m-1)
(m..n).reduce(0) do |tot,_|
current = next_to_last + last
next_to_last = last
last = current
tot + current
end
end
def fib(m)
next_to_last, last = -1, 1
0.upto(m).each do |n|
current = next_to_last + last
next_to_last, last = last, current
end
[next_to_last, last]
end
Example
m, n = 6, 12
(n+1).times { |i| puts "#{i}: #{fib(i)}" }
0: [0, 0]
1: [0, 1]
2: [1, 1]
3: [1, 2]
4: [2, 3]
5: [3, 5]
6: [5, 8]
7: [8, 13]
8: [13, 21]
9: [21, 34]
10: [34, 55]
11: [55, 89]
12: [89, 144]
fib_last(6,12) #=> 4 (fib_sum(6,12) #=> 8 + 13 + 21 + 34 + 55 + 89 + 144 = 364)
fib_last(1,2) #=> 2 (fib_sum(1,2) #=> 1 + 1 = 2)
fib_last(1,3) #=> 4 (fib_sum(1,3) #=> 1 + 1 + 2 = 4)
fib_last(1,4) #=> 7 (fib_sum(1,4) #=> 1 + 1 + 2 + 3 = 7)
fib_last(2,3) #=> 3 (fib_sum(2,3) #=> 1 + 2 = 3)

Related

Algorithm Challenge number formatting problem

Invoice numbers are numeric only with any number of digits. To format one correctly, group the digits in group of three plus a group of any remainder, but never leave one digit by itself, unless it's a one digit number. Eg these are all correct formatting
123
12-34
6
783-907-23-45
And these are not
123-4
98-456
There's one more catch user input is passed directly to the function and you never know what characters users might type. Ignore any part of the input that is not digit
Invoice.format_number should always return a string
module Invoice
def self.format_number(str)
return ""
end
end
puts Invoice.format_number("ab1234")
What I have tried
1st approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
num_of_digits = arr.length
pairs_of_two = 0
pairs_of_three = 0
if num_of_digits > 5
while num_of_digits > 0 do
break if num_of_digits <= 3
if num_of_digits >= 3 && (num_of_digits % 3 == 0 || num_of_digits % 3 == 2)
pairs_of_three += 1
num_of_digits -= 3
elsif num_of_digits % 2 == 0 || num_of_digits % 2 == 1
pairs_of_two += 1
num_of_digits -= 2
end
end
end
2nd approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
len = arr.length - 1
if arr.length > 4
str = ""
i = 0
while i < len do
if arr[i..i+3].length == 4
str << arr[i..i+2].join + "-"
i += 3
elsif arr[i..i+2].length == 3
str << arr[i..i+1].join + "-"
i += 2
elsif arr[i..i+1].length == 2
str << arr[i..i+1].join
i += 2
elsif !arr[i].nil?
str << arr[i]
i += 1
end
end
puts str
else
if arr.length <= 3
puts arr.join
else
puts arr[0..1].join + "-" + arr[2..3].join
end
end
But none of them is correct
Here is the function invoice_number in python
def invoice_number(invoice):
s = ''.join(x for x in invoice if x <= '9' and x >= '0')
n = len(s)
if n <= 3:
return s
w = ''
i = 0
while i + 3 <= n:
for j in range(0, 3):
w += s[i + j]
i += 3
w += ('-')
m = n - i
if m == 0: return w[:-1]
if m == 1: return w[:m-3] + '-' + s[-2:]
return w + s[i:]
Testing
print(invoice_number('1234567'))
print(invoice_number('12345678'))
print(invoice_number('abc123456789'))
print(invoice_number('1234abc5678xyz9foobar'))
123-45-67
123-456-78
123-456-789
123-456-789
Eliminating non-digits is easy with re. For your format, the key is to figure our the "right" splitting indices.
Here is a try:
import re
def splits(n, k):
idx = [(i, min(n, i+k)) for i in range(0, n, k)]
if len(idx) > 1:
(a, b), (c, d) = idx[-2:]
if d - c < 2:
idx[-2:] = [(a, b - 1), (c - 1, d)]
return idx
def myformat(s):
s = re.sub(r'[^0-9]+', '', s)
parts = [s[a:b] for a, b in splits(len(s), 3)]
return '-'.join(parts)
Tests:
>>> myformat('123')
123
>>> myformat('1234')
12-34
>>> myformat('6')
6
>>> myformat('7839072345')
783-907-23-45
As the question was asked for ruby, adding solution for ruby. (The inspiration of the code is mostly from #yuri answer)
def format_invoice(invoice)
# only numbers are allowed
invoice = invoice.tr("^0-9","")
#puts invoice
return invoice if(invoice.length <= 3)
formatted_invoice = ''
i = 0
# Loop to divide the invoice in group of 3
while i + 3 <= invoice.length do
for j in 0..2 do
formatted_invoice += invoice[i + j]
end
i += 3
formatted_invoice += ('-')
end
m = invoice.length - i
return formatted_invoice[0..-2] if m == 0
return formatted_invoice[0..m-4] + '-' + invoice[-2..-1] if m == 1
return formatted_invoice + invoice[i..-1]
end
Testing
puts format_invoice('abc1') # 1
puts format_invoice('abc123') # 123
puts format_invoice('abc123A4') # 12-34
puts format_invoice('1234567') # 123-45-67
puts format_invoice('12345678') # 123-456-78
puts format_invoice('abc123456789') # 123-456-789
puts format_invoice('1234a#c5678xyz9foobar') # 123-456-789

Recursive Multiplier in ruby

I am trying to create a recursive multiplier in ruby.
#j = 0
def multiplier(x, y, z)
count = 0
if x > 0
if z > 0
#j += y
z -= 1
count += 1
multiplier(x, y, z)
else
x -= 1
z = count
p z
multiplier(x, y, z)
end
else
return #j
end
end
def main
puts "Calculation is: " + multiplier(3, 10, 4).to_s
end
main
X is how many times the multiplication happens
Y is the number we want to multiply
Z is the number we multiply by
The code should output 120 with the variables that are there
I am having issues getting Z to stay what I need it to be. Also, I'd prefer to do this without a global variable
So something like x*(y*z) but without the times symbol
The main issue with your code is count is a local variable, it is not saved between recursive calls. Also, if you want to avoid globals, pass the variable as an additional parameter in call to the function. In FP we call it accumulator:
def multiplier(x, y, z, j = 0)
if z > 0
multiplier(x, y, z - 1, j + y)
elsif z.zero? # done with z, jump to x × (y × z)
# z = -1 to explicitly mark we are done with z
multiplier(x, j, -1, 0)
elsif y.zero? # yay, we are all set, reducing is done!
j
else
# still doing x × result step
multiplier(x, y - 1, -1, j + x)
end
end
multiplier(3, 10, 4)
#⇒ 120
The above surely lacks necessary checks for input validity, but I bet you got the idea.
Building on the recursive answer above, here's a more generic function that can take an arbitrarily long list of positive integers and multiply them by recursive addition:
def multiplier(*integers, accum: 0)
if integers.size == 1
# We are finished!
integers[0]
elsif integers[-1].positive?
# "Multiply" the last two integers, by recursive addition
integers[-1] -= 1
multiplier(*integers, accum: accum + integers[-2])
else
# The above multiplication is complete; set the last integer to its result
integers[-2] = accum
multiplier(*integers[0..-2])
end
end
I would say the idiomatic way in ruby is using iterators instead of recursion/cycles
def multiplier(x, y, z)
x.times.map do
y.times.map do
z
end.reduce(:+)
end.reduce(:+)
end
or
def multiplier(x, y, z)
x.times.map do
y.times.map do
z
end
end.flatten.reduce(:+)
end
Or if the only operation is inc
def multiplier(x, y, z)
j = 0
x.times do
y.times do
z.times do
j += 1
end
end
end
j
end
the output is the same
multiplier(3, 10, 4)
# 120
For an arbitrary number of args we have to use recursion
def multiplier(*xs, res: 0)
return res if xs.empty?
xs[0].times do
res += 1 if xs.size == 1
res = multiplier(*xs.drop(1), res: res)
end
res
end
or
def multiplier(*xs, res: 0)
head, *tail = xs
head.to_i.times do
res += 1 if tail.empty?
res = multiplier(*tail, res: res)
end
res
end
I'd start by writing a method to multiply two numbers recursively:
def multiply_2(a, b)
return 0 if a.zero?
b + multiply_2(a - 1, b)
end
multiply_2(3, 4)
#=> 12
and build upon that method to multiply three numbers:
def multiply_3(a, b, c)
multiply_2(multiply_2(a, b), c)
end
multiply_3(3, 4, 10)
#=> 3
and eventually extend that to handle n numbers:
def multiply_n(a, b, *more)
result = multiply_2(a, b)
return result if more.empty?
multiply_n(result, *more)
end
multiply_n(3, 4, 10, 2)
#=> 240
Note that you might run into a SystemStackError for large numbers. This can be avoided by making multiply_2 tail-recursive (leaving that as an exercise, it's not hard) and enabling Ruby's :tailcall_optimization.
It could be written thusly.
def multiplier(*args)
prod = recurse(*args.map(&:abs))
args.count { |n| n < 0 }.even? ? prod : -prod
end
def recurse(first, *rest)
first.zero? || rest.empty? ? first : ([recurse(*rest)]*first).sum
end
multiplier(3, 10, 4) #=> 120
multiplier(3, 10, 4, 2, 3) #=> 720
multiplier(3, -10, 4) #=> -120
multiplier(3, -10, -4) #=> 120
multiplier(3, 0, 4) #=> 0
multiplier(3, 0, -4) #=> 0
Suppose we wish to compute multiplier(3, -4). recurse(3, 4) is called, where
first = 3
rest = [4]
first.zero? #=> false
rest.empty? #=> false
so we compute
([recurse(4)]*3).sum
In recurse(4),
first = 4
rest = []
As rest.empty #=> true, recurse returns first #=> 4, so
([recurse(4)]*3).sum]
#=> ([4]*3).sum => [4,4,4].sum => 12
is returned to multiplier. As [3, -4] contains an odd number of negative values, multiplier returns -12.

How does recursion work in ruby?

I have this method which switches the number digit 5 with 7.
def switch_digit(num)
if num <= 0
return 0
end
digit = num % 10
if (digit == 5)
digit = 7
end
return switch_digit(num/10) * 10 + digit
end
switch_digit(5952)
Can someone explain why once the method hits the base case it doesn't return 0?
How does this recursive method actually work? Does it append the returned digit with the next digit?
I added a little change to your code, to be aware it's working.
Finally I also expected the value of the method was 0, but it is not.
The end is reached, but the returned value is not 0. Why?
def switch_digit(num, array)
if num <= 0
array << num
p array
puts "The end"
return 0
end
digit = num % 10
array << [digit, num]
if (digit == 5)
digit = 7
end
return p switch_digit(num/10, array) * 10 + digit
end
p "returned value = " + switch_digit(123456789, Array.new).to_s
Which outputs:
#=> [[9, 123456789], [8, 12345678], [7, 1234567], [6, 123456], [5, 12345], [4, 1234], [3, 123], [2, 12], [1, 1], 0]
#=> The end
#=> 1
#=> 12
#=> 123
#=> 1234
#=> 12347
#=> 123476
#=> 1234767
#=> 12347678
#=> 123476789
#=> "returned value = 123476789"
The base case returns 0, but the overall result is determined by the return switch_digit(num/10) * 10 + digit
Follow the code through with a smaller example e.g. switch_digit(15):
num <= 0 # no
digit = num % 10 # 5
digit == 5 # yep, so swap it for a 7
return switch_digit(num/10) * 10 + 7
num/10 is 1 so what does the recursive switch_digit(1) evaluate to?
num <= 0 # no
digit = num % 10 # 1
digit == 1 # so leave it unchanged
return switch_digit(num/10) * 10 + 1
num/10 is 0 so now we hit the base case
switch_digit(15) == switch_digit(1) * 10 + 7
switch_digit(1) == switch_digit(0) * 10 + 1
switch_digit(0) == 0 # the base case
working back up, plugging in values from lower down results:
switch_digit(1) == 0 * 10 + 1 == 1
switch_digit(15) == 1 * 10 + 7 == 17
I'd also add that there's nothing specific to Ruby about how recursion is handled here. Any other descriptions of recursion, or classic example such as a recursive factorial function should help you get a better understanding.

Binary Search stuck in infinite loop

I am trying to test a binary search algorithm. The logic seems right, however, I'm running into some issues.
There are two different methods, case and if. If one method fails, the other is right.
1.
arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
case m
when m < t
l = (m + 1)
when m == t
puts "hello world"
when m > t
u = (m - 1)
end
end
end
search(arr, 5)
2.
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
if m < t
l = (m + 1)
elsif m == t
puts "hello world"
break
elsif m > t
u = (m - 1)
end
end
end
I thought my code would work, but something is causing my while loop to go on infinitely. What am I doing to cause an infinite loop?

Ruby - Why does this minor function change result in a big time difference?

This script is for Project Euler #14
I am refactoring it and all I did was grab 1 line of code n.even? ? n = n/2 : n = (3*n) + 1 and made it into it's own function. This change increase the execution time by 5 seconds.
Why would that happen?
Before:
def longest_collatz_sequence1(count)
check = []
while count >= 1
n = count
seq = [count]
while n > 1
n.even? ? n = n/2 : n = (3*n) + 1
seq << n
end
count -= 1
check << seq
value = sequence_check(check) if check.length == 2
end
puts "The number that produces the largest chain is: #{value[0][0]}"
end
def sequence_check(check)
check[0].length > check[1].length ? check.delete_at(1) : check.delete_at(0)
check
end
s = Time.new
longest_collatz_sequence1 1000000
puts "elapsed: #{Time.new-s}"
#~12.2 seconds
After:
def longest_collatz_sequence1(count)
check = []
while count >= 1
n = count
seq = [count]
while n > 1
n=collatz(n)
seq << n
end
count -= 1
check << seq
value = sequence_check(check) if check.length == 2
end
puts "The number that produces the largest chain is: #{value[0][0]}"
end
def collatz(n)
n.even? ? n = n/2 : n = (3*n) + 1
end
def sequence_check(check)
check[0].length > check[1].length ? check.delete_at(1) : check.delete_at(0)
check
end
s = Time.new
longest_collatz_sequence1 1000000
puts "elapsed: #{Time.new-s}"
#~17.7 seconds

Resources