Гольф кода: Fractran

Класс StringBuffer поддерживает массив символов для содержания содержания строк, которые Вы связываете, тогда как + метод создает новую строку каждый раз его названный и добавляет эти два параметра (param1 + param2).

StringBuffer быстрее, потому что 1. это могло бы быть в состоянии использовать свой уже существующий массив для concat/store все строки. 2. даже если они не помещаются в массив, быстрее для выделения большего вспомогательного массива затем для генерации новых Строковых объектов для каждого воскрешения.

49
задан 8 revs, 3 users 92% 23 May 2017 в 00:31
поделиться

25 ответов

Fractran - 1779 фракций

(Edit: fixed)

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

Похоже, ТАК победил ' Не разрешите мне публиковать что-то до тех пор, пока это, поэтому я разместил исходный код Fractran здесь .

Входные данные указаны следующим образом:

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

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

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

p_0^F0 p1^F1 ...

. Наконец, ввод интерпретатору задается следующим образом:

2^(program) 3^(input) 5

где программа и ] 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, мою (C ++) программу можно найти здесь [tar.gz] . В ошеломляющей демонстрации собачьего кормления (и демонстрации) я использовал свой синтаксический анализатор C ++ YAML yaml-cpp , так что вам придется скачать и связать с этим. И для yaml-cpp, и для "компилятора" вам понадобится CMake для создания кроссплатформенного make-файла.

Эта программа используется:

./fracc interpreter.frp

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

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

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

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

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


Однако,

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

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

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

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


Или!

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

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

45
ответ дан 7 November 2019 в 11:14
поделиться

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))
1
ответ дан 7 November 2019 в 11:14
поделиться

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);
    }
}
1
ответ дан 7 November 2019 в 11:14
поделиться

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

Примечания:

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

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

2
ответ дан 7 November 2019 в 11:14
поделиться

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

Тесты:

(фрактран_интерпреттер 108 0 '(3/2))

(фрактран_интерпреттер 60466176 0' (455/33 11/13 1/11 3/7 11/2 1/3))

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

2
ответ дан 7 November 2019 в 11:14
поделиться

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}

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

2
ответ дан 7 November 2019 в 11:14
поделиться

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
2
ответ дан 7 November 2019 в 11:14
поделиться

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

Вызов как отличный фрактал.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
2
ответ дан 7 November 2019 в 11:14
поделиться

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()
4
ответ дан 7 November 2019 в 11:14
поделиться

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

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

К сожалению, НЕ обрабатывает бонусный вектор: (

2
ответ дан 7 November 2019 в 11:14
поделиться

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

Вот расширенная версия, использующая шаблон соответствия с | case вместо функции :

//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]

Edit , давайте немного повеселимся и вычислим несколько простых чисел (см. Связанную страницу в начальном посте). Я написал новую функцию 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]

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

5
ответ дан 7 November 2019 в 11:14
поделиться

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

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()
5
ответ дан 7 November 2019 в 11:14
поделиться

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
6
ответ дан 7 November 2019 в 11:14
поделиться

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

Удобно вводить как объекты fractions.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
6
ответ дан 7 November 2019 в 11:14
поделиться

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
ответ дан 7 November 2019 в 11:14
поделиться

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
12
ответ дан 7 November 2019 в 11:14
поделиться

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

Вдохновленный решением Гнибблера Мне удалось найти решение. используя 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 символов.)

16
ответ дан 7 November 2019 в 11:14
поделиться

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

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

20
ответ дан 7 November 2019 в 11:14
поделиться

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 символ (без объявлений пространства имен или чего-либо еще, только один оператор 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
4
ответ дан 7 November 2019 в 11:14
поделиться

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

Входные данные имеют формат [[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;
};
4
ответ дан 7 November 2019 в 11:14
поделиться

Я пока не могу оставлять комментарии, но вот «немного» более короткая версия 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

0
ответ дан 7 November 2019 в 11:14
поделиться

Reference implementation in Python

This implementation operates on prime factorizations.

First, it decodes a list of fraction tuples by encoding the numerator and denominator as a list of (idx, value) tuples, where idx is the number of the prime (2 is prime 0, 3 is prime 1, and so forth).

The current state is a list of exponents for each prime, by index. Executing an instruction requires first iterating over the denominator, checking if the indexed state element is at least the specified value, then, if it matches, decrementing state elements specified in the denominator, and incrementing those specified in the numerator.

This approach is about 5 times the speed of doing arithmetic operations on large integers in Python, and is a lot easier to debug!

A further optimisation is provided by constructing an array mapping each prime index (variable) to the first time it is checked for in the denominator of a fraction, then using that to construct a 'jump_map', consisting of the next instruction to execute for each instruction in the program.

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)
2
ответ дан 7 November 2019 в 11:14
поделиться

Вы можете сделать это так:

$animal = 'cow';
$sounder = "sound_$animal";
print ${sounder}();

Однако гораздо лучше было бы использовать массив:

$sounds = array('dog' => sound_dog, 'cow' => sound_cow);

$animal = 'cow';
print $sounds[$animal]();

Одно из преимуществ метода массива заключается в том, что когда вы возвращаетесь к своему коду шесть месяцев спустя и задаетесь вопросом: «Ну и дела, где эта функция sound_cow используется?» вы можете ответить на этот вопрос с помощью простого текстового поиска вместо того, чтобы следовать всей логике, которая создает имена переменных функций на лету.

тогда этот состоит из 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)))))
1
ответ дан 7 November 2019 в 11:14
поделиться

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 ^ Intervalted_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 итераций. Вот полный вывод .

К сожалению, при попытке второго тестового вектора произошла ошибка 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 }
}
41
ответ дан 7 November 2019 в 11:14
поделиться

Немного поздно ... dc 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
1
ответ дан 7 November 2019 в 11:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: