web-dev-qa-db-ja.com

フィボナッチワンライナー

Project Euler in Ruby one-linersからの質問を解決しようとしていますが、 質問2)のよりエレガントな解決策があるかどうか知りたいです

フィボナッチ数列の新しい各項は、前の2つの項を追加することによって生成されます。 1と2から始めると、最初の10の用語は次のようになります。

1、2、3、5、8、13、21、34、55、89、.。

値が400万を超えないフィボナッチ数列の項を検討することにより、偶数値の項の合計を求めます。

これがRubyでの私の1行のソリューションです:

(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}

ここでの私の主な懸念は、範囲(1..32)を使用しているのは、フィボナッチ数列の数値が4,000,000を超え始めるまでに必要なのはそれだけであることがわかったからです。どういうわけかこれを一行に組み込んでほしいのですが、なかなかわかりません。

セミコロンは使用できません!

23
clem

アレックスの答えに触発されました:

# 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
11
Sony Santos

これに対する私のお気に入りの解決策は、ハッシュを使用することです。ハッシュの値は、無名関数によって決定できます。

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

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

それは「本物の」ワンライナーであり、非常にルビーっぽいです。

75
Alex Reisner

Ruby 1.9列挙子の使用:

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]

一行として:

p Enumerator.new { |yielder| i, j = 0, 1; loop {i, j = j, i + j; yielder.yield i} }.take_while { |n| n <= 4E6}
19
Wayne Conrad

私のお気に入りは:

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

から https://Gist.github.com/1007228

8

これはどう?

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

この回答を参照 説明が必要です。)

6
Gareth Rees

これがRuby 2.0ソリューションで、怠惰ではないinject/reduceを使用していません。

(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(&:+)

最初の0が含まれていないため、フィボナッチジェネレーターは特に好きではありません。このソリューションでは、最初の奇数がFであることも利用しています。3 (F1 このシーケンスジェネレーターで)。

よりクリーンな(フィボナッチに関して)そして正しい(Liber Abaciの定義では)解決策は次のようになります:

(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(&:+)

このソリューションにはセミコロンが含まれていますが、このように使用した場合にカウントされるかどうかはわかりません:)。

[更新]

これがセミコロンのない適切なフィボナッチジェネレーター(0から始まる)ソリューションです(ところで、これはjavascriptのセミコロン戦争ですか?!?):)

(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(&:+)
5
luismreis
_puts (1..20).inject([0, 1]){|Fibonacci| Fibonacci << Fibonacci.last(2).inject(:+) }
_

これは、injectキーワードを使用してフィボナッチ数列を印刷するために使用した中で最高のソリューションです。説明:1).inject([0,1])は、シリーズのコレクション(1)要素のデフォルト値(0)の最初の値を保持します。 2)最初にフィボナッチオブジェクトは0、1を持ち、Fibonacci.last(2)を使用して注入を通過します3).inject(:+)は0+ 1を追加します4)これは0 + 1 = 1を追加します次に、Fibonacciにプッシュされ、外側のinject([0,1])での次の反復でinject(1,2)になります。ここで、1は合計(0 + 1)の後の値で、2はコレクションの次の反復値。収集が終了するまでなど

だからシリーズは次のようになります

_0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
_
3
susheel

これは古代の質問であり、回答済みとして分類されていますが、誰も1つのブロックで質問を解決することはできません。実際には、1行1ブロックで、セミコロンなしで偶数の項の合計を与えるものはありません。 (ウェインは1行で解決することに気づきましたが、arothに応じて1ブロックの解決策が良いかもしれないと思いました)。これが行う解決策です:

(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 }

セミコロンが1つある少しクリアなバージョンの場合。

(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 }

私もそれを説明すると思います。3つの情報が配列内で(各反復でaとして)最初のフィボナッチ数、2番目のフィボナッチ数、および偶数項の合計として繰り越されます。これを念頭に置いて、このコードは非常に明確なRubyだと思います。

これは、1つのブロックを除いて、基本的にclemsと同じであることに注意してください。

3
Mike H-R

Alex's Hashに基づいて構築すると、盲目になる可能性がありますが、これは1行で、セミコロンがなく、範囲の依存関係がなくなります。 instance_evalトリックは、恐ろしい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)}

出力:4613732

恐ろしいことを警告しました。申し訳ありませんが、セミコロンを使用せずに実際に値を返すようにすることはできません。

3
Ben Nagy

単なる概算を超えて、Fib(70)までの正しい値を返します。しかし、非常に高速です。

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

(説明については https://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding を参照してください)

2
spickermann

今のところ、フィボナッチの目標を達成するための4つの方法が考えられます。

  1. スタビーラムダの使用:
_puts 'Fibonacci Sequence in a Line: ', ->(a=1, b=0) { 10.times.collect { (a, b = b, a + b)[0] } }.call
_

これは10シリーズを評価します。ただし、ユーザーの番号を取得する場合は、次のようにします。

_puts 'Fibonacci Sequence in a Line: ', ->(a=1, b=0) { gets.to_i.times.collect { (a, b = b, a + b)[0] } }.call
_
  1. tapメソッドの使用:
_[0, 1].tap { |a| 10.times { a.Push(a[-1] + a[-2]) } }
_
  1. _reduce / inject_メソッドの使用:
_(1..10).reduce([0, 1]) { |a| a.Push(a.last(2).sum) }
_

または

_10.times.reduce([0, 1]) { |a| a.Push(a.last(2).sum) }
_
  1. _each_with_object_または_map.with_object_メソッドの使用:
_10.times.each_with_object([0, 1]) { |_, a| a.Push(a.last(2).sum) }
_

注:Ruby 2.4+がない場合は、sumメソッドがない可能性があります。その場合、最後の2つの要素を_ary[-2] + ary[-1]_またはary.last(2).reduce(:+)

あなたの問題に来る:

値が400万を超えないフィボナッチ数列の項を検討することにより、偶数値の項の合計を求めます。

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

または(これはそれほど素晴らしいことではありません):

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

出力:4613732

お役に立てれば!

1
S.Goswami

Ruby 2.0の新しいレイジーでは、次のように書くことができます。

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(:+)
1
lyuehh

上記の答えの要約ソリューションとして、私の謙虚な追加で:

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(:+)

カリー化の見た目はあまり好きではありませんが、他の答えと同じように見せたくありませんでした。代替案take_while念のため:

  take_while { |value| value < 4*10**6 }.
1
phil pirozhkov

シンプルでエレガントが最善の方法ですよね?

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

出力:

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

これが1行ですRubyオイラー確率#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(:+)

または読みやすさを向上させるために??

(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(:+)
0
Justin

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

0