24

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!

Community
  • 1
  • 1
clem
  • 3,524
  • 3
  • 25
  • 41
  • 4
    I think it's subverting the spirit of your challenge a bit if the "one-liner" solutions include multiple blocks. I mean, you could do a Java one-liner the same way, if you didn't mind having a line that was 500 characters long and completely unreadable. – aroth Jun 20 '11 at 23:07
  • It's nothing to do with Ruby specifically, that's just the language I'm learning. It's just for fun. – clem Jun 20 '11 at 23:16
  • @aroth, Chaining blocks in Ruby is as natural as an assignment with multiple arithmetic operators. For a one-liner which bends the spirit of the rules more, see my solution: the semicolons are a dead givaway. – Wayne Conrad Jun 21 '11 at 00:26
  • 1
    @Wayne - If chaining blocks in Ruby is always done by using a single line of code, then all I can say is ugh...I will never understand why seeming rational people take a practice that needlessly obfuscates code and make it "natural". Part of the design philosophy behind Ruby as a language was that it should be easy for a human to read and understand, and of your two example solutions the multi-line one is by far the most readable. – aroth Jun 21 '11 at 00:40
  • 2
    @aroth, I agree. I don't chain blocks on one line unless it's more readable. Sometimes it is, often it isn't. The one-liner in my example is because the OP asked for it, not because it's what I'd write. That said, writing one-liners _is_ a valid exercise, like a musician playing musical scales. You wouldn't write one liners in production code, nor would you play musical scales in a concert. – Wayne Conrad Jun 21 '11 at 01:12
  • @aroth a one block solution for you sir. – Mike H-R Jun 25 '13 at 12:32

17 Answers17

79

My favorite solution to this is to use a Hash, the values of which can be determined by an anonymous function:

fibonacci = Hash.new{ |h,k| h[k] = k < 2 ? k : h[k-1] + h[k-2] }

fibonacci[6]  # => 8 
fibonacci[50] # => 12586269025

It's a "genuine" one-liner and very Ruby-ish.

Alex Reisner
  • 29,124
  • 6
  • 56
  • 53
  • 4
    Very clever! But you're using a colon - isn't that twice as bad as using a semicolon? – Andrew Grimm Aug 10 '11 at 22:29
  • 4
    Bravo! You get automagic memoization by using the hash to drive the function. This is elegance! – Patrick Farrell Aug 20 '12 at 02:24
  • 3
    `fibonacci[2299] # => stack level too deep (SystemStackError)` – Amit Kumar Gupta Dec 14 '13 at 21:14
  • 3
    I'd like to highlight the performace difference between both approaches. Hash: `code`fibonacci = Hash.new{ |h,k| h[k] = k < 2 ? k : h[k-1] + h[k-2] } puts fibonacci[30] 832040 [Finished in 0.1s] `code` Lambda: `code` fibo = lambda { |x| (x<2) ? x : fibo.call(x-1) + fibo.call(x-2) } 832040 [Finished in 1.7s] `code` – DavidSilveira Sep 28 '14 at 20:08
  • p fibonacci # {1=>1, 0=>0, 2=>1, 3=>2, 4=>3, 5=>5, 6=>8, 7=>13, 8=>21, 9=>34, 10=>55, 11=>89, 12=>144, 13=>233, 14=>377, 15=>610, 16=>987, 17=>1597, 18=>2584, 19=>4181, 20=>6765, 21=>10946, 22=>17711, 23=>28657, 24=>46368, 25=>75025, 26=>121393, 27=>196418, 28=>317811, 29=>514229, 30=>832040, 31=>1346269, 32=>2178309, 33=>3524578, 34=>5702887, 35=>9227465, 36=>14930352, 37=>24157817, 38=>39088169, 39=>63245986, 40=>102334155, 41=>165580141, 42=>267914296, 43=>433494437, 44=>701408733, 45=>1134903170, 46=>1836311903, 47=>2971215073, 48=>4807526976, 49=>7778742049, 50=>12586269025} – jasonleonhard Jul 07 '15 at 17:34
  • This is by far the fastest. And unbelievably fast too. – look Aug 28 '15 at 05:13
  • 2
    Without a colon: `fib = Hash.new { |fib, n| fib[n] = fib[n - 1] + fib[n - 2] }.merge!(0 => 0, 1 => 1)` – Dennis Nov 22 '15 at 00:52
  • can any one please explain how this piece of code wokrs – YasirAzgar Nov 06 '16 at 18:05
19

Using a Ruby 1.9 Enumerator:

