Project Euler #6 in Ruby (What's the difference?) - ruby

I solved #6 for Project Euler using two different methods, but the second one just happened to be coincidental. I don't really understand why I don't have to return the square of new_sum like I did for my first method. Does anyone know what's the major difference between the two?
def square_difference(num)
new_sum = 0
sum = 0
while num >= 1
sum += num**2 && new_sum += num
num -= 1
end
return new_sum**2 - sum
end
AND
def square_difference(num)
new_sum = 0
sum = 0
while num >= 1
new_sum += num && sum += num**2
num -= 1
end
return new_sum - sum
end

The precedence of && is higher than that of +=. So these expressions:
sum += num**2 && new_sum += num
new_sum += num && sum += num**2
don't work as you expected. The first one just happen to provide the seemingly "correct" result.
Divide them into separate lines and you'll see the difference. Or, at least use and instead of &&. and has a lower precedence than that of +=.

Both formulas have the same subtle bug. I stepped through it a couple of times to understand what was going on.
From the second one:
[30] pry(main)> square_difference(4)
new_sum: 16, sum: 16, num: 3
new_sum: 41, sum: 25, num: 2
new_sum: 70, sum: 29, num: 1
new_sum: 100, sum: 30, num: 0
=> 70
We can see that new_sum does not seem to be behaving as intended.
What is actually happening is that new_sum += num && sum += num**2 is being evaluated to new_sum += (num && sum += num **2), which in turn evaluates to new_sum += (sum += num **2)
This is a result of the && operator, which has higher precedence (as Yu Hao pointed out) and returns the first value which determines whether the AND condition is satisfied.
[31] pry(main)> 2 && 2
=> 2
[32] pry(main)> 2 && 4
=> 4
[33] pry(main)> 4 && 2
=> 2
[34] pry(main)> nil && 2
=> nil
[35] pry(main)> 2 && nil
=> nil

Related

Coding Challenge 2

Find the element in an array which has consecutive plus or minus 1. I can achieve in O(n) time by doing a linear search. Is there any efficient way to achieve in less than O(n) time.
What I mean by plus or minus 1 is 2 consecutive number in an array will have either -1 or 1 as the difference
For example sample array inputs
Consecutive element have +1 of difference
arr = [1,2,3,4,5]
Consecutive elements have -1 of difference
arr = [ -1,-2, -3, -4 , -5, ]
Elements have either +1 or -1 of difference
arr = [-5,-4,-3,-4, -3,-2,-1,0,1,2,3,4,3,2,1]
Note: Array is unsorted
Example
arr = [-5,-4,-3,-4,-3,-2,-1,0,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1,0,-1,-2,-3,-4,-3,-2,-1]
while i <= arr.length do
if num == a[i]
puts "found"
break
end
i += 1
end
2nd approach which in my mind is instead of starting from 0 index I have done some custom logic
num = gets.to_i
i = 0
if (num < 0 && arr[0] < 0) || (num > 0 && arr[0] < 0)
i = (num + arr[0].abs).abs
elsif num < 0 && arr[0] > 0
i = num.abs + arr[0]
elsif num > 0 && arr[0] > 0
i = (num > arr[0] ? (num - arr[0]) : (arr[0] - num))
end
while i <= arr.length do
if num == a[i]
puts "found"
break
end
i += 1
end
As Nico Schertler said, O(n) is the best you can do. This is a find operation, and find operations on unsorted data are always O(n), because in the worst case they must check every element. This would be different if your data were sorted. See binary search.
Note also that your example code does not do what you're asking - it simply finds some number num in the list. Your example code is equivalent to:
arr.find { |i| i == num }
What you appear to be asking for is
arr.find { |i| i == num - 1 or i == num + 1 }

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.

Partial Fibonacci Sum, how to improve performance?

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)

Ruby FizzBuzz using arrays, my logic seems right but it is getting an error

FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)
Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.
You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end
#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]

ruby inject with conditional in block?

doing the first Project Euler question: summing the multiples of 3 and 5 between 1 and 1000, I came up with this (pretty simple)
sum = 0
1.upto(999) { |i| sum += i if 0 == i%3 || 0 == i%5 }
sum
but I thought this would work but it doesn't, can someone show me what I'm doing wrong, or why it doesn't work?
1.upto(999).inject(0) { |sum, i| sum + i if 0 == i%3 || 0 == i%5 }
thanks!
inject passes the result of the block through to the next iteration as the first argument. Your block will return nil when your if statement is false, which then gets passed back in as sum.
To get the correct answer, the block should return the current sum when it's false:
1.upto(999).inject(0) { |sum, i| (0 == i%3 || 0 == i%5) ? sum + i : sum }
Complementary answer: if you're about to tackle Euler problems you should start to build your own extensions of reusable code. In this case, the first extension would be Enumerable#sum:
module Enumerable
def sum
inject(0, :+)
end
end
And now you can write a solution that separates the condition of the summatory (you can read it out loud and it makes sense, that's typical of functional/declarative style):
1.upto(999).select { |x| x % 3 == 0 || x % 5 == 0 }.sum
You could even push it one step further and create Fixnum#divisible_by? so you can write:
1.upto(999).select { |x| x.divisible_by?(3) || x.divisible_by?(5) }.sum
More: here it's not a problem, but later on strict implementations (those using arrays) will require too much memory. Try then with laziness:
require 'lazy'
1.upto(999).lazy.select { |x| x % 3 == 0 || x % 5 == 0 }.sum
1.upto(999).inject(0) { |sum, i| sum += i if 0 == i%3 || 0 == i%5; sum }
Would also work (note the +=).
Or, use & proc which addresses self.
(1..999).select{|x| x%3==0||x%5==0}.inject &:+
(1..999).to_a.keep_if{|d| d%3 == 0 || d%5 == 0}.reduce(:+) for completeness.

Resources