Вывести первые n чисел последовательности Фибоначчи в одном выражении

так что я немного возился с Python в последнее время, и я пытаюсь найти способ вывести N-е число последовательности Фибоначчи в одном выражении. Это код, который я написал до сих пор:

(lambda f: f if f<2 else (f-1)+(f-2))(n)
# n == 1 -> 1
# n == 2 -> 1
# n == 3 -> 3
# n == 4 -> 5
# n == 5 -> 7
....

однако, как я уже говорил выше, это просто выводит набор нечетных чисел. Я смущен, почему это происходит, потому что, если я перепишу это как именованную лямбда-функцию, это будет выглядеть примерно так:

f = lambda n: n if n<2 else f(f-1)+f(f-2)
# f(1) -> 1
# f(2) -> 1
# f(3) -> 2
# f(4) -> 3
...
# f(10) -> 55
...

причина, по которой я добавлен тег лямбда-исчисления, потому что я не уверен, подпадает ли этот вопрос под область простого понимания того, как Python обрабатывает это. Я немного читал о y-комбинаторе в лямбда-исчислении, но это иностранный язык для меня и не мог извлечь ничего из ресурсов, которые я нашел для этого о лямбда-исчислении.

теперь причина, по которой я пытаюсь сделать это в одной строке кода, а не называть его, заключается в том, что я хочу попробовать и поместить эту лямбда-функцию в список понимание. Поэтому сделайте что-нибудь вроде этого:

[(lambda f: f if f<2 else (f-1)+(f-2))(n) for n in range(10)]

и создайте массив из первых X чисел в последовательности Фибоначчи.

то, что я ищу, - это метод выполнения всего этого в одном выражении, и если это попадет в область лямбда-исчисления, что, я считаю, так и есть, для кого-то, чтобы объяснить, как это будет работать.

не стесняйтесь предлагать ответ на JavaScript, C# или других C-подобных языках, которые поддерживают лямбда-функции.

EDIT: я нашел решение того, что я пытался сделать:

[(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f:(lambda n: n if n<2 else f(n-1)+f(n-2)))(y) for y in range(10)]

Я знаю, что это совсем не практично, и этот метод никогда не должен использоваться, но я был обеспокоен тем, Могу ли я это сделать, а не должен ли я когда-либо это делать.

5 ответов


Как насчет:

