Prolog (SWI) , 134 128 127 124 bajtów
Ta odpowiedź jest częścią współpracy między mną a 0 '. Oboje nad tym pracowaliśmy razem, jedynym powodem, dla którego to publikuję, jest to, że wygrałem Rock, Paper, Scissors.
\Q-->{Q=1};"(",\N,")",\B,{findnsols(N,I,(between(2,inf,I),\+ (between(3,I,U),0=:=I mod(U-1))),L)->append(_,[Y],L),Q is Y*B}.
Wypróbuj online!
Wyjaśnienie
Ta odpowiedź jest doskonałym przykładem tego, co sprawia, że gra w golfa w prologu jest przyjemnością.
Ta odpowiedź korzysta z potężnego systemu Prologs dla określonych gramatyk klauzulowych. Oto nasza gramatyka trochę niepolita.
head(1)-->[].
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Pierwsza zasada budowy to:
head(1)-->[].
Mówi to Prologowi, że pusty ciąg odpowiada 1.
Nasza druga zasada budowy jest nieco bardziej złożona.
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
To mówi nam, że każdy niepusty ciąg zawiera nawiasy wokół klauzuli z tymi samymi regułami, na prawo od klauzuli z tymi samymi regułami.
Mówi nam również, że wartość tej klauzuli ( Q
) jest zgodna z regułą:
{prime(N,Y),Q is Y*B}
Podział tego Q
jest iloczynem 2 liczb Y
i B
. B
jest tylko wartością klauzuli po lewej stronie i Y
jest N
liczbą pierwszą, gdzie N
jest wartość klauzuli w nawiasach.
Ta reguła obejmuje obie reguły tworzenia drzewa czynników
- Łączenie się mnoży
- Obudowa zajmuje n-tą liczbę pierwszą
Teraz definicje predykatów. W wersji bez golfa rozgrywane są dwa predykaty (w moim rzeczywistym kodzie przesłałem je do przodu). Dwa istotne tutaj predykaty isprime/1
, które pasują do liczby pierwszej, i prime/2
która, podana N
i Y
, pasuje do iff, Y
jest N
liczbą pierwszą. Najpierw mamy
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
To dzieło o dość standardowej definicji pierwotności, nalegamy, aby nie było liczby między 2 a I
, w tym 2, ale nie I
dzieli I
.
Następny predykat jest również dość prosty
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Używamy, findnsols
aby znaleźć pierwsze N
liczby, które są pierwsze, a następnie zwracamy ostatnią. Sztuczka polega na tym, że chociaż findnsols
nie ma gwarancji znalezienia najmniejszych N
liczb pierwszych, ze względu na sposób, w jaki obsługuje SWI between
, zawsze znajdzie mniejsze liczby pierwsze. Oznacza to jednak, że musimy ciąć, aby zapobiec znalezieniu większej liczby pierwszych.
Golfa
Możemy dwukrotnie podać przyczynę w naszym kodzie. Ponieważ isprime
jest używany tylko wtedy, gdy jego definicja może zostać przeniesiona do wewnątrz prime
. Kolejnym krokiem jest przejście prime
bezpośrednio do DCG, ale ponieważ używamy wycięcia, prime
aby zapobiec findnsols
tworzeniu zbyt wielu liczb pierwszych, mamy trochę problemu. Cięcie tnie cały DCG zamiast tylko tego, czego chcemy. Po trochę kopania dokumentacji odkryliśmy, że once/1
można go użyć do wycięcia tylko tej części, ale nie całego DCG. Jednak więcej kopania dokumentacji ujawniło, że ->
operator może być również wykorzystany do wykonania podobnego zadania. ->
Operator jest grubsza odpowiada ,!,
więc przenieśliśmy naszą cięcie na drugiej stronie append/3
i zastąpił go ->
.
W SWI-Prolog predykaty (i reguły) można podawać operatorom jako nazwy, co pozwala nam upuścić normalnie wymagane nawiasy. W ten sposób możemy zaoszczędzić 6 bajtów, wywołując regułę \
.