Код Гольф: Fractran

Вызов

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

Fractran

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

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

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

Тест Векторы

: [(3, 2)]
вход: 72 (2332)
выход: 243 (35)

: [(3, 2)]
вход: 1296 (2434)
выход: 6561 (38)

: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)]
вход: 72 (2332)
выход: 15625 (56)

бонус тест вектор:

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

: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)]
вход: 60466176 (210310)
выход: 7888609052210118054117285652827862296732064351090230047702789306640625 (5100)

Представления И Оценка

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

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

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

победители

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

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

почетные грамоты на:

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

25 ответов


Фрактран - 1779 фракций

(Edit: исправлено)

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

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

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

во-первых, мы кодируем дробь m/n = p_0^a0... p_k^ak by:

  • начните с 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

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

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

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

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

./fracc interpreter.frp

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

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

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

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

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


,

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

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

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

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


или!

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

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


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]

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

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

интерпретируемая программа встроена в виде списка базовых 10 цифр внутри одной базы 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 итераций. Вот полный вывод.

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

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

# 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's 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's 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 }
}

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

состояние передается в %rdi, программа (указатель на массив дробей с нулевым завершением) - в %rsi. Результаты возвращаются в %Ракс в обычные 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     ,    %rcx    // otherwise, advance to next instruction
    jmp     1b
9:  mov     %rdi,   %rax    // copy result for return
    ret

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


Руби, 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

Руби, 53 52 использование Rational

вдохновленный решение gnibbler я смог получить решение, используя 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 символов.)


Golfscript - 32

    
    {:^{1=1$\%!}?.1={~@\/*^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

Хаскелл, 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

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

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

Python-53

улучшение благодаря Paul

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()

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]

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


один Javascript: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;
};

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()

C#:

опрятный версия:

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 chars (без объявлений пространства имен или любого из них, только один оператор using (не system) и основная функция):

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

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

вызовите как groovy фрактал.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

Perl,84 82 char

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

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

принимает 110 символов, чтобы пройти бонусный тест:

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

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}

это закончилось как своего рода подделка входа Дарио.


схеме: 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))

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


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

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

не обрабатывает бонус вектор печально : (


ссылочная реализация в 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's 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)

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

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

Newline является необязательным. Звоните как:

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;
    }
  }
}

Примечания:

  1. двоеточие обозначение first @program: pointy-sub не работает в текущих реализациях; вместо этого должен использоваться первый блок @program.

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


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))

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);
    }
}

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

моя первая попытка сделать это с полностью стандартным R5схема 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 символа (обратите внимание, что порядок аргументов является обратным моему первому решению, но таким же, как у Джордана; в этой версии это сохраняет один символ).:

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

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

(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)))))

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

просто для удовольствия dc решение (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

Я еще не могу оставить комментарии, но вот" немного " более короткая версия C# версии 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 символ :).

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