Вопрос

Соревнование

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

Фрактран

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

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

Подробную информацию о том, как и почему работает Фрактран, см. запись об эсоланге и эта запись по хорошей/плохой математике.

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

Программа: [(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. Если вы фанат Джей, извините!

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

Победители

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

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

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

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

Решение

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

(Редактировать:зафиксированный)

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

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

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

Сначала мы кодируем дробь 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^15108 кодируется в 500.Итак, мы проходим

2^{2^15} 3^500 5

в программу.Тогда вывод имеет вид

2^(program) 3^(output)

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

2^{2^15} 3^3125

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

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

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

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

./fracc interpreter.frp

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

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

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

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

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


Однако,

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

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

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

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


Или!

Если вы чувствуете вдохновение и Действительно Если вы хотите увидеть интерпретацию этого интерпретатора, вы можете написать «умный» интерпретатор Fractran, основанный на типе вывода 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.

Интерпретируемая программа встраивается в виде списка цифр по основанию 10 внутри одного числа по основанию 11, используя цифру «а» для обозначения границы, за исключением самого конца.Программа сложения [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 допустил ошибку, когда я попробовал второй тестовый вектор (но я думаю, что он будет работать в реализации Ника, использующей векторы простой экспоненты).

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

# 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.Результаты возвращаются в %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 для минимизированной версии.

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

Вдохновлен решение гнибблера Мне удалось найти решение с помощью 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)

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

Гольфскрипт - 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

Питон, 83 82 81 72 70 символов.

Удобно вводить данные в виде дробей. Объекты Fraction.Та же идея, что и в решении 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

С, 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

Питон - 53

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

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

тестовые примеры

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)

Питон - 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()

Ф#: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#):

> 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

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

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

Питон, 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()

С#:

Удобная версия:

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 (не системный) и функция Main):

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

классный, 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

Перл, 84 82 символа

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

@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

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

Я получаю бонусный вектор! (с помощью Dr.Схема, выделение 256 мб)

Луа:

Аккуратный код:

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, значение), где 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)

Перл 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;
    }
  }
}

Примечания:

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

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

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

Джава, 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.Ж 108 455 33 11 13 1 11 3 7 11 2 1 3

15625

Java -CP.Ф 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 символа

Моя первая попытка сделать это с помощью совершенно стандартного R.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 к Scheme, который содержит 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, если довольно распространен 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)))))

Немного поздно...84 символа постоянного тока

Просто для развлечения а 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

Я пока не могу оставлять комментарии, но вот «немного» более короткая версия версии RCIX на C# (думаю, она на 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] и в следующем теле else я заменил i+=2 с i++ чтобы получить еще одного персонажа :P

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top