Trova il più grande primo fragile


21

Considera la funzione Remove(n, startIndex, count)che rimuove le countcifre dal numero a npartire dalla cifra in posizione startIndex. Esempi:

Remove(1234, 1, 1) = 234
Remove(123456, 2, 3) = 156
Remove(1507, 1, 2) = 07 = 7
Remove(1234, 1, 4) = 0

Chiameremo il numero primo X fragile se ogni possibile Removeoperazione lo rende non primo. Ad esempio, 80651 è un numero primo fragile perché tutti i seguenti numeri non sono numeri primi:

651, 51, 1, 0, 8651, 851, 81, 8, 8051, 801, 80, 8061, 806, 8065

Obbiettivo

Scrivi un programma che trova il più grande primo fragile. Modifica: rimosso il limite di tempo perché c'era un modo relativamente giusto per aggirarlo.

Il punteggio è il fragile numero primo trovato dal tuo programma. In caso di pareggio, vince l'invio precedente.

Regole

  • È possibile utilizzare qualsiasi lingua e librerie di terze parti.
  • Esegui il programma sul tuo hardware.
  • È possibile utilizzare i test probabilistici di primalità.
  • Tutto è nella base 10.

Voci principali

  • 6629 cifre di Qualtagh (Java)
  • 5048 cifre di Emil (Python 2)
  • 2268 cifre di Jakube (Python 2)

Modifica: ho aggiunto la mia risposta.

  • 28164 cifre di Suboptimus Prime, basate sull'algoritmo di Qualtagh (C #)

5
Anche se non codifico la risposta, potrei iniziare la ricerca in un punto molto vicino a un grande primo fragile. Ovviamente, nessuno vuole iniziare la ricerca da 1. Cosa mi impedisce di farlo? Esattamente quanto vicino posso iniziare la mia ricerca prima di essere chiamato per avere una risposta difficile? Ad ogni modo, adoro la sfida.
Rainbolt,

2
@SuboptimusPrime Potresti invece rimuovere del tutto il limite di tempo, perché credo che ad un certo punto sarà così raro che sarà comunque un'impresa trovare il prossimo. (Simile a codegolf.stackexchange.com/questions/41021/… )
Martin Ender,


7
Stai ancora lasciando in svantaggio coloro che hanno computer più lenti
John Dvorak,

11
Mi ci è voluto un tempo imbarazzante per capire che "Scrivi un programma che trova il primo fragile più grande" non significava "Esiste un primo fragile più grande. Scrivi un programma che lo trova". Immagino di aver fatto troppo Project Euler. :-P
ruakh,

Risposte:


9

Java - 3144 3322 6629 cifre

6 0{3314} 8969999

6 0{6623} 49099

Questa soluzione si basa sulla risposta di FryAmTheEggman .

  1. L'ultima cifra è 1 o 9.
  2. Se l'ultima cifra è 1, la precedente è 0, 8 o 9.
  3. Se l'ultima cifra è 9, una precedente è 0, 4, 6 o 9.
  4. ...

E se scavassimo più a fondo?

Diventa una struttura ad albero:

                        S
             -----------------------
             1                     9
    ------------------         ----------------
    0           8    9         0    4    6    9
---------     -----
0   8   9      ...

Chiamiamo il numero R giusto composito se R e tutti i suoi finali sono compositi.

Esamineremo tutti i giusti numeri compositi in modo molto ampio: 1, 9, 01, 81, 91, 09, 49, 69, 99, 001, 801, 901 ecc.

I numeri che iniziano con zero non vengono controllati per primalità ma sono necessari per creare ulteriori numeri.

Cercheremo un numero target N nella forma X00 ... 00R, dove X è uno di 4, 6, 8 o 9 e R è il giusto composto. X non può essere primo. X non può essere 0. E X non può essere 1 perché se R termina con 1 o 9, N contiene 11 o 19.

Se XR contiene numeri primi dopo l'operazione "rimuovi", anche XYR li contenga per qualsiasi Y. Quindi non dovremmo attraversare i rami a partire da R.

Lascia che X sia una costante, diciamo 6.

pseudocodice:

X = 6;
for ( String R : breadth-first-traverse-of-all-right-composites ) {
  if ( R ends with 1 or 9 ) {
    if ( remove( X + R, i, j ) is composite for all i and j ) {
      for ( String zeros = ""; zeros.length() < LIMIT; zeros += "0" ) {
        if ( X + zeros + R is prime ) {
          // At this step these conditions hold:
          // 1. X + 0...0 is composite.
          // 2. 0...0 + R = R is composite.
          // 3. X + 0...0 + R is composite if 0...0 is shorter than zeros.
          suits = true;
          for ( E : all R endings )
            if ( X + zeros + E is prime )
              suits = false;
          if ( suits )
            print R + " is fragile prime";
          break; // try another R
                 // because ( X + zeros + 0...0 + R )
                 // would contain prime ( X + zeros + R ).
        }
      }
    }
  }
}

