Fibonacci One-Liner

Viewed 22468

I'm trying to solve questions from Project Euler in Ruby one-liners, and I'm curious if there's a more elegant solution for question two:

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

Here is my one line solution in Ruby:

(1..32).inject([0,1]) {|arr, i| (arr << arr[-1] + arr[-2] if arr[-1] + arr[-2] <= 4000000) || arr}.inject(0) {|total, i| total += i.even? ? i : 0}

My main concern here is that I'm using the range (1..32) only because I happen to know that that's all that's necessary until numbers in the Fibonacci sequence begin to exceed 4,000,000. I would prefer that this be built into the one-line somehow, but I haven't been able to figure it out.

Semi-colons are not allowed!

17 Answers

I can think of 4 ways for now to achieve the fibonacci goal!

  1. Using a stabby lambda:
puts 'Fibonacci Sequence in a Line: ', ->(a=1, b=0) { 10.times.collect { (a, b = b, a + b)[0] } }.call

This evaluates 10 series. But if you want to get the user's number:

puts 'Fibonacci Sequence in a Line: ', ->(a=1, b=0) { gets.to_i.times.collect { (a, b = b, a + b)[0] } }.call
  1. Using the tap method:
[0, 1].tap { |a| 10.times { a.push(a[-1] + a[-2]) } }
  1. Using the reduce / inject method:
(1..10).reduce([0, 1]) { |a| a.push(a.last(2).sum) }

or

10.times.reduce([0, 1]) { |a| a.push(a.last(2).sum) }
  1. Using the each_with_object or map.with_object method:
10.times.each_with_object([0, 1]) { |_, a| a.push(a.last(2).sum) }

Note: If you don't have Ruby 2.4+ you may not have the sum method. In that case, you can add the last two elements with ary[-2] + ary[-1] or ary.last(2).reduce(:+).

Coming to your problem:

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

[0, 1].tap { |a| until (s = a.last(2).sum) > 4_000_000 do a.push(s) end }.select(&:even?).sum

Or (which is not that great):

[0, 1].tap { |a| loop while a.push(a.last(2).sum)[-1] < 4_000_000 }.tap(&:pop).select(&:even?).sum

Outputs: 4613732

Hope this helps!

Simple and elegant is the best way, right?

a0 = 1; a1 = 1; 20.times {|i| b = a0 + a1; a0 = a1; a1 = b; puts b };

Output:

2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
=> 20
Related