Miller-Rabin Strong Pseudoprimes


16

Dato un numero intero non negativo N, genera il numero intero dispari positivo più piccolo che è un forte pseudoprime a tutte le Nprime basi prime.

Questa è la sequenza OEIS A014233 .

Casi di test (uno indicizzato)

1       2047
2       1373653
3       25326001
4       3215031751
5       2152302898747
6       3474749660383
7       341550071728321
8       341550071728321
9       3825123056546413051
10      3825123056546413051
11      3825123056546413051
12      318665857834031151167461
13      3317044064679887385961981

Casi di prova per N > 13 non sono disponibili perché tali valori non sono stati ancora trovati. Se riesci a trovare i termini successivi nella sequenza, assicurati di inviarli a OEIS!

Regole

  • Puoi scegliere di assumere Ncome valore indicizzato zero o un indice indicizzato.
  • È accettabile che la tua soluzione funzioni solo per i valori rappresentabili nell'intervallo di numeri interi della tua lingua (quindi fino a N = 12numeri interi a 64 bit senza segno), ma la tua soluzione deve teoricamente funzionare per qualsiasi input supponendo che la tua lingua supporti numeri interi di lunghezza arbitraria.

sfondo

Qualsiasi numero intero pari positivo xpuò essere scritto nella forma in x = d*2^scui dè dispari. de spuò essere calcolato dividendo ripetutamente nper 2 fino a quando il quoziente non è più divisibile per 2. dè quel quoziente finale ed sè il numero di volte in cui 2 dividen .

Se un numero intero positivo nè primo, allora il piccolo teorema di Fermat afferma:

Fermat

In qualsiasi campo finito Z/pZ (dov'è pun numero primo), le uniche radici quadrate di 1sono 1e -1(o, equivalentemente, 1e p-1).

Possiamo usare questi tre fatti per dimostrare che una delle seguenti due affermazioni deve essere vera per un numero primo n(dove d*2^s = n-1ed rè un numero intero in [0, s)):

Condizioni di Miller-Rabin

Il test di primalità di Miller-Rabin opera testando il contrappeso della rivendicazione di cui sopra: se esiste una base atale che entrambe le condizioni di cui sopra sono false, allora nnon è primo. Quella base asi chiama testimone .

Ora, testare ogni base [1, n)sarebbe proibitivo in termini di tempo di calcolo per grandi n. Esiste una variante probabilistica del test di Miller-Rabin che verifica solo alcune basi scelte casualmente nel campo finito. Tuttavia, è stato scoperto che aè sufficiente testare solo basi prime e quindi il test può essere eseguito in modo efficiente e deterministico. In effetti, non tutte le basi prime devono essere testate: è richiesto solo un certo numero e quel numero dipende dalla dimensione del valore testato per la primalità.

Se viene testato un numero insufficiente di basi prime, il test può produrre falsi positivi - numeri interi compositi dispari in cui il test non riesce a dimostrare la loro composizione. In particolare, se una base anon riesce a provare la composizione di un numero composito dispari, quel numero viene chiamato base pseudoprime fortea . Questa sfida riguarda la ricerca di numeri compositi dispari che sono psuedoprimi forti su tutte le basi minori o uguali al Nnumero primo (che equivale a dire che sono pseudoprimi forti su tutte le basi primi inferiori o uguali al Nnumero primo) .


1
Post sandbox (ora eliminato)
Mego

Un algoritmo che testa tutti i valori dispari da 1 a risultato di una pseudo-primalità forte è consentito dalle regole?
user202729

@ user202729 Non vedo perché non lo sia. Cosa ti farebbe pensare che sia?
Mego

Suggerirei di rendere questa una domanda a codice più veloce perché la maggior parte delle risposte sarà semplicemente una forza bruta.
Neil A.

@NeilA. Non sono d'accordo che questo sarebbe meglio come codice più veloce. Mentre è vero che le risposte saranno quasi certamente una forza bruta (poiché non è stato ancora sviluppato un altro algoritmo e non mi aspetto che PPCG lo faccia), il golf del codice è molto più semplice, ha una barriera molto più bassa per l'ingresso (dal momento che i presentatori può segnare le proprie soluzioni), non mi richiede di correre e segnare tutte le soluzioni (e gestire i runtime esorbitanti), e il problema è abbastanza interessante come una sfida di golf.
Mego,

Risposte:


4

C, 349 295 277 267 255 byte

N,i;__int128 n=2,b,o,l[999];P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}main(r){for(P(scanf("%d",&N));r|!b;)for(++n,b=i=N;i--&&b;){for(b=n-1,r=0;~b&1;b/=2)++r;for(o=1;b--;o=o*l[i]%n);for(b=o==1;r--;o=o*o%n)b|=o>n-2;for(o=r=1;++o<n;r&=n%o>0);}printf("%llu",n);}