fib = Enumerator.new do |yielder|
  i = 0
  j = 1
  loop do
    i, j = j, i + j
    yielder.yield i
  end
end

p fib.take_while { |n| n <= 4E6 }
# => [1, 1, 2 ... 1346269, 2178309, 3524578]

As one line:

p Enumerator.new { |yielder| i, j = 0, 1; loop {i, j = j, i + j; yielder.yield i} }.take_while { |n| n <= 4E6}
Wayne Conrad
  • 103,207
  • 26
  • 155
  • 191
  • 1
    A while ago I found out about the strange construct `i=j+j=i`. It's the same thing as `i, j = j, i + j`. – Jonas Elfström Jan 29 '13 at 19:43
  • @Jonas, Did you mean `j = i + i = j`? It's an interesting construct! It's not one I'd like to use in production code, but a good one for thinking about how Ruby works. Thank you for pointing it out. – Wayne Conrad Jan 29 '13 at 20:12
  • I wouldn't use it in production code either and me confusing them is a good indicator of why. – Jonas Elfström Jan 30 '13 at 12:28
11

Inspired on Alex's answer:

# Ruby 1.8.7
f = lambda { |x| x < 2 ? x : f.call(x-1) + f.call(x-2) }
puts f.call(6)   #=> 8

# Ruby 1.9.2
f = ->(x){ x < 2 ? x : f[x-1] + f[x-2] }
puts f[6]        #=> 8
Sony Santos
  • 5,435
  • 30
  • 41
  • 4
    `fib = ->(n, i=0, j=1){(1..n).map{i=j+j=i}}` Call it with `fib[7]` – Jonas Elfström Jan 29 '13 at 19:35
  • I wonder why the syntax you used seems to have disappeared in Ruby 1.9.3. – Jonas Elfström Jan 29 '13 at 19:37
  • @JonasElfström, you must put your code as an answer, it deserves a +1! :) I'd add a `[n-1]` before the last `}` to get the same result type (number instead of array). I'll adapt my answer to 1.9.3, thank's for the tip. – Sony Santos Jan 30 '13 at 12:15
  • @JonasElfström The syntax I've used for 1.9.2 didn't work in 1.9.2. The truth is: I thought that `->` was replacing the `lambda` keyword and the rest would be the same, and I didn't test the code in 1.9.2 (I was not using [rvm](https://rvm.io/) at that time). Now it's correct. Thank you! – Sony Santos Jan 30 '13 at 12:33
  • 1
    I missed that the answer should be the n:th Fibonacci number and not the sequence itself. Here's another silly lambda for that `fib = ->(n, i=0, j=1){n.times{i=j+j=i};i}` Since `i=j+j=i` is such a quirky construct I don't think it deserves being an answer. – Jonas Elfström Jan 30 '13 at 12:36
  • @JonasElfström `.last` is better than `[n-1]`, but your `;i` is far more interesting. :) Hey, this question is for one-liners, it's hard to not being quirky. – Sony Santos Jan 30 '13 at 12:37
  • sorry @JonasElfström is there any chance you can explain your code? I'm convinced there's genius in there but it's hidden within madness. could you explain what `;i` does and why `i=j+j=i` works? also out of interest where did you come by such arcane and mystical knowledge? :) – Mike H-R Oct 18 '13 at 00:45
  • 1
    It's an offensive way to exploit operator precedence order. Try this is irb. `i=0;j=1` then run `i=j+j=i; puts "i:#{i}, j:#{j}"` a couple of times. – Jonas Elfström Oct 18 '13 at 07:53
  • 2
    `i=j+j=i` is evaluated from left to right. Starting point `i=0;j=1`. First `i` will be set to 1 + 0 since = precedes +. Now `i=1;j=0`. `i` will be set to 0 + 1. We arrive at `i=1;j=1`. `i` will be set to 1 + 1. `i=2;j=1`. `i` will be set to 1 + 2. `i=3;j=2`. `i` will be set to 2 + 3. `i=5;j=3` and so on, thus producing the Fibonacci sequence. – Jonas Elfström Oct 18 '13 at 08:04
  • 1
    @MikeH-R ...and the `;i` is synonym for `; return i`. – Sony Santos Oct 18 '13 at 11:30
  • 1
    ahh, so it is functionally equivalent to `i,j = j, j+i` since the exact same operations are performed, cool. thanks for the explanation. also I see now, of course it's a return i, that makes sense. incidentally, I couldn't ask why that's necessary, could I? shouldn't the inside block (the times one) return i as it's final value by default? instead it seems to return n, which is obviously why you fix it with the `;i`. Am I mistaken in remembering that blocks return the last value in them? – Mike H-R Oct 18 '13 at 12:25
  • hmmm. So playing around in IRB it looks like a blocks return value isn't the last value in it, but [here](http://rubylearning.com/satishtalim/ruby_blocks.html) and my memory say that it is (from the link: "A code block's return value (like that of a method) is the value of the last expression evaluated in the code block.") can someone explain why I'm mistaken? – Mike H-R Oct 18 '13 at 12:33
  • oh and code saying that blocks don't return the last expression: `a=10.times {i=j+j=i} #returns 10`, `(1..10).each {i=j+j=i} # returns (1..10)` e.t.c. with some more things I tried. – Mike H-R Oct 18 '13 at 12:36
  • 1
    Array#each returns the array and Integer#times returns self. http://ruby-doc.org/core-2.0.0/Array.html#method-i-each http://www.ruby-doc.org/core-2.0.0/Integer.html#method-i-times – Jonas Elfström Oct 23 '13 at 20:53
  • @MikeH-R Your comments made me "improve" it. `->(✌, ❤=0, ♥=1){(1..✌).map{❤=♥+♥=❤}}[8]` :) – Jonas Elfström Oct 24 '13 at 07:14
  • In a regular laptop at least it doesn't perform well at all on values above 35. f[35] – s1mpl3 Oct 26 '17 at 05:12