(lambda f: (4 << f * (3 + f)) // ((4 << 2 * f) - (2 << f) - 1) & ((2 << f) - 1))(n)

он не запускает последовательность обычным способом:

0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, ...

но как только вы пройдете 1, Вы в порядке. Вы найдете подробное объяснение в записи в блоге целочисленная формула для чисел Фибоначчи наряду с большим количеством соответствующей информации.

в моей системе решение на основе золотого сечения @lehiester сходит с рельсов на F71, производя 308061521170130 вместо 308061521170129 и продолжает отклоняться от там.


вам нужно назначить лямбду фактической переменной, а затем вызов лямбда внутри лямбда:

>>> g = lambda f: f if f < 2 else g(f-1)+g(f-2)
>>> [g(n) for n in range(10)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

у меня есть однострочное решение, которое соответствует вашим критериям, но это один из самых сумасшедших кодов, которые я когда-либо писал. Он не использует понимание списка, но он смешивает динамическое решение и лямбда-функцию в одной строке.

fib = (lambda n: (lambda fib: fib(fib, [], n, None))(lambda fib, arr, i, _: arr if i == 0 else fib(fib, arr, i-1, arr.append(1) if len(arr) < 2 else arr.append(arr[-1]+arr[-2]))))

просто чтобы объяснить это немного. Первая часть (lambda fib: fib(fib, [], n, None)) возьмите лямбда-функцию в качестве параметра, а затем вызовите ее с параметрами, которые она ожидает. Этот трюк позволяет нам назначить имя лямбда-функции и передать это имя самому себе.. это магия.

вместо второй части, основная функция,lambda fib, arr, i, _: arr if i == 0 else fib(fib, arr, i-1, arr.append(1) if len(arr) < 2 else arr.append(arr[-1]+arr[-2]))) использует еще один трюк для реализации динамического решения. Первый параметр fib является ссылкой на себя, второй параметр,arr, это массив, содержащий наше решение, и он заполняется слева направо, вызывая рекурсивно fib ровно n раза. Рекурсия заканчивается, когда третий параметр i становится 0. Четвертый параметр-уродливый трюк: он не используется функцией, но используется для вызов метода append arr.

это абсолютно менее элегантное решение, но оно также является самым быстрым. Я сообщаю тайминги для N=500 ниже.

наивное решение невыполнимо, но здесь вы можете найти код для вычисления одного элемента за раз ряда (это, вероятно, то, что вы хотели смешать лямбда-функцию и рекурсию):

(lambda n: ((lambda fib: fib(fib,n+1))(lambda fib, i: (1 if i <= 2 else fib(fib,i-2) + fib(fib,i-1)))))(N)

решение, предложенное @cdlane:

%timeit [0, 1] + [(4<<n*(3+n)) // ((4<<2*n)-(2<<n)-1) & ((2<<n)-1) for n in range(N)][1:]
10 loops, best of 3: 88.3 ms per loop

решение, предлагаемое @lehiester:

%timeit [int(round((lambda n: ((1+5**0.5)**n-(1-5**0.5)**n)/(2**n*5**0.5))(x))) for x in range(N)]
1000 loops, best of 3: 1.49 ms per loop

мое уродливое решение:

%timeit (lambda n: (lambda fib: fib(fib, [], n, None))(lambda fib, arr, i, _: arr if i == 0 else fib(fib, arr, i-1, arr.append(1) if len(arr) < 2 else arr.append(arr[-1]+arr[-2]))))(N)
1000 loops, best of 3: 434 us per loop

еще одно уродливое и быстрое решение, которое не использует рекурсию:

%timeit (lambda n: (lambda arr, fib_supp: [arr] +  [fib_supp(arr) for i in xrange(n)])([], (lambda arr: arr.append(1) if len(arr) < 2 else arr.append(arr[-1]+arr[-2])))[0])(N)
1000 loops, best of 3: 346 us per loop

обновление

наконец-то я нашел элегантный способ сформулировать одну функцию. Идея всегда одна и та же, но с помощью setitem класса метод вместо добавления. Некоторые из трюков, которые я использовал, можно найти в этом ссылке. Этот подход немного медленнее, но, по крайней мере, читаемый:

%timeit (lambda n: (lambda arr, fib_supp: any(fib_supp(i, arr) for i in xrange(2,n)) or arr)([1] * n, (lambda i, arr: arr.__setitem__(i,(arr[i-1]+arr[i-2])))))(N)
1000 loops, best of 3: 385 us per loop

вы должны каким-то образом назначить ему имя, чтобы использовать рекурсивное определение-в противном случае рекурсивная лямбда-функция невозможна в Python, поскольку у нее нет специального рефлексивного ключевого слова, которое ссылается на нее.

как упоминал @TerryA, вы можете использовать трюк в этот пост для генерации последовательности x числа Фибоначчи в одном операторе с рекурсивным определением.

или, вы можете использовать закрытая форма, что было бы намного быстрее:

[int(round((lambda n: ((1+5**0.5)**n-(1-5**0.5)**n)/(2**n*5**0.5))(x)))
 for x in range(10)]

это предполагает, что x не очень большой, хотя, потому что арифметика float будет переполняться вокруг x=600 и, вероятно, будут иметь большие ошибки округления до этой точки-как указывает @cdlane, это начинает расходиться с фактической последовательностью при x=71, то есть x in range(72).


EDIT: @cdlane разделил закрытую форму только с целочисленной арифметикой, которая должна работать для любого x в теории. Я бы, вероятно, использовал этот вместо выражения выше.

[0, 1] + [(4<<n*(3+n)) // ((4<<2*n)-(2<<n)-1) & ((2<<n)-1)
          for n in range(10)][1:]

лямбда-исчисление через Python

тег лямбда-исчисление, вместо того, чтобы писать ответ, который полагается на умные трюки или языковые функции, специфичные для python, я буду использовать только простые лямбды
U = lambda f: f (f)

Y = U (lambda h: lambda f: f (lambda x: h (h) (f) (x)))

loop = Y (lambda recur: lambda acc: lambda a: lambda b: lambda n:
  acc if n == 0 else
    recur (acc + [a]) (a + b) (a) (n - 1))

fibonacci = loop ([]) (0) (1)

print (fibonacci (10))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

конечно, мы использовали четыре имени лямбда U, Y, loop и fibonacci – поскольку каждая лямбда является чистой функцией, мы можем заменить любую ссылку на ее имя его значение

# in Y, replace U with its definition
Y = (lambda f: f (f)) (lambda h: lambda f: f (lambda x: h (h) (f) (x)))
# in loop, replace Y with its definition
loop = (lambda f: f (f)) (lambda h: lambda f: f (lambda x: h (h) (f) (x))) (lambda recur: lambda acc: lambda a: lambda b: lambda n:
  acc if n == 0 else
    recur (acc + [a]) (a + b) (a) (n - 1))
# in fibonacci, replace loop with its definition
fibonacci = (lambda f: f (f)) (lambda h: lambda f: f (lambda x: h (h) (f) (x))) (lambda recur: lambda acc: lambda a: lambda b: lambda n:
  acc if n == 0 else
    recur (acc + [a]) (a + b) (a) (n - 1)) ([]) (0) (1)

fibonacci теперь это единственное, чистое выражение-мы могли бы назвать лямбду прямо в print заявление...

# in print, replace fibonacci with its definition
print ((lambda f: f (f)) (lambda h: lambda f: f (lambda x: h (h) (f) (x))) (lambda recur: lambda acc: lambda a: lambda b: lambda n:
  acc if n == 0 else
    recur (acc + [a]) (a + b) (a) (n - 1)) ([]) (0) (1) (10))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

еще раз, используя другой программы

я написал очень аналогичный ответ (также в Python), но в контексте другой программы – может быть полезно помочь увидеть общность из техники


еще раз, на другом языке

мы сделаем все это снова только на этот раз в JavaScript. JS лучше подходит для демонстрация это упражнение потому, что мы можем показать код в браузере и синтаксис лямбда-гораздо более снисходительными (в плане форматирования кода) – кроме этого, вы заметите, что программы почти идентичные

const U = f =>
  f (f)

const Y =
  U (h => f => f (x => h (h) (f) (x)))

const loop = Y (recur => acc => a => b => n =>
  n === 0
    ? acc
    : recur (acc.concat ([a])) (a + b) (a) (n - 1))

const fibonacci =
  loop ([]) (0) (1)

console.log (fibonacci (10))
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
// in Y, replace U with its definition
Y = (f => f (f)) (h => f => f (x => h (h) (f) (x)))
// in loop, replace Y with its definition
loop = (f => f (f)) (h => f => f (x => h (h) (f) (x))) (recur => acc => a => b => n =>
  n === 0
    ? acc
    : recur (acc.concat ([a])) (a + b) (a) (n - 1))
// in fibonacci, replace loop with its definition
fibonacci = (f => f (f)) (h => f => f (x => h (h) (f) (x))) (recur => acc => a => b => n =>
  n === 0
    ? acc
    : recur (acc.concat ([a])) (a + b) (a) (n - 1)) ([]) (0) (1)

fibonacci теперь это единственное, чистое выражение-мы могли бы назвать лямбду прямо в console.log заявление...

о и действительно быстро, слишком

console.time ('fibonacci (500)')
console.log ((f => f (f)) (h => f => f (x => h (h) (f) (x))) (recur => acc => a => b => n =>
  n === 0
    ? acc
    : recur (acc.concat ([a])) (a + b) (a) (n - 1)) ([]) (0) (1) (500))
console.timeEnd ('fibonacci (500)')
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 490 more items ]
// fibonacci (500): 3 ms

практика делает совершенным

лямбда-исчисление было одним из моих избранные участки учись!--39-->. Только с Y-комбинатором я провел бесчисленные часы изучение его красоты и изысканности. Надеюсь, вы найдете эти исследования полезными. Если у вас есть какие-либо другие вопросы по теме, не стесняйтесь спрашивать ^_^