Accetta 1 input basato su stdin, ad esempio:

echo "1" | ./millerRabin

Certamente non scoprirà presto nessun nuovo valore nella sequenza, ma farà il lavoro. AGGIORNAMENTO: ora ancora più lento!

  • Leggermente più veloce e più breve, con ispirazione dalla risposta di Neil A ( a^(d*2^r) == (a^d)^(2^r))
  • Significativamente più lento dopo aver realizzato che tutte le soluzioni a questa sfida saranno dispari, quindi non è necessario imporre esplicitamente che controlliamo solo numeri dispari.
  • Ora sto usando GCC __int128, che è più breve di unsigned long longquando lavora anche con numeri più grandi! Anche su macchine little-endian, la stampa con%llu funziona ancora bene.

Meno-minified

N,i;
__int128 n=2,b,o,l[999];
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}
main(r){
    for(P(scanf("%d",&N));r|!b;)
        for(++n,b=i=N;i--&&b;){
            for(b=n-1,r=0;~b&1;b/=2)++r;
            for(o=1;b--;o=o*l[i]%n);
            for(b=o==1;r--;o=o*o%n)b|=o>n-2;
            for(o=r=1;++o<n;r&=n%o>0);
        }
    printf("%llu",n);
}

Guasto (obsoleto)

unsigned long long                  // Use the longest type available
n=2,N,b,i,d,p,o,                    // Globals (mostly share names with question)
l[999];                             // Primes to check (limited to 999, but if you
                                    // want it to be "unlimited", change to -1u)
m(){for(o=1;p--;o=o*l[i]%n);}       // Inefficiently calculates (l[i]^p) mod n

// I cannot possibly take credit for this amazing prime finder;
// See /codegolf//a/5818/8927
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}

main(r){
    for(
        P(scanf("%llu",&N));        // Read & calculate desired number of primes
        r|!b;){                     // While we haven't got an answer:
        n=n+1|1;                    // Advance to next odd number
        for(b=1,i=N;i--&&b;){       // For each prime...
            for(d=n-1,r=0;~d&1;d/=2)++r; // Calculate d and r: d*2^r = n-1
            // Check if there exists any r such that a^(d*2^r) == -1 mod n
            // OR a^d == 1 mod n
            m(p=d);
            for(b=o==1;r--;b|=o==n-1)m(p=d<<r);
            // If neither of these exist, we have proven n is not prime,
            // and the outer loop will keep going (!b)
        }
        // Check if n is actually prime;
        // if it is, the outer loop will keep going (r)
        for(i=r=1;++i<n;r&=n%i!=0);
    }
    printf("%llu",n);               // We found a non-prime; print it & stop.
}

Come accennato, questo utilizza input basato su 1. Ma per n = 0 produce 9, che segue la sequenza correlata https://oeis.org/A006945 . Non più; ora si blocca su 0.

