Ответ 1
Вдохновленный Алексом:
# 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
Я пытаюсь решить вопросы из Project Euler в однострочном пространстве Ruby, и мне любопытно, есть ли более элегантное решение для вопрос два:
Каждый новый член последовательности Фибоначчи генерируется добавлением двух предыдущих членов. Начиная с 1 и 2, первые 10 членов будут:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89,...
Рассматривая члены в последовательности Фибоначчи, значения которых не превышают четырех миллионов, найдем сумму четных членов.
Вот мое однострочное решение в 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}
Моя главная проблема заключается в том, что я использую диапазон (1..32) только потому, что я знаю, что все, что необходимо, до тех пор, пока числа в последовательности Фибоначчи не превысят 4 000 000. Я бы предпочел, чтобы это было встроено в одну строку, но я не смог понять это.
Полуколоны не допускаются!
Вдохновленный Алексом:
# 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
Моим любимым решением для этого является использование хэша, значения которого могут быть определены анонимной функцией:
fibonacci = Hash.new{ |h,k| h[k] = k < 2 ? k : h[k-1] + h[k-2] }
fibonacci[6] # => 8
fibonacci[50] # => 12586269025
Это "настоящий" однострочный и очень рубиновый.
Использование 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]
В качестве одной строки:
p Enumerator.new { |yielder| i, j = 0, 1; loop {i, j = j, i + j; yielder.yield i} }.take_while { |n| n <= 4E6}
Мое любимое занятие:
def fib(n)
(0..n).inject([1,0]) { |(a,b), _| [b, a+b] }[0]
end
Как насчет этого?
(((1 + 5 ** 0.5) / 2) ** 35 / 5 ** 0.5 - 0.5).to_i / 2
(См. этот ответ для объяснения.)
Здесь используется рубиновый 2.0-решение без использования инъекции/уменьшения, которое не является ленивым:
(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 (F 1 в этом генераторе последовательности).
Очиститель (Фибоначчи) и правильное (в определении 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), без полуколонии (btw, является ли это секьюрити-двоеточие javascript 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(&:+)
Основываясь на Alex Hash, это может заставить вас ослепнуть, но это одна строка, точки с запятой и исключает зависимость диапазона. трюк instance_eval очень полезен для oneliners и гольфа, хотя это ужасно 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
Я предупреждал вас, что это было ужасно. Я не могу заставить его фактически вернуть значение, не используя точку с запятой, извините.
Я понимаю, что это древний вопрос и был классифицирован как ответ, но никто не может решить вопрос в одном блоке, ни один из них фактически не дает сумму четных членов в одной строке и в одном блоке и с нет полуколонок (только заметили, что waynes разрешает с одной строкой, но я думал, что одноблочное решение может быть приятным в ответ на атро). вот решение, которое делает:
(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..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 }
Я полагаю, что я тоже объясню, три массива информации передаются в массиве (как a
на каждой итерации) первое число фибоначчи, второе число фибоначчи и сумма четных членов. имея это в виду, я думаю, что этот код довольно четкий рубин.
следует отметить, что это в основном то же самое, что и clems, за исключением одного блока
Возвращает правильные значения до 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 для объяснения)
puts (1..20).inject([0, 1]){|Fibonacci| Fibonacci << Fibonacci.last(2).inject(:+) }
Это лучшее решение, которое я когда-либо использовал для печати серии Fibonacci с использованием ключевого слова для инъекций.
Объяснение:
1) .inject([0,1])
будет удерживать значение по умолчанию (0) первого значения элемента коллекции (1) в ряду.
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
С новым ленивым в 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(:+)
Как обобщающее решение для ответов выше, с моими скромными дополнениями:
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(:+)
Мне не нравится, как выглядит currying, но не хотел, чтобы он выглядел похожим на другие ответы. Альтернатива take_while
только для случая:
take_while { |value| value < 4*10**6 }.
Здесь однострочное рубиновое решение для 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(:+)
Или для лучшей читаемости
(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(:+)
(1..32).inject([0, 1]) { |fib| fib << fib.last(2).inject(:+) }
Простой и элегантный - лучший способ, верно?
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
Сейчас я могу придумать 4 способа достижения цели Фибоначчи!
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
tap
:[0, 1].tap { |a| 10.times { a.push(a[-1] + a[-2]) } }
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) }
each_with_object
или map.with_object
:10.times.each_with_object([0, 1]) { |_, a| a.push(a.last(2).sum) }
Примечание: если у вас нет Ruby 2. 4+ у вас может не быть метода sum. В этом случае вы можете добавить два последних элемента с помощью ary[-2] + ary[-1]
или ary.last(2).reduce(:+)
.
Приходя к вашей проблеме:
Рассматривая члены в последовательности Фибоначчи, значения которых не превышают четырех миллионов, найдите сумму четных членов.
[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
Надеюсь это поможет!
Вот мой один лайнер, когда таблица @fib
заполняется, когда мы возвращаем метод.
@fib=[0,1];def fib num; return 0 if num < 0; @fib[num]||=fib(num-1)+fib(num-2);end