Dovremmo limitare la quantità di zeri perché potrebbe richiedere troppo tempo per trovare un numero primo nella forma X + zeri + R (o per sempre se tutti sono composti).

Il vero codice è abbastanza dettagliato e può essere trovato qui .

Il test di primalità per i numeri nel lungo intervallo int viene eseguito dalla variante deterministica del test di Miller. Per i numeri BigInteger viene eseguita prima una divisione di prova e poi il test BailliePSW. È probabilistico ma abbastanza certo. Ed è più veloce del test di Miller-Rabin (dovremmo fare molte iterazioni per numeri così grandi in Miller-Rabin per ottenere abbastanza precisione).

Modifica: il primo tentativo è stato errato. Dovremmo anche ignorare i rami che iniziano con R se X0 ... 0R è primo. Quindi X0 ... 0YR non sarebbe fragile primo. Quindi è stato aggiunto un controllo aggiuntivo. Questa soluzione sembra essere corretta.

Modifica 2: aggiunta un'ottimizzazione. Se (X + R) è divisibile per 3, anche (X + zeri + R) è divisibile per 3. Quindi (X + zeri + R) non può essere primo in questo caso e tali R possono essere saltate.

Modifica 3: non è stato necessario saltare le cifre primi se non si trovano nell'ultima o nella prima posizione. Quindi finali come 21 o 51 sono ok. Ma non cambia nulla di molto.

conclusioni:

  1. La mia ultima risposta è stata la verifica della fragilità per 100 minuti. La ricerca della risposta (verificando tutte le varianti precedenti) ha richiesto circa 15 minuti. Sì, non ha senso limitare il tempo di ricerca (possiamo iniziare la ricerca dal numero di destinazione, quindi il tempo sarebbe zero). Ma potrebbe essere significativo limitare i tempi di controllo come in questa domanda .
  2. La risposta 60 ... 049099 ha la cifra 4 al centro. Se l'operazione "rimuovi" la tocca, il numero diventa divisibile per 3. Quindi dovremmo controllare le operazioni di rimozione sui lati sinistro e destro. Il lato destro è troppo corto. La lunghezza del lato sinistro è quasi n = lunghezza (N).
  3. I test di primalità come BPSW e Miller-Rabin utilizzano una quantità costante di esponenziali modulari. La sua complessità è O (M (n) * n) secondo questa pagina , dove M (n) è la complessità della moltiplicazione. Java utilizza gli algoritmi Toom-Cook e Karatsuba ma prendiamo l'algoritmo studioso per semplicità. M (n) = n 2 . Quindi la complessità del test di primalità è O (n 3 ).
  4. Dovremmo controllare tutti i numeri da length = 6 a 6629. Prendiamo min = 1 e max = n per comunanza. L'intera complessità del controllo è O (1 3 + 2 3 + ... + n 3 ) = O ((n * (n + 1) / 2) 2 ) = O (n 4 ).
  5. La risposta di Emil ha gli stessi sintomi asintotici. Ma il fattore costante è inferiore. La cifra "7" è in mezzo al numero. Il lato sinistro e il lato destro possono essere quasi uguali. Dà (n / 2) 4 * 2 = n 4 / 8. Accelerazione: 8X. I numeri nel modulo 9 ... 9Y9 ... 9 possono essere 1,7 volte più lunghi rispetto al modulo X0 ... 0R con lo stesso tempo di verifica.

1
Grazie per il merito, ma il tuo algoritmo è enormemente più complesso del mio! Ottimo lavoro e benvenuto in PPCG! :)
FryAmTheEggman,

@FryAmTheEggman: grazie per l'idea! È stimolante.
Qualtagh,

L'analisi della complessità del controllo è molto interessante, ma probabilmente è importante anche la complessità della ricerca. Penso che il tuo algoritmo richieda un numero significativamente inferiore di test di primalità di grandi numeri (rispetto a quelli di Emil) per trovare un grande primo fragile. E puoi velocizzare i test di primalità usando una libreria nativa. Sto usando Mpir.NET e il controllo del tuo numero per essere un numero primo fragile richiede solo pochi minuti.
Suboptimus Prime,

13

