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 Qjest iloczynem 2 liczb Yi B. Bjest tylko wartością klauzuli po lewej stronie i Yjest Nliczbą pierwszą, gdzie Njest 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/2która, podana Ni Y, pasuje do iff, Yjest Nliczbą 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 Idzieli 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, findnsolsaby znaleźć pierwsze Nliczby, które są pierwsze, a następnie zwracamy ostatnią. Sztuczka polega na tym, że chociaż findnsolsnie 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ż isprimejest używany tylko wtedy, gdy jego definicja może zostać przeniesiona do wewnątrz prime. Kolejnym krokiem jest przejście primebezpośrednio do DCG, ale ponieważ używamy wycięcia, primeaby zapobiec findnsolstworzeniu 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/1moż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/3i 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łę \.