Код Гольф: Фракран

Задача

Напишите программу, которая выступает в качестве Fractran интерпретатор. Победителем является самый короткий интерпретатор по количеству символов на любом языке. Ваша программа должна принимать два входа: программа фрактанта, которая должна быть выполнена, и целое число ввода n. Программа может быть в любой форме, удобной для вашей программы - например, список из 2-х кортежей или плоский список. Вывод должен быть единственным целым числом, являющимся значением регистра в конце выполнения.

Fractran

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

  • Проверьте, является ли произведение текущего состояния и фракция, находящаяся в настоящее время под программным счетчиком, целым числом. Если это так, умножьте текущее состояние на текущую дробь и reset счетчик программ на начало списка.
  • Продвиньте счетчик программ. Если конец списка достигнут, остановитесь, в противном случае вернитесь к шагу 1.

Подробнее о том, как и почему работает Fractran, см. запись esolang и эту запись по хорошей математике/плохой математике.

Тест-векторы

Программа: [(3, 2)]
Вход: 72 (2 3 3 2)
Выход: 243 (3 5)

Программа: [(3, 2)]
Вход: 1296 (2 4 3 4)
Выход: 6561 (3 8)

Программа: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)] < ш > Вход: 72 (2 3 3 2)
Выход: 15625 (5 6)

Бонусный тестовый вектор:

Ваше сообщение не обязательно должно правильно исполнять эту последнюю программу, чтобы быть приемлемым ответом. Но это правда, если это так!

Программа: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)] < ш > Вход: 60466176 (2 10 3 10)
Выход: 7888609052210118054117285652827862296732064351090230047702789306640625 (5 100)

Представления и подсчет очков

Программы занимают строго по длине в символах - самое короткое - лучшее. Не стесняйтесь представить как красиво выложенную, так и документированную и "минитизированную" версию вашего кода, чтобы люди могли видеть, что происходит.

Язык 'J' недопустим. Это потому, что уже известное решение в J на ​​одной из связанных страниц. Если вы поклонник J, извините!

В качестве дополнительного бонуса, однако, любой, кто может предоставить работающего переводчика fractran в фрактране, получит бонус в размере 500 баллов. В маловероятном случае с несколькими переводчиками для самостоятельного хостинга, каждый с самым коротким числом дробей получит награду.

Победители

Официальным победителем, после подачи самоорганизационного решения фрактанта, содержащего 1779 фракций, является решение Джесси Бедер. Практически говоря, решение слишком медленно, чтобы выполнить даже 1 + 1, однако.

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

Почетные упоминания:

  • решение Stephen Canon, в 165 символах сборки x86 (28 байт машинного кода)
  • Иорданское решение в 52 символах рубина - который обрабатывает длинные целые числа
  • Бесполезное решение в 87 символах Python, которое, хотя и не является самым коротким решением Python, является одним из немногих решений, которые не являются рекурсивными и, следовательно, более сложны программ с легкостью. Это также очень читаемо.

Ответ 1

Fractran - 1779 фракций

(Изменить: исправлено)

(Я надеюсь, что люди по-прежнему следуют этой теме, потому что это заняло некоторое время!)

Похоже, SO не позволит мне публиковать что-то до тех пор, пока это, поэтому я разместил источник Fractran здесь.

Вход задается следующим образом:

Сначала мы кодируем дробь m/n = p_0^a0... p_k^ak:

  • Начните с 1. Затем для каждого ai:
  • Умножьте на p_2i^ai, если ai > 0
  • Умножьте на p_2i+1^{-ai}, если a_i < 0

Таким образом, мы кодируем любую фракцию как положительное целое число. Теперь, учитывая прогорам (последовательность закодированных фракций F0, F1,...), мы кодируем, что

p_0^F0 p1^F1 ...

Наконец, ввод в интерпретатор задается:

2^(program) 3^(input) 5

где program и input закодированы, как указано выше. Например, в первой тестовой задаче 3/2 получает кодировку до 15, поэтому программа получает кодировку до 2^15; и 108 получает кодировку до 500. Итак, мы проходим

2^{2^15} 3^500 5

для программы. Выходной сигнал тогда имеет вид

2^(program) 3^(output)

поэтому в первом примере это будет

2^{2^15} 3^3125

Как это работает?

Я написал метаязык, который компилируется в Fractran. Он позволяет использовать функции (простой Fractran и последовательности других функций) и цикл while и if (для удобства!). Код для этого можно найти здесь.

Если вы хотите скомпилировать этот код до самого Fractran, мою программу (С++) можно найти здесь [tar.gz]. В потрясающем показе dogfooding (и хвастаясь) я использовал свой парсер С++ YAML yaml-cpp, поэтому вам нужно будет скачать и установить ссылку с этим. Для iaml-cpp и "компилятора" вам понадобится CMake для генерации межплатформенного make файла.