Python 2 - 126 1221 1337 1719 2268 cifre

999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

'9' * 1944 + '7' + '9' * 323

Ci sono circa len (n) ^ 2 numeri risultanti di Rimuovi (n, startIndex, count). Ho provato a minimizzare quei numeri. Se ci sono molte cifre una accanto all'altra sono uguali, molti di questi numeri risultanti possono essere ignorati, perché appaiono più volte.

Quindi l'ho portato all'estremo, solo 9s e un piccolo primo nel mezzo. Ho anche dato un'occhiata al primo fragile sotto 1 milione e ho visto che ci sono primi così fragili. La ricerca di numeri con 2 9 alla fine funziona davvero bene, non so perché. 1 numero, 3 o 4 9s alla fine porta a numeri primi fragili più piccoli.

Utilizza il modulo pyprimes . Non sono sicuro, se va bene. Utilizza il test miller_rabin, quindi è probabilistico.

Il programma trova questo fragile primo a 126 cifre in circa 1 minuto e per il resto del tempo cerca senza successo.

biggest_found = 80651

n = lambda a,b,c: '9'*a + b + '9'*c

for j in range(1000):
   for digit in '124578':
      for i in range(2000):
         number = int(n(i,digit,j))
         if is_prime(number):
            if (number > biggest_found):
               if all(not is_prime(int(n(i,digit,k))) for k in range(j)):
                  biggest_found = number
                  print(i+j+1, biggest_found)
            break

modificare:

Ho appena visto che hai rimosso il limite di tempo. Eseguirò il programma tutta la notte, forse compaiono alcuni numeri primi fragili davvero grandi.

modifica 2:

Ha reso il mio programma originale più veloce, quindi ancora nessuna soluzione con più di 126 cifre. Quindi sono saltato sul treno e ho cercato x 9s + 1 digit + y 9s. Il vantaggio è che devi controllare i numeri O (n) per la primalità, se hai risolto y. Trova un 1221 piuttosto rapidamente.

modifica 3:

Per il numero di 2268 cifre uso lo stesso programma, ho diviso il lavoro solo su più core.


3
"in circa 1 minuto" - scusa, devo segnalare un "bug" di pluralizzazione. : P
hichris123,

La natura probabilistica del mugnaio-rabin è ciò che mi stava mordendo per le mie ultime voci. Potresti voler verificare anche con un altro algoritmo.
John Meacham,

Perché controlli solo che i numeri formati dalla rimozione delle cifre dalla fine siano compositi? Perché non controllare i numeri formati rimuovendo le cifre dalla parte anteriore?
isaacg,

1
Perché ho controllato questi prima nel ciclo 'for i'. Qui aggiungo 9s all'inizio e faccio un primo controllo. Quando trovo il primo numero primo di questo modulo, so che tutti i numeri con meno 9s all'inizio non sono primi. E dopo aver verificato la rimozione di 9s alla fine, mi fermo (pausa), perché ora ogni numero ha un numero primo e quindi non è primo.
Jakube,

Ah, molto intelligente.
isaacg,

7

Python 2.7 - 429623069 99993799

Finora nessuna ottimizzazione. Basta usare alcune osservazioni banali sui numeri primi fragili (grazie a Rainbolt in chat):

  1. I numeri primi fragili devono terminare con 1 o 9 (i Primi non sono pari e il numero finale non deve essere primo)
  2. I numeri primi fragili che terminano con 1 devono iniziare con 8 o 9 (il primo numero non può essere primo e 11, 41 e 61 e sono tutti numeri primi)
  3. I numeri primi fragili che terminano con 9 devono iniziare con 4,6 o 9 (vedere il ragionamento per 1, ma solo 89 è primo)

Sto solo cercando di far rotolare la palla :)

Questo tecnicamente ha una durata leggermente superiore a 15 minuti, ma controlla un solo numero nei tempi supplementari.

is_primeè preso da qui (isaacg lo ha usato qui ) ed è probabilistico.

def substrings(a):
    l=len(a)
    out=set()
    for i in range(l):
        for j in range(l-i):
            out.add(a[:i]+a[len(a)-j:])
    return out

import time

n=9
while time.clock()<15*60:
    if is_prime(n):
        if not any(map(lambda n: n!='' and is_prime(int(n)), substrings(`n`))):
            print n
    t=`n`
    if n%10==9 and t[0]=='8':n+=2
    elif n%10==1 and t[0]!='8':n+=8
    elif t[0]=='1' or is_prime(int(t[0])):n+=10**~-len(t)
    else:n+=10

