Q, 47 байт
m:{*/1_-':|(0<){y-x x bin y}[*+60(|+\)\1 0]\x}
Тестовое задание
+(i;m'i:1 2 3 4 5 6 7 8 9 42 1000 12345)
читать его как пары (i, map (m, i)), где m - вычисляющая функция, а i - различные аргументы
пишет
1 1
2 2
3 3
4 3
5 5
6 5
7 10
8 8
9 8
42 272
1000 12831
12345 138481852236
объяснение
n funtion\arg
применяет функцию (function (function (... function (args)))) n раз (внутренне использует рекурсию тала) и возвращает последовательность результатов. Мы вычисляем 60 первых элементов ряда Фибоначчи как *+60(|+\)\1 0
. В этом случае функция равна ( | +): + \, примененный к последовательности, вычисляет частичные суммы (ex + \ 1 2 3 равно 1 3 6), и | переворачивает последовательность. Таким образом, на каждой «итерации» мы вычисляем частичные суммы двух предыдущих чисел Фибоначчи и возвращаем частичные суммы, обратные. 60(|+\)\1 0
генерирует последовательности 1 0, 1 1, 2 1, 3 2, 5 3, 8 5, 13 8, 21 13, ...*+
примененные к этому результату, переворачивают (traspose) и принимают первое. Результатом является последовательность 1 1 2 3 5 8 13 21 34 55 ..
(cond)function\args
применяет функцию (function (.. function (args))), в то время как cond true, и возвращает последовательность частичных результатов
function[arg]
применяется к функции более чем одного аргумента создает проекцию (частичное применение)
Мы можем дать имя аргументам, но неявными именами являются x, y, z
{y-x x bin y}[*+60(|+\)\1 0]
объявляет лямбду с аргументами x, y с частичной проекцией (arg x - ряд Фибоначчи, рассчитывается как * + 60 (| +) \ 1 0). x представляет значения Фибоначчи, а y число для обработки. Бинарный поиск (bin) используется для определения индекса большего числа Фибоначчи <= y (x bin y
) и вычитания соответствующего значения x.
Чтобы рассчитать произведение по частичным результатам, мы обращаем их и вычисляем разницу для каждой пары ( -':|
), отбрасываем первое ( 1_
потому что равно 0) и умножаем на (*/
).
Если нас интересует накопленная сумма, код такой же, но +/
вместо */
. Мы также можем использовать любой другой диадический оператор вместо + или *
Об эффективности исполнения
Я знаю, что в этом конкурсе эффективность не проблема. Но в этой задаче мы можем варьировать от линейных затрат до экспоненциальных, так что мне это интересно.
Я разработал вторую версию (длина 48 байт без комментариев) и повторил тестовые кейсы по 1000 раз на обеих версиях.
f:*+60(|+\)\1 0;m:{*/1_-':|(0<){x-f f bin x}\x} /new version
время выполнения: оригинальная версия 0'212 сег, новая версия 0'037 сег
Оригинальная версия рассчитывает фиббонитную серию один раз для каждой функции приложения; Новая версия Фибоначчи рассчитывает только один.
В обоих случаях для вычисления ряда Фибоначчи используется хвостовая рекурсия
2
можно разложить как-1 + 3
. Правильное утверждение теоремы Цекендорфа состоит в том, что положительное число Фибоначчи может быть однозначно разложено как сумма непоследовательных чисел Фибоначчи с положительным индексом.