8

My favorite is:

def fib(n)
  (0..n).inject([1,0]) { |(a,b), _| [b, a+b] }[0]
end

from https://gist.github.com/1007228

6

How about this?

(((1 + 5 ** 0.5) / 2) ** 35 / 5 ** 0.5 - 0.5).to_i / 2

(See this answer for an explanation.)

Community
  • 1
  • 1
Gareth Rees
  • 64,967
  • 9
  • 133
  • 163
  • I like this solution, but wouldn't you have to know the significance of why you are using 35? Given the Euler project questions, you really only know the number 4000000 and to sum the even numbers. – pjammer Dec 23 '12 at 03:59
  • 1
    @pjammer: 35 can be replaced with `(Math.log(4000000 * 5 ** 0.5, (1 + 5 ** 0.5) / 2) + 2).to_i`, if you like. – Gareth Rees Jan 05 '13 at 13:06
5

Here's a ruby 2.0 solution, without using inject/reduce which is not lazy:

(1..Float::INFINITY).
  lazy.
  with_object([0,1]).
  map { |x, last| last[1] = last[0] + (last[0] = last[1]) }.
  select { |x| x % 2 == 0 }.
  take_while { |x| x < 4_000_000 }.
  reduce(&:+)

I don't particularly like the fibonacci generator, because it doesn't include the initial 0. This solution also takes advantage of the first odd number being F3 (F1 in this sequence generator).

A cleaner (Fibonacci-wise) and correct (In Liber Abaci's definition) solution would be:

(1..Float::INFINITY).
  lazy.
  with_object([0,1]).
  map { |x, last| last[1] = last[0] + (last[0] = last[1]);last[0] }.
  select { |x| x % 2 == 0 }.
  take_while { |x| x < 4_000_000 }.
  reduce(&:+)

This solution includes a semi-colon, but I don't know if it counts when used this way :).

[Update]

Here's a proper Fibonacci generator (starting on 0) solution, with no semi-colon (btw, is this a javascript semi-colon wars thingy ?!?) :)

(1..Float::INFINITY).
  lazy.
  with_object([0,1]).
  map { |x, last| last[0].tap { last[1] = last[0] + (last[0] = last[1]) } }.
  select { |x| x % 2 == 0 }.
  take_while { |x| x < 4_000_000 }.
  reduce(&:+)
luismreis
  • 4,288
  • 2
  • 19
  • 11
3

I realize this is an ancient question and has been classed as answered but no-one manages to solve the question in one block, none of them actually give the sum of the even valued terms in one line and in one block and with no semi colons (just noticed that waynes does solve with one line but I thought a one block solution might be nice in response to aroth). here is a solution that does:

(1..Float::INFINITY).inject([0,1,0]){|a| if a[0]+a[1] < 4000000 then [a[1],a[0]+a[1],(a[0]+a[1]).even? ? a[2] + (a[0]+a[1]) : a[2]] else break a[2] end }

for a slightly clearer version with one semi colon.

(1..Float::INFINITY).inject([0,1,0]){|a| sum=a[0]+a[1]; if sum < 4000000 then [a[1],sum,sum.even? ? a[2] + sum : a[2]] else break a[2] end }

I figure I'll explain it too, three pieces of information get carried forward in the array (as a at each iteration) the first fibonacci number, the second fibonacci number and the sum of the even terms. bearing this in mind I think this code is quite clear ruby.

it should be noted that this is basically the same as clems except in one block

Mike H-R
  • 7,726
  • 5
  • 43
  • 65
  • And afaik it seems to be efficient and memoises the information without storing too much of it (so both speed and memory efficient) – Mike H-R Jun 25 '13 at 12:36
  • +1 for pointing out what the rest of missed: We were so focused on the fib. generator that we forgot to add that last bit that the OP asked for. – Wayne Conrad Feb 14 '16 at 12:57
3
puts (1..20).inject([0, 1]){|Fibonacci| Fibonacci << Fibonacci.last(2).inject(:+) }

This is the best solution I ever had used to print the Fibonacci series using inject keyword. Explanation: 1) .inject([0,1]) will hold the default value (0) first value of collection (1) element of the series. 2) At first Fibonacci object will have 0, 1 using Fibonacci.last(2) that will be passed through inject 3) .inject(:+) will add the 0+1 4) This will add 0+1 = 1 and then will be pushed to Fibonacci which on next iteration with outer inject([0,1]) will become inject(1,2) here 1 is the value after sum (0+1) and 2 is the next iteration value of collection. and so on till the end of collection