Solo una nota, quando inizio con questo n=429623069mi alzo 482704669. La cifra in più sembra davvero uccidere questa strategia ...


Non male come inizio! Anche se sembra che is_prime esegua un controllo deterministico completo per i valori a 32 bit, che è un po 'eccessivo. Penso che il metodo is_prime potrebbe funzionare più velocemente se commenterai l'intera parte della divisione di prova.
Suboptimus Prime,

@SuboptimusPrime Oh, grazie. Non l'ho nemmeno guardato: P
FryAmTheEggman,

@SuboptimusPrime Penso che il controllo deterministico completo sia più veloce per valori piccoli perché l'autore ha definito i passaggi da prendere tra i fattori candidati. Grazie ancora per l'idea, ma sembra molto più veloce quando lo lasci in :)
FryAmTheEggman

Piccola correzione alla tua risposta: 91 = 13x7, quindi 91 è composito e i numeri primi fragili che terminano con 1 possono effettivamente iniziare con 9.
Suboptimus Prime,

@SuboptimusPrime Abbastanza bene, non so come ho sbagliato. Il valore che ho pubblicato dovrebbe essere ancora valido, dato che stavo solo saltando alcuni possibili valori.
FryAmTheEggman,

7

Python 2, 828 cifre 5048 cifre

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999799999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
155*'9'+'7'+4892*'9'

Come ha sottolineato @Jakube, il primo primo che ho presentato non era in realtà fragile a causa di un bug nel mio codice. La correzione del bug è stata semplice ma ha anche rallentato notevolmente l'algoritmo.

Mi sono limitato a un sottoinsieme facilmente ricercabile dei numeri primi fragili, vale a dire quelli che consistono solo nella cifra 9 e esattamente in una cifra 7.

def fragile_prime_generator(x, b_max):
  bs, cs = set(), set()
  prime = dict()

  def test_prime(b,c):
    if (b,c) not in prime:
      prime[(b,c)] = is_prime(int('9'*b+`x`+'9'*c))
    return prime[(b,c)]

  def test_frag(b,c):
    for b2 in xrange(b):
      if test_prime(b2,c):
        bs.add(b2)
        return False
    for c2 in xrange(c):
      if test_prime(b,c2):
        cs.add(c2)
        return False
    return True

  a = 1
  while len(bs)<b_max:
    for b in xrange(min(a, b_max)):
      c = a-b
      if b not in bs and c not in cs and test_prime(b,c):
        bs.add(b)
        cs.add(c)
        if test_frag(b,c): yield b,c
    a += 1
  print "no more fragile primes of this form"

for b,c in fragile_prime_generator(7, 222):
  print ("%d digit fragile prime found: %d*'9'+'%d'+%d*'9'"
          % (b+c+1, b, x, c))

Ho usato la stessa is_primefunzione (da qui ) di @FryAmTheEggman.

Modificare:

Ho apportato due modifiche per rendere l'algoritmo più veloce:

  • Provo a saltare quanti più controlli di primalità possibile e torno indietro solo quando viene trovato un potenziale primo fragile per assicurarsi che sia davvero fragile. C'è un piccolo numero di controlli duplicati, quindi ho grossolanamente memorizzato la funzione di controllo principale.

  • Per i numeri del modulo b*'9' + '7' + c*'9'ho limitato la dimensione di b. Più basso è il limite, meno numeri devono essere controllati, ma aumentano le probabilità di non trovare alcun primo grande fragile. Ho scelto arbitrariamente 222 come limite.

A poche migliaia di cifre, un solo controllo primo può già richiedere alcuni secondi al mio programma. Quindi, probabilmente non posso fare molto meglio con questo approccio.

Non esitate a verificare la correttezza della mia richiesta. A causa del controllo probabilistico della primalità il mio numero potrebbe teoricamente non essere primo, ma se lo è, dovrebbe essere fragile. O ho fatto qualcosa di sbagliato. :-)


2
Il tuo primo trovato non è fragile. Se chiami Rimuovi (n, 83.838) [Rimuovi tutto tranne le prime 82 cifre], finirai con un numero primo.
Jakube,

1
Ah, grazie @Jakube. Stavo cercando di essere troppo intelligente. Ho scoperto che stavo saltando più controlli di primalità di quanto avrei dovuto. Sto per risolverlo.
Emil,

1
Controllato di nuovo, ora i risultati sono corretti.
Jakube,

Il tuo numero di 5048 cifre è, in effetti, un fragile primo secondo il mio programma.
Suboptimus Prime,

@SuboptimusPrime: Fantastico, grazie per il controllo!
Emil,

4

