4

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 }).

Aaron Yodaiken
  • 19,163
  • 32
  • 103
  • 184

9 Answers9

3

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(:+)
Community
  • 1
  • 1
Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
  • If you get stuck writing such a generator, have a look at the following: http://www.davidflanagan.com/2007/08/fibonacci-numbers-with-ruby-19-fibers.html – Michael Kohl Jun 13 '10 at 14:54
  • 1
    @Michael: oh my! That post is a _terrible_ use of `Fiber` and way too complex (let alone slow). If you need something like it, write a good enumerable (have a look at the triangular number generator I pointed to) and get an `Enumerator` and use `next`, but even that should be avoided. – Marc-André Lafortune Jun 13 '10 at 17:22
  • I post my version below. Pls, check it. Is it what you mean? – ceth Mar 22 '11 at 10:55
  • @Marc-AndréLafortune why do you pass `i` to the block in `1.upto(Float::INFINITY) do |i|`? It doesn't look like you are using it... also, can you explain how the Float::INFINITY works here? To me it reads "do something forever" but obviously I am wrong. – Mohamad Dec 23 '14 at 15:55
  • @Mohamad This was from dema's answer below, but you are right this can be simplified further. I've edited my answer accordingly. No you are right, it is an infinity loop, but using it as an enumerable makes it easy to take only a few of the items. – Marc-André Lafortune Dec 24 '14 at 19:52
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
kgthegreat
  • 1,172
  • 9
  • 10
2

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
Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
ceth
  • 44,198
  • 62
  • 180
  • 289
1

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(:+)
tibbon
  • 1,018
  • 2
  • 16
  • 30
1

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.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
1
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 }
user868386
  • 401
  • 1
  • 5
  • 9
0

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}"
0

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
zed_0xff
  • 32,417
  • 7
  • 53
  • 72
  • If you start with `@p0 = 1`, you can get rid of your `if`... Also, Ruby has parallel assignment which can be useful here. Did you know that `2.even?` returns `true`? – Marc-André Lafortune Jun 13 '10 at 17:42
  • I know that. I just posted my archive code from my Project Euler tasks archive. I wrote this a long ago. – zed_0xff Jun 13 '10 at 18:49
0
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
KellysOnTop23
  • 1,325
  • 2
  • 15
  • 34