Использование этой программы:

./fracc interpreter.frp

Он считывает имя функции со стандартного ввода и записывает соответствующую "псевдо-фракцию" (я объясню, что в секунду) на стандартный вывод. Итак, чтобы скомпилировать интерпретатор (функция Interpret), вы можете запустить

echo "Interpret" | ./fracc interpreter.frp > interpret

Вывод ( "псевдо-Fractran" ) будет представлять собой последовательность строк, каждая из которых содержит строку разделенных пробелами цифр. Строка соответствует доле: если n -й символ в строке an, то дробь является произведением p_n^an.

Очень легко преобразовать это в Fractran, но если вы ленивы, вы можете использовать to-fractions.py. [ Примечание: раньше у меня была программа на С++, и я небрежно проигнорировал переполнение целого числа. Я перевел его на Python, чтобы избежать этого.]

Примечание о вводе: если вы хотите протестировать другую функцию таким образом, соглашение всегда будет одинаковым. Он имеет ряд параметров (как правило, комментарий выше функции объясняет это) в псевдофрактане, поэтому дайте ему то, что он хочет, плюс 1 в самом следующем слоте (так, в обычном Fractran, умножаем один раз на первое правое что он не будет использоваться). Это бит "сигнала" для запуска функции.


Однако

Я не рекомендую фактически пытаться запустить интерпретатор Fractran (увы). Я тестировал многие его компоненты и, например, функцию IncrementPrimes, которая берет пару простых чисел и возвращает следующие два простых числа, занимает около 8 минут, используя мой глупый интерпретатор С++ (нет необходимости публиковать это:). Плюс, он (по крайней мере) квадратично по количеству вызовов функций - удвоение числа вызовов функций заставляет его занимать не менее четырех раз (более, если существуют циклы while или if). Поэтому я предполагаю, что запуск интерпретатора займет не менее нескольких дней, если не годы: (

Итак, как я знаю, что это работает? Ну, конечно, я не уверен на 100%, но я довольно близко. Прежде всего, я тестировал многие, многие из его компонентов, и, в частности, очень тщательно тестировал все элементы метаязыка (последовательности функций и if и while).

Кроме того, метаязык легко перевести на ваш любимый язык и еще проще перевести на С++, поскольку все параметры функций передаются по ссылке. Если вы снова чувствуете себя ленивым, вы можете скачать мой перевод здесь [tar.gz] (там нет makefile, это всего два файла .cpp, поэтому прямое вызов gcc в порядке).

Итак, вы можете сравнить два интерпретатора, запустить версию С++ (он также принимает ввод/вывод в псевдо-Fractran), проверьте, что это работает, а затем убедите себя, что метаязык тоже работает.


Или!

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

Но я не совсем уверен, как это сделать, и я доволен тем, что сделал, поэтому я оставлю это как упражнение для читателя.

Ответ 2

Fractran: 84 фракции

FTEVAL = [197*103/(2^11*101), 101/103, 103*127/(2*101), 101/103, 109/101,
  2*23/(197*109), 109/23, 29/109,197*41*47/(31*59), 11^10*53/(127*197), 197/53,
  37/197, 7^10*43/(11^10*37), 37/43, 59/(37*47), 59/47, 41*61/59, 31*67/(41*61),
  61/67, 7*67/(127*61), 61/67,101/71, 73/(127^9*29), 79/(127^2*73),
  83/(127*73), 89/(2*29), 163/29, 127^11*89/79, 337/83, 2*59/89, 71/61,
  7*173/(127*163), 163/173, 337*167/163, 347/(31*337), 337/347, 151/337,
  1/71,19*179/(3*7*193), 193/179, 157/(7*193), 17*181/193, 7*211/(19*181),
  181/211, 193/181, 157/193, 223/(7*157), 157/223, 281*283/239,
  3*257*269/(7*241), 241/269, 263/241, 7*271/(257*263), 263/271, 281/263,
  241/(17*281), 1/281, 307/(7*283), 283/307, 293/283, 71*131/107, 193/(131*151),
  227/(19*157), 71*311/227, 233/(151*167*311), 151*311/229, 7*317/(19*229),
  229/317, 239*331/217, 71*313/157, 239*251/(151*167*313), 239*251/(151*313),
  149/(251*293), 107/(293*331), 137/199, 2^100*13^100*353/(5^100*137),
  2*13*353/(5*137), 137/353, 349/137, 107/349, 5^100*359/(13^100*149),
  5*359/(13*149), 149/359, 199/149]

Это написано полностью вручную. Я сделал псевдоязык, чтобы иметь возможность выражать вещи более четко, но я не писал компилятор и решил писать оптимизированный код Fractran напрямую.

FTEVAL принимает ввод 3^initial_state * 5^encoded_program * 199, производит промежуточные результаты 3^interpreted_program_state * 199 и полностью останавливается на числе, делящимся на 233.

Интерпретированная программа встраивается в список десятизначных базовых цифр внутри одного базового номера 11, используя цифру "a", чтобы отметить границу, за исключением самого конца. Программа добавления [3/2] кодируется как

int("3a2", 11) = 475.

Программа умножения [455/33, 11/13, 1/11, 3/7, 11/2, 1/3] кодируется как

int("1a3a11a2a3a7a1a11a11a13a455a33", 11) = 3079784207925154324249736405657

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

Первый тестовый вектор, завершенный в менее одной секунды, дал желаемый результат после 4545 итераций и остановился после 6172 итераций. Здесь полный вывод.

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

Пространство здесь слишком мало, чтобы все объяснить. Но вот мой псевдокод. Надеюсь, я напишу свой процесс через пару дней.

# Notations:
# %p
#     designates the exponent of prime factor p that divides the
#     current state.
# mov x y
#     directly translates to the fraction y/x; its meaning: test if x divides
#     the current state, if so divide the current state by x and multiply it by
#     y.  In effect, the prime exponents of x and y are exchanged.  A Fractran
#     program only comprises of these instructions; when one is executable, the
#     program continues from the beginning.
# dec x => mov x, 1
#     wipes out all factors of x
# inc x => mov 1, x
#     this form is here for the sake of clarity, it usually appears in a
#     loop entry statement and is merged as such when compiled
# sub n ret m {...}
#     conceptually represents a Fractran sub-program, which will execute just
#     like a normal Fractran program, that is, the sub-program statements
#     execute when triggered and loop back.  The sub-program only exits when none of
#     its statement is executable, in which occasion we multiply the program's
#     state by m.  We can also explicitly break out early on some conditions.
#     It is also possible to enter a sub-prorgram via multiple entry points and
#     we must take care to avoiding this kind of behavior (except in case where
#     it is desirable).

# entry point 101: return 29
# Divide %2 modulo 11:
#   - quotient is modified in-place
#   - remainder goes to %127
sub 101 ret 101 { mov 2^11, 197 }
sub 101 ret 109 { mov 2, 127 }
sub 109 ret 29 { mov 197, 2 }

# entry point 59: return 61
# Multiply %127 by 10^%31 then add result to %7,
# also multiply %31 by 10 in-place.
sub 59 ret 41*61 {
  mov 31, 197*41
  sub 197 ret 37 { mov 127, 11^10 }
  sub 37 { mov 11^10, 7^10 }
}
sub 61 ret 61 { mov 41, 31 }
sub 61 ret 61 { mov 127, 7 } # the case where %31==0

# entry point 71: return 151 if success, 151*167 if reached last value
# Pop the interpreted program stack (at %2) to %7.
sub 71 {
  # call sub 101
  inc 101

  # if remainder >= 9:
  mov 29*127^9, 73
  #     if remainder == 11, goto 79
  mov 73*127^2, 79
  #     else:
  #         if remainder == 10, goto 83
  mov 73*127, 83
  #         else:
  #             if quotient >= 1: goto 89
  mov 29*2, 89
  #             else: goto 163
  mov 29, 163

  # 79: restore remainder to original value, then goto 89
  mov 79, 127^11*89

  # 83: reached a border marker, ret
  mov 83, 337

  # 89: the default loop branch
  # restore quotient to original value, call 59 and loop when that rets
  mov 2*89, 59
  mov 61, 71

  # 163: reached stack bottom,
  # ret with the halt signal
  sub 163 ret 337*167 { mov 127, 7 }

  # 337: clean up %31 before ret
  sub 337 ret 151 { dec 31 }
}

# entry point 193, return 157
# Divide %3 modulo %7:
#   - quotient goes to %17
#   - remainder goes to %19
sub 193 ret 17*181 {
  mov 3*7, 19
}
mov 7*193, 157
sub 181 ret 193 { mov 19, 7 }
mov 193, 157
sub 157 ret 157 { dec 7 }

# entry point 239: return 293
# Multiply %17 by %7, result goes to %3
mov 239, 281*283
sub 241 { mov 7, 3*257 }
sub 263 ret 281 { mov 257, 7 }
mov 281*17, 241
sub 283 ret 293 { dec 7 }

# entry point 107: return 149 if success, 233 if stack empty
# Pop the stack to try execute each fraction
sub 107 {
  # pop the stack
  inc 71*131

  # 151: popped a value
  # call divmod %3 %7
  mov 131*151, 193

  # if remainder > 0:
  mov 19*157, 227
  #     pop and throw away the numerator
  mov 227, 71*311
  #     if stack is empty: halt!
  mov 151*167*311, 233
  #     else: call 239 to multiply back the program state and gave loop signal
  mov 151*311, 229
  sub 229 ret 239*331 { mov 19, 7 }
  # else: (remainder == 0)
  #     pop the numerator
  mov 157, 71*313
  #     clear the stack empty signal if present
  #     call 239 to update program state and gave ret signal
  mov 151*167*313, 239*251
  mov 151*313, 239*251

  # after program state is updated
  # 313: ret
  mov 293*251, 149
  # 331: loop
  mov 293*331, 107
}

# main
sub 199 {
  # copy the stack from %5 to %2 and %13
  sub 137 ret 137 { mov 5^100, 2^100*13^100 }
  sub 137 ret 349 { mov 5, 2*13 }

  # call sub 107
  mov 349, 107

  # if a statement executed, restore stack and loop
  sub 149 ret 149 { mov 13^100, 5^100 }
  sub 149 ret 199 { mov 13, 5 }
}

Ответ 3

сборка x86_64, 165 символов (28 байтов машинного кода).

Состояние передается в% rdi, программа (указатель на массив с нулевым завершением фракций) находится в% rsi. Результаты возвращаются в% rax за обычные соглашения о вызовах в стиле C. Использование нестандартных соглашений вызова или синтаксиса Intel (это синтаксис AT & T) приведет к падению еще нескольких символов, но я ленив; кто-то другой может это сделать. Команда или две могут почти наверняка быть спасены путем перенастройки потока управления, если кто-то хочет сделать это, не стесняйтесь.

Промежуточные вычисления (числитель состояний *) могут иметь ширину до 128 бит, но поддерживается только 64-разрядное состояние.

_fractran:
0:  mov     %rsi,   %rcx    // set aside pointer to beginning of list
1:  mov    (%rcx),  %rax    // load numerator
    test    %rax,   %rax    // check for null-termination of array
    jz      9f              // if zero, exit
    mul     %rdi
    mov   8(%rcx),  %r8     // load denominator
    div     %r8
    test    %rdx,   %rdx    // check remainder of division
    cmovz   %rax,   %rdi    // if zero, keep result
    jz      0b              // and jump back to program start
    add     $16,    %rcx    // otherwise, advance to next instruction
    jmp     1b
9:  mov     %rdi,   %rax    // copy result for return
    ret

Удалить комментарии, посторонние пробелы и ярлык _fractran для свернутой версии.

Ответ 4

Ruby, 58 57 56 53 52 символа

Это моя первая в истории игра в гольф, поэтому, пожалуйста, будьте осторожны.

def f(n,c)d,e=c.find{|i,j|n%j<1};d ?f(n*d/e,c):n end

Использование:

irb> f 108, [[455, 33], [11, 13], [1,11], [3,7], [11,2], [1,3]]
=> 15625

irb> f 60466176, [[455, 33], [11, 13], [1, 11], [3, 7], [11, 2], [1, 3]]
=> 7888609052210118054117285652827862296732064351090230047702789306640625

Довольно версия (252):

def fractran(instruction, program)
  numerator, denominator = program.find do |numerator, denominator|
    instruction % denominator < 1
  end

  if numerator
    fractran(instruction * numerator / denominator, program)
  else
    instruction
  end
end

Ruby, 53 52 с использованием Rational

Вдохновленный gnibbler solution Я смог получить решение с использованием Rational до 53 52 символа. Еще один длиннее, чем (менее элегантное) решение выше.

def f(n,c)c.map{|i|return f(n*i,c)if i*n%1==0};n end

Использование:

irb> require 'rational'
irb> f 60466176, [Rational(455, 33), Rational(11, 13), Rational(1, 11), Rational(3, 7), Rational(11, 2), Rational(1, 3)]
=> Rational(7888609052210118054117285652827862296732064351090230047702789306640625, 1)

(A to_i вызов для более красивого вывода добавит еще 5 символов.)

Ответ 5

Golfscript - 32

    
    {:^{1=1$\%!}?.1={[email protected]\/*^f}{}if}:f

    ; 108 [[3 2]] f p
    # 243
    ; 1296 [[3 2]] f p
    # 6561
    ; 108 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p
    # 15625
    ; 60466176 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p
    # 7888609052210118054117285652827862296732064351090230047702789306640625

Ответ 6

Haskell, 102 символа

import List
import Ratio
l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l
$ ghci
Prelude> :m List Ratio
Prelude List Ratio> let l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l
Prelude List Ratio> [3%2]&108
243
Prelude List Ratio> [3%2]&1296
6561
Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108
15625

88 с ослабленными ограничениями на формат ввода/вывода.

import List
import Ratio
l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator.(*)n)l
Prelude List Ratio> let l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator
Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108
15625 % 1

Ответ 7

C, 159 153 151 131 111 110 символов

v[99],c,d;main(){for(;scanf("%d",v+c++););while(d++,v[++d])
*v%v[d]?0:(*v=*v/v[d]*v[d-1],d=0);printf("%d",*v);}
$ cc f.c
$ echo 108 3 2 . | ./a.out; echo
243
$ echo 1296 3 2 . | ./a.out; echo
6561
$ echo 108 455 33 11 13 1 11 3 7 11 2 1 3 . | ./a.out; echo
15625

Ответ 8

Python, 83 82 81 72 70 символов.

Удобно иметь вход как фракции. Фракционные объекты. Такая же идея, как в решении Ruby.

def f(n,c):d=[x for x in c if x*n%1==0];return d and f(n*d[0],c) or n

# Test code:
from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625

Ответ 9

Python - 53

Улучшение благодаря Полю

f=lambda n,c:next((f(n*x,c)for x in c if x*n%1==0),n)

testcases

from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625

Python - 54 Без использования фракции

f=lambda n,c:next((f(n*i/j,c)for i,j in c if n%j<1),n)

Python - 55

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

f=lambda n,c:([f(n*i/j,c)for i,j in c if n%j<1]+[n])[0]

Здесь одна возможность, но растет до 65, даже без включения импорта

from itertools import chain
f=lambda n,c:(chain((f(n*i/j,c)for i,j in c if n%j<1),[n])).next()

Ответ 10

F #: 80 символов

let rec f p=function|x,(e,d)::t->f p (if e*x%d=0I then(e*x/d,p)else(x,t))|x,_->x

Здесь расширенная версия с использованием match pattern with |cases вместо function:

//program' is the current remainder of the program
//program is the full program
let rec run program (input,remainingProgram) =
    match input, remainingProgram with
        | x, (e,d)::rest -> 
            if e*x%d = 0I then //suffix I --> bigint
                run program (e*x/d, program) //reset the program counter
            else    
                run program (x, rest) //advance the program
        | x, _ -> x //no more program left -> output the state   

Тестовый код:

let runtests() = 
    [ f p1 (108I,p1) = 243I
      f p1 (1296I,p1) = 6561I
      f p2 (108I,p2) = 15625I
      f p2 (60466176I,p2) = pown 5I 100] 

И результат (протестирован в F # interactive):

> runtests();;
val it : bool list = [true; true; true; true]

Изменить, давайте еще немного поужинаем с этим и вычислим некоторые простые числа (см. связанную страницу в стартовом сообщении). Я написал новую функцию g, которая дает промежуточные значения состояния.

//calculate the first n primes with fractran
let primes n = 
    let ispow2 n = 
        let rec aux p = function
            | n when n = 1I -> Some p
            | n when n%2I = 0I -> aux (p+1) (n/2I)
            | _ -> None
        aux 0 n

    let pp = [(17I,91I);(78I,85I);(19I,51I);(23I,38I);(29I,33I);(77I,29I);(95I,23I); 
              (77I,19I);(1I,17I);(11I,13I);(13I,11I);(15I,14I);(15I,2I);(55I,1I)]

    let rec g p (x,pp) =
        seq { match x,pp with 
                |x,(e,d)::t -> yield x
                               yield! g p (if e*x%d=0I then (e*x/d,p) else (x,t))
                |x,_ -> yield x }

    g pp (2I,pp)
    |> Seq.choose ispow2
    |> Seq.distinct
    |> Seq.skip 1 //1 is not prime
    |> Seq.take n
    |> Seq.to_list

Получает колоссальные 4,7 секунды, чтобы кашлять первые 10 простых чисел:

> primes 10;;
Real: 00:00:04.741, CPU: 00:00:04.005, GC gen0: 334, gen1: 0, gen2: 0
val it : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29]

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

Ответ 11

Python, 110 103 95 87 символов

frc.py

def f(n,c):
 d=c
 while len(d):
  if n%d[1]:d=d[2:]
  else:n=d[0]*n/d[1];d=c
 return n

test.py

(показывает, как управлять им)

from frc import f
def test():
 """
 >>> f(108, [3,2])
 243
 >>> f(1296, [3,2])
 6561
 >>> f(108, [455,33,11,13,1,11,3,7,11,2,1,3])
 15625
 >>> f(60466176, [455, 33,11, 13,1, 11,3, 7,11, 2,1, 3])
 7888609052210118054117285652827862296732064351090230047702789306640625L
 """
 pass
import doctest
doctest.testmod()

Ответ 12

С#

Типичная версия:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int ip = 1;
            decimal reg = Convert.ToInt32(args[0]);
            while (true)
            {
                if (ip+1 > args.Length)
                {
                    break;
                }
                decimal curfrac = Convert.ToDecimal(args[ip]) / Convert.ToDecimal(args[ip+1]);
                if ((curfrac * reg) % 1 == 0)
                {
                    ip = 1;
                    reg = curfrac * reg;
                }
                else
                {
                    ip += 2;
                }
            }
            Console.WriteLine(reg);
            Console.ReadKey(true);
        }
    }
}

Вырезать версию весом 201 символов (без деклараций пространства имен или любого из них, только один оператор using (не система) и основная функция):

using System;namespace T{using b=Convert;static class P{static void Main(string[] a){int i=1;var c=b.ToDecimal(a[0]);while(i+1<=a.Length){var f=b.ToDecimal(a[i])/b.ToDecimal(a[i+1]);if((f*c)%1==0){i=1;c*=f;}else{i+=2;}}Console.Write(c);}}}

Примеры (ввод осуществляется через аргументы командной строки):

input: 108 3 2
output: 243.00
input: 1296 3 2
output: 6561.0000
input: 108 455 33 11 13 1 11 3 7 11 2 1 3
output: 45045.000000000000000000000000

Ответ 13

Javascript one: 99 символов. Нет бонусного вектора: (

function g(n,p,q,i,c){i=0;while(q=p[i],c=n*q[0],(c%q[1]?++i:(n=c/q[1],i=0))<p.length){};return n;};

Входной сигнал находится в формате [[a,b],[c,d]]. Я воспользовался преимуществами Javascript: вместо выполнения var x=0, y=0; вы можете добавить столько параметров, сколько захотите. Неважно, действительно ли вы передаете их или нет, поскольку они по умолчанию равны null.

Довольно версия:

function g(n,p) {
    var q, c, i=0;
    while(i < p.length) {
        q = p[i];
        c = n * q[0];
        if(c % q[1] != 0) {
            ++i;
        } else {
            n = c % q[1];
            i = 0;
        }
    }
    return n;
};

Ответ 14

Groovy, 136 117 107 символов.

Вызов как groovy fractal.groovy [состояние ввода] [программный вектор как список чисел]

a=args.collect{it as int}
int c=a[0]
for(i=1;i<a.size;i+=2) if(c%a[i+1]==0){c=c/a[i+1]*a[i];i=-1}
println c

Пример

bash$ groovy fractal.groovy 108 455 33 11 13 1 11 3 7 11 2 1 3
Output: 15625

Ответ 15

Perl, 84 82 char

Использует стандартный ввод.

@P=<>=~/\d+/g;$_=<>;
($a,$%)[email protected][$i++,$i++],$_*$a%$%or$i=0,$_*=$a/$%while$i<@P;
print

Принимает 100 символов для прохождения бонусного теста:

use Math'BigInt blcm;@P=<>=~/\d+/g;$_=blcm<>;
($%,$=)[email protected][$i++,$i++],$_*$%%$=or$i=0,($_*=$%)/=$=while$i<@P;print

Ответ 16

Lua:

Код:

a=arg;
ip=2;
reg=a[1];
while a[ip] do
    curfrac = a[ip] / a[ip+1];
    if (curfrac * reg) % 1 == 0 then
        ip=2;
        reg = curfrac * reg
    else
        ip=ip+2
    end
end
print(reg)

Компактный код, взвешивающий в 98 символов (сокращение, предложенное Scoregraphic по моему другому ответу, и больше предлагается gwell):

a=arg i=2 r=a[1]while a[i]do c=a[i]/a[i+1]v=c*r if v%1==0 then i=2 r=v else i=i+2 end end print(r)

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

C:\Users\--------\Desktop>fractran.lua 108 3 2
243
C:\Users\--------\Desktop>fractran.lua 1296 3 2
6561
C:\Users\--------\Desktop>fractran.lua 108 455 33 11 13 1 11 3 7 11 2 1 3
15625

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

НЕ обрабатывает вектор бонуса: (

Ответ 17

Perl 6: 77 Персонажи (экспериментальные)

sub f(@p,$n is copy){
loop {my$s=first {!($n%(1/$_))},@p or return $n;$n*=$s}}

Новая строка является необязательной. Вызов:

say f([3/2], 1296).Int;
say f([455/33, 11/13, 1/11, 3/7, 11/2, 1/3], 60466176).Int;

Считываемая версия:

sub Fractran (@program, $state is copy) {
  loop {
    if my $instruction = first @program:
      -> $inst { $state % (1 / $inst) == 0 } {
      $state *= $instruction;
    } else {
      return $state.Int;
    }
  }
}

Примечания:

  • Обозначение двоеточия first @program: pointy-sub не работает с текущими реализациями; сначала следует использовать БЛОК, @program.

  • У Rakudo, похоже, есть багги Rat, дающие неверные результаты. Текущий Niecza запускает все тестовые программы правильно и быстро, включая фракцию "бонус".

Ответ 18

Схема: 326

Я думал, что для подачи четности требуется представление Схемы. Я также просто хотел повод сыграть с ним. (Извините за свои рудиментарные знания, я уверен, что это можно оптимизировать, и я открыт для предложений!)

#lang scheme
(define fractran_interpreter
(lambda (state pc program)
    (cond
        ((eq? pc (length program)) 
            (print state))
        ((integer? (* state (list-ref program pc)))
            (fractran_interpreter (* state (list-ref program pc)) 0 program))
        (else
            (fractran_interpreter state (+ pc 1) program)))))

Тесты:

(fractran_interpreter 108 0 '(3/2))

(fractran_interpreter 60466176 0 '(455/33 11/13 1/11 3/7 11/2 1/3))

Я получаю бонус-вектор! (используя Dr. Scheme, выделяя 256 мб)

Ответ 19

Haskell: 116 109 символов

f p x[]=x
f p x((n,d):b)|x*n`mod`d==0=f p(x*n`div`d)p|True=f p x b
main=do{p<-readLn;x<-readLn;print$f p x p}

Это оказалось чем-то вроде подделки входа в Дарио.

Ответ 20

Реализация ссылок в Python

Эта реализация работает на простых факторизациях.

Сначала он декодирует список кортежей с чередованием, кодируя числитель и знаменатель как список кортежей (idx, value), где idx - это число простых чисел (2 - это просто 0, 3 - простое 1, и поэтому д).

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

Этот подход примерно в 5 раз превышает скорость выполнения арифметических операций над большими целыми числами в Python и намного легче отлаживать!

Дальнейшая оптимизация обеспечивается путем построения массива, сопоставляющего каждый простой индекс (переменную) с первым, когда он проверяется в знаменателе дробной части, а затем используя это для построения "jump_map", состоящего из следующей инструкции для выполнить для каждой команды в программе.

def primes():
  """Generates an infinite sequence of primes using the Sieve of Erathsones."""
  D = {}
  q = 2
  idx = 0
  while True:
    if q not in D:
      yield idx, q
      idx += 1
      D[q * q] = [q]
    else:
      for p in D[q]:
        D.setdefault(p + q, []).append(p)
      del D[q]
    q += 1

def factorize(num, sign = 1):
  """Factorizes a number, returning a list of (prime index, exponent) tuples."""
  ret = []
  for idx, p in primes():
    count = 0
    while num % p == 0:
      num //= p
      count += 1
    if count > 0:
      ret.append((idx, count * sign))
    if num == 1:
      return tuple(ret)

def decode(program):
  """Decodes a program expressed as a list of fractions by factorizing it."""
  return [(factorize(n), factorize(d)) for n, d in program]

def check(state, denom):
  """Checks if the program has at least the specified exponents for each prime."""
  for p, val in denom:
    if state[p] < val:
      return False
  return True

def update_state(state, num, denom):
  """Checks the program state and updates it according to an instruction."""
  if check(state, denom):
    for p, val in denom:
      state[p] -= val
    for p, val in num:
      state[p] += val
    return True
  else:
    return False

def format_state(state):
  return dict((i, v) for i, v in enumerate(state) if v != 0)

def make_usage_map(program, maxidx):
  firstref = [len(program)] * maxidx
  for i, (num, denom) in enumerate(program):
    for idx, value in denom:
      if firstref[idx] == len(program):
        firstref[idx] = i
  return firstref

def make_jump_map(program, firstref):
  jump_map = []
  for i, (num, denom) in enumerate(program):
    if num:
      jump_map.append(min(min(firstref[idx] for idx, val in num), i))
    else:
      jump_map.append(i)
  return jump_map

def fractran(program, input, debug_when=None):
  """Executes a Fractran program and returns the state at the end."""
  maxidx = max(z[0] for instr in program for part in instr for z in part) + 1
  state = [0]*maxidx

  if isinstance(input, (int, long)):
    input = factorize(input)

  for prime, val in input:
    state[prime] = val

  firstref = make_usage_map(program, maxidx)
  jump_map = make_jump_map(program, firstref)

  pc = 0
  length = len(program)
  while pc < length:
    num, denom = program[pc]
    if update_state(state, num, denom):
      if num:
        pc = jump_map[pc]
      if debug_when and debug_when(state):
        print format_state(state)
    else:
      pc += 1

  return format_state(state)

Ответ 21

Haskell, 142 символа

Без каких-либо дополнительных библиотек и полного ввода-вывода.

t n f=case f of{(a,b):f'->if mod n b == 0then(\r->r:(t r f))$a*n`div`b else t n f';_->[]}
main=readLn>>=(\f->readLn>>=(\n->print$last$t n f))

Ответ 22

Java, 200 192 179 символов

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

Вот минимальная версия:

class F{public static void main(String[]a){long p=new Long(a[0]);for(int i=1;i<a.length;){long n=p*new Long(a[i++]),d=new Long(a[i++]);if(n%d<1){p=n/d;i=1;}}System.out.print(p);}}

java -cp. F 108 455 33 11 13 1 11 3 7 11 2 1 3

15625

java -cp. F 1296 3 2

6561

Вот очищенная версия:

public class Fractran {

    public static void main(String[] args) {
        long product = new Long(args[0]);

        for (int index = 1; index < args.length;) {
            long numerator = product * new Long(args[index++]);
            long denominator = new Long(args[index++]);

            if (numerator % denominator < 1) {
                product = numerator / denominator;
                index = 1;
            } // if
        } // for

        System.out.print(product);
    }
}

Ответ 23

Схема 73 символа

Моя первая попытка, сделав это с полностью стандартной схемой RS 5 RS, появилась в 104 символах:

(define(f p n)(let l((q p)(n n))(if(null? q)n(let((a(* n(car q))))(if(integer?
a)(l p a)(l(cdr q)n))))))

Работа с несколькими элементами в тестовом векторе:

> (f '(3/2) 1296)
6561
> (f '(455/33 11/13 1/11 3/7 11/2 1/3) 60466176)
7888609052210118054117285652827862296732064351090230047702789306640625

Если вы предполагаете, что λ привязан к lambda и let/cc определен (как они находятся в схеме PLT, см. ниже определения для выполнения этого в схемах, которые не определяют их), тогда я могу адаптируйте второе решение Ruby в Иордании на схему, которая выводится до 73 символов (обратите внимание, что порядок аргументов является обратным для моего первого решения, но то же самое, что и у Jordan; версия, которая сохраняет один символ).:

(define(f n p)(let/cc r(map(λ(i)(if(integer?(* n i))(r(f(* n i)p))))p)n))

Если у меня нет предопределенных λ и let/cc, то это число равно 111 символам (88, если определена довольно обычная аббревиатура call/cc):

(define(f n p)(call-with-current-continuation(lambda(r)(map(lambda(i)(if(integer?(*
n i))(r(f(* n i)p))))p)n)))

Определения λ и let/cc:

(define-syntax λ 
  (syntax-rules () 
    ((_ . body) (lambda . body)))

(define-syntax let/cc 
  (syntax-rules () 
    ((_ var . body) (call-with-current-continuation (lambda (var) . body)))))

Ответ 24

Немного поздно... dc 84 chars

Просто для удовольствия a dc solution (OpenBSD)

[ss1sp]s1[Rlp1+sp]s2?l1xz2/sz[z2/ds_:bl_:az0<L]dsLx
1[;als*lp;b~0=1e2lpdlz!<L]dsLxlsp

Он обрабатывает все случаи:

$ dc fractran.dc  
455 33 11 13 1 11 3 7 11 2 1 3 60466176
7888609052210118054117285652827862296732064351090230047702789306640625

Ответ 25

Я не могу оставлять комментарии, но здесь "немного" более короткая версия RCIX С# версии (я считаю, что 7 символов короче)

using System;namespace T{static class P{static void Main(string[] a){int i=1;Func<string,decimal> d=Convert.ToDecimal;var c=d(a[0]);while(i+1<=a.Length){var f=d(a[i])/d(a[++i]);if((f*c)%1==0){i=1;c*=f;}else i++;}Console.Write(c);}}}

который использует

Func<string,decimal> d=Convert.ToDecimal

и вызывает d(); вместо

using b=Convert;

и повторно вызывая b.ToDecimal();.

Я также удалил ненужную пару фигурных скобок вокруг инструкции else, чтобы получить 1 char:).

Я также заменил a[i+1] на a[++i], а в следующем корпусе я заменил i+=2 на i++, чтобы получить еще один char: P