C #, 10039 28164 cifre

6 0{28157} 169669

Modifica: ho realizzato un altro programma basato sull'algoritmo di Qualtagh con alcune piccole modifiche:

  • Sto cercando i numeri del modulo L000 ... 000R, in cui L è composto a destra, R è composto a destra. Ho permesso al numero composito sinistro L di avere più cifre, sebbene si tratti principalmente di un cambiamento stilistico e probabilmente non influisce sull'efficienza dell'algoritmo.
  • Ho aggiunto il multithreading per velocizzare la ricerca.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const int PrimeNotFound = int.MaxValue;

    private static BitArray _primeSieve;
    private static HashSet<Tuple<int, int>> _templatesToSkip = new HashSet<Tuple<int, int>>();

    static void Main(string[] args)
    {
        int bestDigitCount = 0;
        foreach (Tuple<int, int> template in GetTemplates())
        {
            int left = template.Item1;
            int right = template.Item2;
            if (SkipTemplate(left, right))
                continue;

            int zeroCount = GetZeroCountOfPrime(left, right);
            if (zeroCount != PrimeNotFound)
            {
                int digitCount = left.ToString().Length + right.ToString().Length + zeroCount;
                if (digitCount >= bestDigitCount)
                {
                    string primeStr = left + " 0{" + zeroCount + "} " + right;
                    Console.WriteLine("testing " + primeStr);
                    bool isFragile = IsFragile(left, right, zeroCount);
                    Console.WriteLine(primeStr + " is fragile: " + isFragile);

                    if (isFragile)
                        bestDigitCount = digitCount;
                }

                _templatesToSkip.Add(template);
            }
        }
    }

    private static int GetZeroCountOfPrime(int left, int right)
    {
        _zeroCount = 0;

        int threadCount = Environment.ProcessorCount;
        Task<int>[] tasks = new Task<int>[threadCount];
        for (int i = 0; i < threadCount; i++)
            tasks[i] = Task.Run(() => InternalGetZeroCountOfPrime(left, right));
        Task.WaitAll(tasks);

        return tasks.Min(task => task.Result);
    }

    private static int _zeroCount;

    private static int InternalGetZeroCountOfPrime(int left, int right)
    {
        const int maxZeroCount = 40000;
        int zeroCount = Interlocked.Increment(ref _zeroCount);
        while (zeroCount <= maxZeroCount)
        {
            if (zeroCount % 1000 == 0)
                Console.WriteLine("testing " + left + " 0{" + zeroCount + "} " + right);

            if (IsPrime(left, right, zeroCount))
            {
                Interlocked.Add(ref _zeroCount, maxZeroCount);
                return zeroCount;
            }
            else
                zeroCount = Interlocked.Increment(ref _zeroCount);
        }

        return PrimeNotFound;
    }

    private static bool SkipTemplate(int left, int right)
    {
        for (int leftDiv = 1; leftDiv <= left; leftDiv *= 10)
            for (int rightDiv = 1; rightDiv <= right; rightDiv *= 10)
                if (_templatesToSkip.Contains(Tuple.Create(left / leftDiv, right % (rightDiv * 10))))
                    return true;

        return false;
    }

    private static bool IsPrime(int left, int right, int zeroCount)
    {
        return IsPrime(left.ToString() + new string('0', zeroCount) + right.ToString());
    }

    private static bool IsPrime(string left, string right, int zeroCount)
    {
        return IsPrime(left + new string('0', zeroCount) + right);
    }

    private static bool IsPrime(string s)
    {
        using (mpz_t n = new mpz_t(s))
        {
            return n.IsProbablyPrimeRabinMiller(20);
        }
    }

    private static bool IsFragile(int left, int right, int zeroCount)
    {
        string leftStr = left.ToString();
        string rightStr = right.ToString();

        for (int startIndex = 0; startIndex < leftStr.Length - 1; startIndex++)
            for (int count = 1; count < leftStr.Length - startIndex; count++)
                if (IsPrime(leftStr.Remove(startIndex, count), rightStr, zeroCount))
                    return false;

        for (int startIndex = 1; startIndex < rightStr.Length; startIndex++)
            for (int count = 1; count <= rightStr.Length - startIndex; count++)
                if (IsPrime(leftStr, rightStr.Remove(startIndex, count), zeroCount))
                    return false;

        return true;
    }

    private static IEnumerable<Tuple<int, int>> GetTemplates()
    {
        const int maxDigitCount = 8;
        PreparePrimeSieve((int)BigInteger.Pow(10, maxDigitCount));
        for (int digitCount = 2; digitCount <= maxDigitCount; digitCount++)
        {
            for (int leftCount = 1; leftCount < digitCount; leftCount++)
            {
                int rightCount = digitCount - leftCount;
                int maxLeft = (int)BigInteger.Pow(10, leftCount);
                int maxRight = (int)BigInteger.Pow(10, rightCount);

                for (int left = maxLeft / 10; left < maxLeft; left++)
                    for (int right = maxRight / 10; right < maxRight; right++)
                        if (IsValidTemplate(left, right, leftCount, rightCount))
                            yield return Tuple.Create(left, right);
            }

        }
    }

    private static void PreparePrimeSieve(int limit)
    {
        _primeSieve = new BitArray(limit + 1, true);
        _primeSieve[0] = false;
        _primeSieve[1] = false;

        for (int i = 2; i * i <= limit; i++)
            if (_primeSieve[i])
                for (int j = i * i; j <= limit; j += i)
                    _primeSieve[j] = false;
    }

    private static bool IsValidTemplate(int left, int right, int leftCount, int rightCount)
    {
        int rightDigit = right % 10;
        if ((rightDigit != 1) && (rightDigit != 9))
            return false;

        if (left % 10 == 0)
            return false;

        if ((left + right) % 3 == 0)
            return false;

        if (!Coprime(left, right))
            return false;

        int leftDiv = 1;
        for (int i = 0; i <= leftCount; i++)
        {
            int rightDiv = 1;
            for (int j = 0; j <= rightCount; j++)
            {
                int combination = left / leftDiv * rightDiv + right % rightDiv;
                if (_primeSieve[combination])
                    return false;

                rightDiv *= 10;
            }

            leftDiv *= 10;
        }

        return true;
    }

    private static bool Coprime(int a, int b)
    {
        while (b != 0)
        {
            int t = b;
            b = a % b;
            a = t;
        }
        return a == 1;
    }
}