Dovrebbe funzionare per tutti n (almeno fino a quando l'output non raggiunge 2 ^ 64) ma è incredibilmente lento. L'ho verificato su n = 0, n = 1 e (dopo molte attese), n = 2.


Faccio un passo avanti nella mia soluzione, e poi mi fai un favore ... Bello!
Neil A.

@NeilA. Scusate! Stavo giocando con tipi int più brevi prima di pubblicare l'aggiornamento. Sono sicuro che troverai 2 byte da qualche parte; questo si sta rivelando sorprendentemente competitivo considerando che si tratta di 2 diverse lingue non golf: D
Dave

3

Python 2, 633 465 435 292 282 275 256 247 byte

0-indicizzato

Metti in discussione la tua implementazione e prova qualcosa di nuovo

La conversione da una funzione a un programma salva alcuni byte in qualche modo ...

Se Python 2 mi dà un modo più breve di fare la stessa cosa, userò Python 2. La divisione è di default un numero intero, quindi un modo più semplice di dividere per 2 e printnon necessita di parentesi.

n=input()
f=i=3
z={2}
a=lambda:min([i%k for k in range(2,i)])
while n:
 if a():z|={i};n-=1
 i+=2
while f:
 i+=2;d,s,f=~-i,0,a()
 while~d&1:d/=2;s+=1
 for y in z:
  x=y**d%i
  if x-1:
   for _ in[]*s:
    x*=x
    if~x%i<1:break
   else:f=1
print i

Provalo online!

Python è notoriamente lento rispetto ad altre lingue.

Definisce un test di divisione di prova per la correttezza assoluta, quindi applica ripetutamente il test di Miller-Rabin fino a quando non viene trovato uno pseudoprime.

Provalo online!

EDIT : Finalmente golfed la risposta

EDIT : utilizzato minper il test di primalità della divisione di prova e modificato in a lambda. Meno efficiente, ma più breve. Inoltre non ho potuto fare a meno di me stesso e ho usato un paio di operatori bit a bit (nessuna differenza di lunghezza). In teoria dovrebbe funzionare (leggermente) più velocemente.

MODIFICARE : Grazie @Dave. Il mio editore mi ha trollato. Pensavo di usare le schede ma invece veniva convertito in 4 spazi. Inoltre ha passato praticamente ogni singolo suggerimento di Python e l'ha applicato.

EDIT : Passato all'indicizzazione 0, mi consente di salvare un paio di byte con la generazione dei numeri primi. Anche ripensato un paio di confronti

EDIT : utilizzata una variabile per memorizzare il risultato dei test anziché le for/elseistruzioni.

EDIT : spostato lambdaall'interno della funzione per eliminare la necessità di un parametro.

EDIT : convertito in un programma per salvare byte

EDIT : Python 2 mi salva byte! Inoltre non devo convertire l'input inint


+1 per il modo in cui hai gestito a^(d*2^r) mod n!
Dave,

Sei consapevole che puoi usare il rientro a spazio singolo (o a scheda singola) in Python per salvare ... un sacco di byte, in realtà
Dave,

@Dave: utilizza 1 tab per livello di rientro
Neil A.

Penso che il tuo IDE ti stia rovinando e risparmiando spazi mentre ti dice che sta usando le schede; quando li sostituisco per spazi singoli, ottengo un conteggio di byte di soli 311 byte! Provalo online!
Dave,

@Dave: Ok, è strano, grazie, aggiornerò la risposta.
Neil A.

2

Perl + Math :: Prime :: Util, 81 + 27 = 108 byte

1 until!is_provable_prime(++$\)&&is_strong_pseudoprime($\,2..nth_prime($_));$_=""

Esegui con -lpMMath::Prime::Util=:all(penalità di 27 byte, ahi).

Spiegazione

Non è solo Mathematica che ha praticamente tutto. Perl ha CPAN, uno dei primi grandi repository di librerie, e ha una vasta collezione di soluzioni pronte per attività come questa. Sfortunatamente, non vengono importati (o addirittura installati) per impostazione predefinita, il che significa che in pratica non è mai una buona opzione usarli nel , ma quando uno di questi si adatta perfettamente al problema ...

Eseguiamo numeri interi consecutivi fino a quando non ne troviamo uno che non è primo, e tuttavia un forte pseudoprime a tutte le basi di numeri interi dal 2 al n ° primo. L'opzione della riga di comando importa la libreria che contiene il builtin in questione e imposta anche l'input implicito (su line-at-a-time; Math::Prime::Utilha una propria libreria di bignum incorporata che non ama le nuove righe nei suoi numeri interi). Questo utilizza il trucco Perl standard dell'uso $\(separatore della linea di output) come variabile per ridurre analisi scomode e consentire la generazione implicita dell'output.

Nota che dobbiamo usare is_provable_primequi per richiedere un test primo deterministico, piuttosto che probabilistico. (Soprattutto dato che un primo test probabilistico probabilmente sta usando Miller-Rabin in primo luogo, che non possiamo aspettarci di dare risultati affidabili in questo caso!)

Perl + Math :: Prime :: Util, 71 + 17 = 88 byte, in collaborazione con @Dada

1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..n‌​th_prime$_}{

Esegui con -lpMntheory=:all(penalità di 17 byte).

Questo usa alcuni trucchi del golf Perl che o non conoscevo (apparentemente Math :: Prime :: Util ha un'abbreviazione!), Che conoscevo ma non pensavo di usare ( }{per produrre $\implicitamente una volta, piuttosto che "$_$\"implicitamente ogni riga) , o ne era a conoscenza, ma in qualche modo è riuscito ad applicare erroneamente (rimuovendo le parentesi dalle chiamate di funzione). Grazie a @Dada per avermelo fatto notare. A parte questo, è identico.


Naturalmente una lingua da golf arriva e batte il resto. Molto bene!
Neil A.

Puoi usare ntheoryinvece di Math::Prime::Util. Inoltre, }{invece di ;$_=""dovrebbe andare bene. E puoi omettere lo spazio dopo 1e la parentesi di alcune chiamate di funzione. Inoltre, &funziona invece di &&. Ciò dovrebbe fornire 88 byte:perl -Mntheory=:all -lpe '1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..nth_prime$_}{'
Dada,

Mi ero completamente dimenticato }{. (Stranamente, mi sono ricordato della parentesi, ma è da un po 'che non gioco a Perl e non ricordo le regole per lasciarla fuori.) ntheoryTuttavia, non conoscevo affatto l' abbreviazione.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.