So the series will be like

0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
susheel
  • 93
  • 10
3

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!

15 Volts
  • 1,946
  • 15
  • 37
3

Building on Alex's Hash, this may make you go blind, but it's one line, no semicolons and eliminates the range dependency. the instance_eval trick is very useful for oneliners and golf, although it's horrible Ruby.

Hash.new{|h,k|h[k]=k<2?k:h[k-1]+h[k-2]}.update(sum: 0,1=>1).instance_eval {self[:sum]+= self[keys.last+1].even? ? self[keys.last] : 0 while values.last < 4E6 || puts(fetch :sum)}

Outputs: 4613732

I warned you it was horrible. I can't make it actually return the value without using a semicolon, sorry.

Ben Nagy
  • 66
  • 1
2

Returns correct values up to Fib(70), beyond that just an approximation. But extremely fast:

(((Math.sqrt(5.0) + 1.0) / 2.0)**n / Math.sqrt(5.0) + 0.5).floor

(see https://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding for explanation)

spickermann
  • 100,941
  • 9
  • 101
  • 131
1

With the new lazy in ruby 2.0, you can write like this.

puts (1..Float::INFINITY).lazy.map{|n| (0..n).inject([1,0]) {|(a,b), _| [b, a+b]}[0] }.take_while{|n| n < 4000000}.select{|x| x % 2 == 0}.reduce(:+)
lyuehh
  • 62
  • 6
1

As a summarizing solution for the answers above, with my humble additions:

32.
  times.
  lazy.
  with_object([0, 1]).map { |_, fib| fib[1] = fib[0] + fib[0] = fib[1]; fib[0] }.
  take_while(&:>.to_proc.curry(2)[4*10**6]).
  select(&:even?).
  inject(:+)

I don't really like how currying looks, but didn't want it to look similar to other answers. Alternative take_while just for the case:

  take_while { |value| value < 4*10**6 }.
phil pirozhkov
  • 4,740
  • 2
  • 33
  • 40
0

(1..32).inject([0, 1]) { |fib| fib << fib.last(2).inject(:+) }

Vadim Sergeevich
  • 143
  • 1
  • 10
0

Here's a one line ruby solution to Euler prob #2

(0..4000000).take_while{|i| (0..i).reduce([1,0]){|(a,b), _| [b, a+b]}[0] <= 4000000 }.map{|i| (0..i).reduce([1,0]){|(a,b), _| [b, a+b]}[0] }.select{|i| i%2 == 0}.reduce(:+)

Or for better readability??

(0..4000000) .
take_while {|i| (0..i).reduce([1,0]){|(a,b), _| [b, a+b]}[0] <= 4000000} .
map {|i| (0..i).reduce([1,0]){|(a,b), _| [b, a+b]}[0]} .
select {|i| i%2 == 0} .
reduce(:+)
Justin
  • 555
  • 4
  • 22
-1

Here is my one liner, with the @fib table being populated as we get the method returns..

@fib=[0,1];def fib num; return 0 if num < 0; @fib[num]||=fib(num-1)+fib(num-2);end
ufo2mstar
  • 1
  • 4
-1

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
Kiryl Plyashkevich
  • 2,157
  • 19
  • 18