Vecchia risposta:

8 0{5436} 4 0{4600} 1

Ci sono alcuni modelli notevoli per i numeri primi fragili:

600..00X00..009
900..00X00..009
800..00X00..001
999..99X99..999

dove X può essere 1, 2, 4, 5, 7 o 8.

Per tali numeri dobbiamo solo considerare (lunghezza - 1) possibili Removeoperazioni. Le altre Removeoperazioni producono duplicati o ovviamente numeri composti. Ho provato a cercare tutti questi numeri con un massimo di 800 cifre e ho notato che 4 modelli escono più frequentemente rispetto agli altri: 8007001, 8004001, 9997999 e 6004009. Dato che Emil e Jakube stanno usando il modello 999X999, ho deciso di utilizzare 8004001 solo per aggiungere un po 'di varietà.

Ho aggiunto le seguenti ottimizzazioni all'algoritmo:

  • Comincio a cercare numeri con 7000 cifre e poi incremento la lunghezza di 1500 ogni volta che viene trovato un numero primo fragile. Se non esiste un numero primo fragile con una determinata lunghezza, lo incremento di 1. 7000 e 1500 sono solo numeri arbitrari che sembravano appropriati.
  • Sto usando il multithreading per cercare numeri con lunghezza diversa allo stesso tempo.
  • Il risultato di ciascun controllo primario viene archiviato in una tabella hash per impedire controlli duplicati.
  • Sto usando l'implementazione di Miller-Rabin da Mpir.NET , che è molto veloce (MPIR è un fork di GMP).
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Mpir.NET;

class Program
{
    const string _template = "8041";

    private static ConcurrentDictionary<Tuple<int, int>, byte> _compositeNumbers = new ConcurrentDictionary<Tuple<int, int>, byte>();
    private static ConcurrentDictionary<int, int> _leftPrimes = new ConcurrentDictionary<int, int>();
    private static ConcurrentDictionary<int, int> _rightPrimes = new ConcurrentDictionary<int, int>();

    static void Main(string[] args)
    {
        int threadCount = Environment.ProcessorCount;
        Task[] tasks = new Task[threadCount];
        for (int i = 0; i < threadCount; i++)
        {
            int index = i;
            tasks[index] = Task.Run(() => SearchFragilePrimes());
        }
        Task.WaitAll(tasks);
    }

    private const int _lengthIncrement = 1500;
    private static int _length = 7000;
    private static object _lengthLock = new object();
    private static object _consoleLock = new object();

