Функция Аккермана известна тем, что является одним из простейших примеров полной вычислимой функции, которая не является примитивно-рекурсивной.
Мы будем использовать определение A(m,n)
взятия двух неотрицательных целых чисел, где
A(0,n) = n+1
A(m,0) = A(m-1,1)
A(m,n) = A(m-1,A(m,n-1))
Вы можете реализовать
- именованная или анонимная функция, принимающая в качестве входных данных два целых числа, возвращающая целое число, или
- программа, получающая два целых числа, разделенных пробелом или новой строкой, в STDIN и выводит результат в STDOUT.
Вы не можете использовать функцию Аккермана или функцию гиперэкспонирования из библиотеки, если она существует, но вы можете использовать любую другую функцию из любой другой библиотеки. Допускается регулярное возведение в степень.
Ваша функция должна быть в состоянии найти значение A(m,n)
для m ≤ 3 и n ≤ 10 менее чем за минуту. Он должен, по крайней мере, теоретически завершаться на любых других входах: учитывая бесконечное пространство стека, собственный тип Bigint и произвольно длительный период времени, он будет возвращать ответ. Изменить: Если ваш язык имеет глубину рекурсии по умолчанию, которая слишком ограничительна, вы можете перенастроить ее без затрат на символы.
Представление с кратчайшим количеством символов выигрывает.
Вот некоторые значения, чтобы проверить ваш ответ:
A | n=0 1 2 3 4 5 6 7 8 9 10
-----+-----------------------------------------------------------------
m=0 | 1 2 3 4 5 6 7 8 9 10 11
1 | 2 3 4 5 6 7 8 9 10 11 12
2 | 3 5 7 9 11 13 15 17 19 21 23
3 | 5 13 29 61 125 253 509 1021 2045 4093 8189
4 | 13 65533 big really big...
A(3,8)
и выше, чем наивно, как другие? Нужно ли придумывать решение без рекурсии, или я могу просто «предполагать бесконечное пространство в стеке» в этих случаях? Я уверен, что это закончится в течение минуты.Ответы:
Пиф , 19
Определяет
a
, который работает как функция Аккермана. Обратите внимание, что для этого требуется более высокая глубина рекурсии, чем допускал до сегодняшнего дня официальный компилятор pytha 3 10
, поэтому я увеличил глубину рекурсии. Это не изменение языка, просто компилятор.Тест:
Объяснение:
По сути, это в первую очередь обусловливает значение истинности того,
G
следует ли возвращать или возвращать H + 1. Если он рекурсивный, первым аргументом всегда является G-1, и онH
зависит от значения истинности того , использовать ли его вa(G,H-1)
качестве второго аргумента или использовать1
в качестве второго аргумента.источник
DaGHR
наM
иa
кg
. (Был ли порядок аргументов для?
изменения?)M
вместо этого, и да,?
порядок аргументов изменился. Сейчас состояние, правда, ложь. Это было правдой, условием, ложью.M
!Хаскелл, 35
это определяет функцию оператора
%
.это работает, заметив , что
m%n
(гдеa
функция Аккермана) для ненулевыхm
в(m-1)%
прикладнойn+1
раз1
. например,3%2
определяется как2%(3%1)
что есть2%(2%(3%0))
, а это2%(2%(2%1))
источник
0%n
n+1
GolfScript (30)
Онлайн демо
Без
1>
(каких особых случаевA(1, n)
) вычислениеA(3, 10)
на компьютере, на котором я его тестировал, занимает 9 минут . В этом особом случае достаточно быстро, чтобы онлайн-демонстрация заняла менее 10 секунд.Обратите внимание, что это не наивный перевод определения. Глубина рекурсии ограничена
m
.рассечение
источник
1>
. После удаления (и заменыif
на?
) вычисления3 10 A
занимают 110 секунд с онлайн-переводчиком и шесть секунд с Java-интерпретатором.Двоичное лямбда-исчисление , 54 бита = 6,75 байта
HexDump:
Binary:
Это λ м . m (λ g . λ n . g ( n g 1)) (λ n . λ f . λ x . f ( n f x )), где все числа представлены в виде церковных чисел .
источник
JavaScript, ES6,
4134 байтаЗапустите это на последней консоли Firefox, и она создаст функцию,
f
которую вы можете вызывать с различными значениямиm
иn
т.п.ИЛИ
попробуйте код ниже в последнем Firefox
источник
Python 2.7.8 -
80, 54, 48, 4645(Кредиты для XNOR!)
Более читаемый, но с еще одним символом:
Не то, чтобы я должен был установить
sys.setrecursionlimit(10000)
, чтобы получить результат дляA(3,10)
. Дальнейшая игра в гольф с использованием логической индексации не работала из-за резко растущей глубины рекурсии.источник
1else
. Стартовая букваe
создает проблемы для парсера, потому что числа могут быть записаны как1e3
.and/or
:A=lambda m,n:m and A(m-1,n<1or A(m,n-1))or-~n
1else
, большинство других версий нет.1else
; это позволяет мне выдавливать полукокса здесь и, возможно, в других местах. Но черт возьми, это зависит от версии! Python 2.7.4 не позволяет этого. Используете ли вы онлайн-версию с 2.7.8 или мне придется ее скачать?1else
.J - 26 символов
Существует альтернативное, более функциональное определение Аккермана:
Бывает так, что
Iter
очень легко написать в J, потому что J имеет способ передачи вm-1
to,Ack
а также для определения начального значения, равногоIter
1. Объясняется взрывом:Это опирается на то, что J называет формой
^:
герунда - по сути, способ иметь больший контроль над всеми границами молчаливым (бессмысленным) способом.На REPL:
Нам нужно определить
ack
по имени, чтобы иметь возможность поместить его в таблицу, потому что$:
это ужасный, уродливый зверь, который набрасывается на любого, кто пытается это понять. Это самореференция, где «я» определяется как самая большая глагольная фраза, содержащая его.table
это наречие, и поэтому хотелось бы стать частью фразы глагола, если вы дадите ему возможность, поэтому вы должны поймать$:
в ловушку названное определение, чтобы использовать его.Изменить: 24 символа?
Спустя годы я нашел решение, которое на два символа короче.
Это намного медленнее, хотя:
3 ack 8
занимает больше минуты на моей машине. Это потому, что (1) я использую сгиб/
вместо итерации, поэтому J, вероятно, должен запомнить больше вещей, чем обычно, и (2), в то время как0&<~
выполняет те же вычисления(0<[)
, что и, фактически, получаетn+1
время выполнения, прежде чем предпринимать рекурсивный шаг при вызовеm ack n
-0&<
происходит быть идемпотентом, чтобы не испортить расчет, ноn
быстро набрать обороты и статьack
очень рекурсивным.Я сомневаюсь, что более мощная машина может выдвинуть новый код менее чем за минуту, потому что это компьютер, где старый код может найти
3 ack 10
менее чем за 15 секунд.источник
C - 41 байт
Ничего особенного - небольшие пределы означают, что все необходимые значения могут быть вычислены менее чем за 1 секунду, наивно следуя определению функции.
источник
Javascript ES6 (34)
Реализация:
источник
JavaScript (ES6) - 34
И тест:
источник
Coq, 40
Это функция типа
nat -> nat -> nat
. Поскольку Coq допускает только построение полных функций, оно также служит формальным доказательством того, что повторение Аккермана является обоснованным.Демо-версия:
Примечание: Coq 8.5, выпущенный после этого испытания, переименован
nat_iter
вNat.iter
.источник
Ракетка 67
источник
Mathematica, 46 байтов
Занимает ровно минуту
a[3,10]
. Обратите внимание, что предел рекурсии по умолчанию в Mathematica слишком малa[3,8]
и ниже (по крайней мере, на моем компьютере), но это можно исправить, настроивисточник
If
быть функцией еще медленнее.m_~a~n_:=m~a~n=
...Javascript с лямбдами, 34
Типичный ответ, не может сделать что-нибудь короче.
источник
Haskell,
4844 символов (36 для списка)Хотя это решение не такое короткое, как у другого решения на Haskell, оно примечательно тем, что оно выражает функцию Аккермана в виде бесконечного списка, который, я думаю, выглядит довольно аккуратно. Результатом является бесконечный список (из бесконечных списков), такой что в позиции [m, n] он содержит значение A (m, n) .
Сам бесконечный список:
Как функция (для соответствия спецификации):
Формулировка была получена путем наблюдения, что общий / общий случай для функции Аккермана состоит в том, чтобы использовать значение слева в качестве индекса в строке выше. Базовый случай для этой рекурсии (т. Е. Самый левый столбец строки, т. Е. A (m, 0) ) заключается в использовании второго крайнего левого значения в строке выше. Базовый случай для этой рекурсии - это случай A (0, n) = n + 1 , то есть первая строка
[1..]
.Таким образом, мы получаем
Затем мы просто добавляем еще один уровень итерации на основе этого паттерна и делаем бессмысленное жонглирование.
источник
iterate
для одной буквы имени, то естьi=iterate;ack=i ...
Крошечный Лисп , 70 (вне конкуренции)
Это выходит за рамки конкуренции, так как язык новее, чем вопрос, и он также не может выполнить то,
(A 3 10)
что требуется в вопросе, из-за переполнения стека.(d A(q((m n)(i m(i n(A(s m 1)(A m(s n 1)))(A(s m 1)1))(s n(s 0 1))))))
Это определяет функцию,
A
которая вычисляет функцию Аккермана. отформатирован:Мы используем все встроенные макросы (
d
(define) иq
(quote) иi
(if)) и одну встроенную функцию (s
- вычитание) здесь.i
выполняет свою истинную часть, когда условием является число> 0 (а в противном случае - ложная часть), поэтому здесь не нужно делать явное сравнение.s
является единственной доступной арифметической операцией, мы используем ее как дляn-1
/m-1
, так и(s n (s 0 1))
дляn+1
.Tiny lisp использует оптимизацию хвостовой рекурсии, но это помогает только для внешнего
A
вызова в результате, но не дляA(m, n-1)
вызова, который используется для параметров.С моей крошечной реализацией lisp в Ceylon на JVM она работает
(A 3 5) = 253
, но кажется, что она ломается при попытке(A 2 125)
прямого вычисления (что должно дать тот же результат). Если после этого вычислить(A 3 4) = 125
, то JVM, похоже, должен оптимизировать функции настолько, чтобы встроить некоторые промежуточные вызовы функций в моем интерпретаторе, обеспечивая большую глубину рекурсии. Странный.Эталонная реализация вытворяет ,
(A 3 5) = 253
а также(A 2 163) = 329
, но не удается(A 2 164)
, и поэтому даже меньше(A 3 6) = (A 2 253)
.источник
Go,
260243240122 байтЯ не видел, чтобы этот вопрос позволял анонсировать.
далеко не конкурентоспособный, но я изучаю этот язык, и я хотел проверить это.
используйте его как,
go run ack.go
а затем укажите два числа,m
иn
. если m> 4 или n> 30, время выполнения может превышать полминуты.для
m=3 n=11
:править : сохранено всего 17 байт, переключаясь на
switch
болееif/else
и дот-импортисточник
switch 0 {case m:r=n+1 case n:r=a(m-1,1) default:r=a(m-1,a(m,n-1))}
Гоswitch
так красиво, гибко!Haskell:
8169 байтa 3 10
занимает около 45 секунд.источник
(неконкурентный) Pyth, 15 байтов
Попробуйте онлайн! (пример использования
g3T
добавленной функции , что означаетg(3,10)
)источник
(неконкурентный) UGL ,
3130 байтВвод разделен переводом строки.
Попробуйте онлайн!
(Это было реализовано в качестве стандартного примера в интерпретаторе.)
источник
Юлия,
343128 байтЭто именованная анонимная функция. Это прямая реализация рекурсивного определения, злоупотребляющая способностью Юлии переопределять операторы .
Попробуйте онлайн!
источник
R -
5452Я использовал это в качестве предлога, чтобы попытаться разобраться в R, так что это, вероятно, действительно плохо сделано :)
Пример запуска
Я получаю переполнение стека для чего-либо кроме этого
T-SQL- 222
Я думал, что я попытаюсь заставить T-SQL сделать это также. Использовал другой метод, потому что рекурсия не так хороша в SQL. Что-нибудь более 4,2 бомб это.
источник
{}
хотя нет предела помогает переполнения стека, так как R не имеет TCO ...мозговой трах , 90 байтов
Попробуйте онлайн!
Предполагается реализация с произвольным размером ячейки, с IO в качестве чисел. -6 байт, если вы не возражаете против использования отрицательных ячеек.
Заканчивается примерно через 30 секунд для 3,8 в связанном интерпретаторе, если вы отметили правильные настройки. Введите введенные числа с добавлением
\
s, например,3,9
is\3\9
.источник
Tcl , 67 байт
Попробуйте онлайн!
Tcl , 77 байт
Попробуйте онлайн!
В онлайн-компиляторе он не запускается из-за истечения времени ожидания, но в локальном интерпретаторе Tcl он работает хорошо. Я профилировал каждый корневой вызов
A
функции, чтобы увидеть, сколько времени заняло вычисление для каждой пары,{m,n}
подлежащей тестированию:Не удается для последней пары
{m,n}={3,10}
, поскольку это занимает немного больше чем одна минута.Для более высоких значений
m
, то необходимо будет увеличитьrecursionlimit
значение.Я могу сократить его до 65 байт, но он не будет отвечать требованию вопроса «Ваша функция должна быть способна найти значение A (m, n) для m ≤ 3 и n ≤ 10 менее чем за минуту». Без
{}
этого тайм-аут на TIO и не делать демо из последних двух записей.Tcl , 65 байт
Попробуйте онлайн!
источник
J: 50
Возвращает в доли секунды для 0 ... 3 против 0 ... 10:
PS: «0 служит для того, чтобы заставить A работать с каждым отдельным элементом, вместо того, чтобы сожрать левый и правый массив и генерировать ошибки длины. Но это не требуется, например, для 9 = 2 A 3.
источник
APL, 31
Довольно просто. Использует символ once один раз, чтобы сохранить один байт путем обращения аргументов. Принимает m в качестве левого аргумента и n в качестве правого аргумента.
TryAPL.org
источник
Руби, 65
объяснение
Это довольно простой перевод алгоритма, приведенного в описании проблемы.
Integer
Ожидается два с.Hash
h
.||=
Оператор используется для вычисления значения , которое не было ранее вычисленным.a[3,10]
рассчитывается в ~ 0,1 сек на моей машине.Вот не разгромленная версия
источник
a[3,10]
выброситьm<1?(n+1):(n<1?a[m-1,1]:a[m-1,a[m,n-1]])
наm<1?n+1:a[m-1,n<1?1:a[m,n-1]]
Мышь-2002 ,
9983 байтаисточник
Java, 274 байта
Он рассчитывает
A(3,10)
за несколько секунд, и, учитывая бесконечное пространство памяти и стека, он может рассчитывать любую комбинациюb
иB
до тех пор, пока результат будет ниже 2 2147483647 -1.источник
import java.math.*;BigInteger A(BigInteger b,BigInteger B){return b.equals(B.ZERO)?B.add(B.ONE):B.equals(B.ZERO)?A(b.subtract(B.ONE),B.ONE):A(b.subtract(B.ONE),A(b,B.subtract(B.ONE)));}
Цейлон,
888785Это простая реализация. отформатирован:
Псевдоним сохраняет только один байт, без него (с записью
Integer
вместоI
) мы получили бы до 86 байт. Еще два байта можно сохранить, заменив== 0
их< 1
дважды.С настройками по умолчанию
ceylon run
, он будет работать доA(3,12) = 32765
(иA(4,0) = 13
), ноA(3,13)
(и, следовательно, такжеA(4,1)
) выдаст ошибку переполнения стека. (A(3,12)
занимает около 5 секунд,A(3,11)
около 3 на моем компьютере.)Использование
ceylon run-js
(то есть выполнение результата компиляции в JavaScript на node.js) происходит намного медленнее (требуется 1 мин 19 сA(3,10)
) и прерывается ужеA(3, 11)
с «Превышен максимальный размер стека вызовов» (с использованием настроек по умолчанию) после запуска для 1 мин 30 с.Цейлон без рекурсии, 228
В качестве бонуса, это нерекурсивная версия (конечно, более длинная, но не подверженная переполнению стека - может в какой-то момент получить ошибку нехватки памяти).
отформатирован:
На моем компьютере он работает намного медленнее, чем рекурсивная версия:
A(3,11)
занимает 9,5 секунды,A(3,12)
34 секунды,A(3,13)
2:08 минуты,A(3,14)
8:25 минут. (У меня изначально была версия, использующая ленивые итерации вместо имеющихся у меня кортежей, которая была даже намного медленнее, с тем же размером).Немного быстрее (21 секунда
A(3,12)
) (но также на один байт длиннее) - версия, использующаяs.push
вместоs.addAll
, но ее нужно вызывать несколько раз, чтобы добавить несколько чисел, поскольку для каждого требуется только одно целое число. Использование LinkedList вместо ArrayList намного медленнее.источник