    private static void SearchFragilePrimes()
    {
        int length;
        lock (_lengthLock)
        {
            _length++;
            length = _length;
        }

        while (true)
        {
            lock (_consoleLock)
            {
                Console.WriteLine("{0:T}: length = {1}", DateTime.Now, length);
            }

            bool found = false;
            for (int rightCount = 1; rightCount <= length - 2; rightCount++)
            {
                int leftCount = length - rightCount - 1;
                if (IsFragilePrime(leftCount, rightCount))
                {
                    lock (_consoleLock)
                    {
                        Console.WriteLine("{0:T}: {1} {2}{{{3}}} {4} {2}{{{5}}} {6}",
                            DateTime.Now, _template[0], _template[1], leftCount - 1,
                            _template[2], rightCount - 1, _template[3]);
                    }
                    found = true;
                    break;
                }
            }

            lock (_lengthLock)
            {
                if (found && (_length < length + _lengthIncrement / 2))
                    _length += _lengthIncrement;
                else
                    _length++;
                length = _length;
            }
        }
    }

    private static bool IsFragilePrime(int leftCount, int rightCount)
    {
        int count;
        if (_leftPrimes.TryGetValue(leftCount, out count))
            if (count < rightCount)
                return false;

        if (_rightPrimes.TryGetValue(rightCount, out count))
            if (count < leftCount)
                return false;

        if (!IsPrime(leftCount, rightCount))
            return false;

        for (int i = 0; i < leftCount; i++)
            if (IsPrime(i, rightCount))
                return false;

        for (int i = 0; i < rightCount; i++)
            if (IsPrime(leftCount, i))
                return false;

        return true;
    }

    private static bool IsPrime(int leftCount, int rightCount)
    {
        Tuple<int, int> tuple = Tuple.Create(leftCount, rightCount);
        if (_compositeNumbers.ContainsKey(tuple))
            return false;

        using (mpz_t n = new mpz_t(BuildStr(leftCount, rightCount)))
        {
            bool result = n.IsProbablyPrimeRabinMiller(20);

            if (result)
            {
                _leftPrimes.TryAdd(leftCount, rightCount);
                _rightPrimes.TryAdd(rightCount, leftCount);
            }
            else
                _compositeNumbers.TryAdd(tuple, 0);

            return result;
        }
    }

    private static string BuildStr(int leftCount, int rightCount)
    {
        char[] chars = new char[leftCount + rightCount + 1];
        for (int i = 0; i < chars.Length; i++)
            chars[i] = _template[1];
        chars[0] = _template[0];
        chars[leftCount + rightCount] = _template[3];
        chars[leftCount] = _template[2];
        return new string(chars);
    }
}

Mentre stavo cercando di verificare la tua prima risposta, ne hai già postata una nuova)). Il controllo ha già richiesto 24 ore. La risposta sembra corretta. Non riesco a credere che BigInteger di Java sia molto più lento delle implementazioni native. Ho pensato a 2, 3 o anche 10 volte più lentamente. Ma 24 ore contro diversi minuti sono troppe.
Qualtagh,

@Qualtagh Per essere onesti, il numero di cifre 10039 ha impiegato 35 ore per trovare a causa dell'algoritmo inferiore :) Il mio programma attuale impiega circa 3 minuti per trovare il numero di 6629 cifre e 6 ore per trovare quello di 28164 cifre.
Suboptimus Prime,

La tua prima risposta è corretta. Verificato! La verifica ha richiesto 48 ore. E non proverò nemmeno a verificare la seconda risposta)). Mi chiedo perché BigInteger sia così lento rispetto a MPIR. È solo JVM / differenza nativa? Ho impostato un flag "-server", quindi mi aspetto che il codice sia compilato JIT. Gli algoritmi di esponenziazione modulare differiscono: sia Java che MPIR usano 2 <sup> k </sup> -ary finestra scorrevole, ma k = 3 è fisso in Java e MPIR sceglie k in base alla dimensione dell'esponente. MPIR utilizza calcoli paralleli su più core o probabilmente funzionalità GPU? Java BigInteger no.
Qualtagh,

1
@Qualtagh Sono abbastanza sicuro che MPIR stia utilizzando un solo core della CPU. Ho aggiunto io stesso il multithreading che ha portato a una ricerca quasi 4 volte più veloce su una CPU quad-core. Non ho confrontato l'implementazione interna di MPIR e Java BigInteger, ma suppongo che MPIR stia utilizzando algoritmi migliori per la moltiplicazione e la divisione modulare. Inoltre, è probabilmente ottimizzato meglio per le CPU a 64 bit (vedere il benchmark in questo post del blog ).
Suboptimus Prime,

2
MPIR è davvero single core e non utilizza GPU. È un mix altamente ottimizzato e ottimizzato di codice C e assemblatore. Esiste una versione MPIR che utilizza solo C (per motivi di portabilità), ma la versione C + ASM è notevolmente più veloce. La versione MPIR che sto usando per MPIR.Net è C + ASM usando il set di istruzioni K8 (1st gen x64), perché volevo che MPIR.Net funzionasse su tutti i PC x64. Le versioni per i successivi set di istruzioni non erano notevolmente più veloci nel mio benchmark crittografico, ma ciò potrebbe ovviamente differire per altre operazioni.
John Reynolds,

2

Haskell - 1220 1277 cifre fissate per reali

99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997999999999999999999999999999999999999999999999999999999999999999999999

9{1150} 7 9{69}

Meglio uno - 1277 cifre

9{871} 8 9{405}

Codice Haskell

downADigit :: Integer -> [Integer]
downADigit n = f [] 1 where
     f xs a | nma /= n = f (((n `div` a10)*a + nma):xs) a10
            | otherwise = xs where
        a10 = a * 10
        nma = n `mod` a

isFragile = all (not . isPrime') . downADigit
findNextPrime :: Integer -> Integer
findNextPrime n | even n = f (n + 1)
                | otherwise = f n where
    f n | isPrime' n  = n
        | otherwise = f (n + 2)

primesFrom n = f (findNextPrime n) where
    f n = n:f (findNextPrime $ n + 1)

primeLimit = 10000

isPrime' n | n < primeLimit = isPrime n
isPrime' n = all (millerRabinPrimality n) [2,3,5,7,11,13,17,19,984,7283,6628,8398,2983,9849,2739]

-- (eq. to) find2km (2^k * n) = (k,n)
find2km :: Integer -> (Integer,Integer)
find2km n = f 0 n
    where 
        f k m
            | r == 1 = (k,m)
            | otherwise = f (k+1) q
            where (q,r) = quotRem m 2        

-- n is the number to test; a is the (presumably randomly chosen) witness
millerRabinPrimality :: Integer -> Integer -> Bool
millerRabinPrimality n a
    | a <= 1 || a >= n-1 = 
        error $ "millerRabinPrimality: a out of range (" 
              ++ show a ++ " for "++ show n ++ ")" 
    | n < 2 = False
    | even n = False
    | b0 == 1 || b0 == n' = True
    | otherwise = iter (tail b)
    where
        n' = n-1
        (k,m) = find2km n'
        b0 = powMod n a m
        b = take (fromIntegral k) $ iterate (squareMod n) b0
        iter [] = False
        iter (x:xs)
            | x == 1 = False
            | x == n' = True
            | otherwise = iter xs

-- (eq. to) pow' (*) (^2) n k = n^k
pow' :: (Num a, Integral b) => (a->a->a) -> (a->a) -> a -> b -> a
pow' _ _ _ 0 = 1
pow' mul sq x' n' = f x' n' 1
    where 
        f x n y
            | n == 1 = x `mul` y
            | r == 0 = f x2 q y
            | otherwise = f x2 q (x `mul` y)
            where
                (q,r) = quotRem n 2
                x2 = sq x

mulMod :: Integral a => a -> a -> a -> a
mulMod a b c = (b * c) `mod` a
squareMod :: Integral a => a -> a -> a
squareMod a b = (b * b) `rem` a

-- (eq. to) powMod m n k = n^k `mod` m
powMod :: Integral a => a -> a -> a -> a
powMod m = pow' (mulMod m) (squareMod m)

-- simple for small primes
primes :: [Integer]
primes = 2:3:primes' where
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n)
                                   $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0
isPrime :: Integer -> Bool
isPrime n | n < 2 = False
          | otherwise = f primes where
            f (p:ps) | p*p <= n = if n `rem` p == 0 then False else f ps
                     | otherwise = True

main = do
    print . head $ filter isFragile (primesFrom $ 10^1000)

Penso che puoi rimuovere tutto tranne gli ultimi 3 ...
Sp3000,

finisce in 5 se rimuovo gli ultimi 3, quindi è divisibile per 5
John Meacham,

2
No, intendo rimuovere tutto finché non ti restano solo gli ultimi 3, il che è primo.
Sp3000,

1
@JohnMeacham Il mio programma suggerisce che questo numero diventa primo se rimuovi 386 cifre da sinistra.
Suboptimus Prime,

1
Per favore, verifica i tuoi numeri prima di pubblicare. Se rimuovi le 1256 cifre a sinistra dal tuo numero di 1276 cifre, otterrai 99999994999999999999, che è il numero primo.
Suboptimus Prime,
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.