Algoritmo di Zakiya

Stato
Discussione chiusa ad ulteriori risposte.

Pythoner

Utente Silver
25 Dicembre 2009
0
0
0
52
Avrei una richiesta da fare.
Si tratta di scrivere l'algoritmo di Zakiya nel vostro linguaggio, Attualmente io e Zakiya abbiamo già le versioni Python e Ruby, ci servono algoritmi in altri linguaggi per fare grafici e comparazioni.
Il problema è che io so solo il Python e Zakiya solo il Python e il Ruby.
Vi chiederei di scrivere l'implementazione, perché poi saranno utilizzate tutte per fare grafici, e saranno messe su un blog o in un libro.
Possibilmente se potete ottimizzate il più possibile, e poi oltre che al codice fornite dettagli sul sistema e sui tempi.

Come implementare l'algoritmo:
Codice:
def SoZP7a(val):
    # all prime candidates > 7 are of form 210*k+(1,11,13,17,19,23,29,31,37
    # 41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121,127,131
    # 137,139,143,149,151,157,163,167,169,173,179,181,187,191,193,197,199,209)
    num = val-((val&1)^1)        # se è pari sottrae 1
    mod=210; rescnt=48           # valore del modulo; numero dei residui
    maxprms = (rescnt*num)//mod  # numero massimo di candidati per la primalità
    prms = [True]*maxprms        # crea una lista di valori True lunga maxprms
    primes = [2,3,5,7]   # Inizializza la lista di numeri primi

    # array di residui per trovare i candidati
    residues = [1,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,
         89,97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,163,
         167,169,173,179,181,187,191,193,197,199,209,211]
    # hash di valori per trovare la posizione dei nonprimi nell'array prms
    pos = {}
    for i in xrange(rescnt): pos[residues[i]] = i-1 # riempie l'hash

    # sieve to eliminate nonprimes from prms
    limit = int(ceil(sqrt(val))) # calcola il limite: radice quadrata di val
    x=r=0  # inizializza x e r a 0
    for prm in prms:   # cicla su ogni valore di prms
        r += 1   # r = r + 1
        if r > 48: r = 1; x += 210
        if not prm: continue  # se prm è falso passa al prossimo valore
        prime = x + residues[r]  # prime è uguale a x più l'elemento r di residues
        if limit < prime: break  # se limit è minore di prime blocca il ciclo
        m = prime*48
        for ri in residues[1:]:  # cicla su ogni numero in residues tranne il primo
           product = prime*(x+ri)
           if product > val: break
           # compute product position index in prms
           qq,rr  = divmod(product,210) # qq = product/210 rr = product % 210
           nonprmpos = qq*48 + pos[rr]
           for nprm in xrange(nonprmpos,maxprms,m): prms[nprm] = False
    # the prms array now has all the positions for primes 11..N
    if num < 9:  return primes[:1+num//2]
    if num < 11: return primes
    n = (48*(x//210)) + r - 1
    while not prms[n]:  # cicla finché l'elemento di prms alla posizione n è Falso
       n += 1; r += 1
       if r > 48: r = 1; x += 210
    for i in xrange(n,maxprms,1):
       if prms[i]: primes.append(x+residues[r]) # aggiunge all'arrray primes il valore x + l'elemento di residues in posizione r
       r += 1
       if r > 48: r = 1; x += 210
    if primes[-1] > num: primes.pop() # se l'ultimo elemento di primes è maggiore di num, elimina l'ultimo elemento di primes
    return primes

Se non avete capito qualcosa dal codice non esitate a chiedere.
Grazie per l'attenzione.

P.S. La pagina di Wikipedia è in costruzione, se volete potete leggere l'articolo di Zakiya qui.

EDIT: Mi sono dimenticato di dire che il codice del pdf non è aggiornato, quindi non usatelo. Per scrivere l'algoritmo usate quello che vi ho detto. Il pdf comunque è molto utile lo stesso.
 
sarebbe interessante sapere le distinzioni dell'uso delle variabili. Per la precisione, quali sono fisse (sarebbero costanti), quali sono fisse durante l'esecuzione a meno di una inizializzione (write once-read many), quali sono invece variabili che variano.

ad esempio residues: si ferma sempre a 211 (credo di no^^)? come varia la lunghezza? devo calcolarli i valori? me la da un'altra funzione? ecc.

:EDIT:
poi ci sarebbero varie implementazioni per le liste tipo primes, ad esempio array fisso (più dei candidati non possono essere), oppure lista bidirezionale (occupa per ogni elemento 2 byte in più ma l'accesso è o(1) alla fine e all'inizio, compreso estrazione ed inserimento - a prima vista) oppure lista semplice (o(n) per la ricerca o l'accesso all'ultimo elemento, senza particolari accorigimenti).
quindi sarebbe da spiegare su cosa bisogna far puntare l'ottimizzazione, come vengono usate le liste, il loro scopo, ecc.
 
Intanto grazie per la risposta!
Allora:
Le costanti sono: num, mod, rescnt, maxprms;
Per gli array: prms rimane sempre di lunghezza maxprms, primes invece all'inizio contiene solo quei 4 numeri ma poi aumenta di dimesione, sempre però senza superare maxprms. residues è sempre costante, non aumenta nè diminuisce di dimensioni e i valori sono quelli basta che copi. pos è più complicato: mi sembra non si possa fare in C però tanto l'i-esimo elemento di pos ha valore i-1 (p.e. il quinto vale 4), quindi basta sostituire pos con i-1.

Per le liste: a primes bisogna solo aggiungere elementi, quindi scegli pure tu come fare. Per prms invece bisogna solo leggerli, credo che leggendoli con una lista bidirezionale si risparimi del tempo.

Ciao e grazie
 
vorrei precisare, solo per conoscenza, che num e maxprms non sono costanti per molte persone (sono variabili che una volta scritte non vengono modificate, ma il valore varia da istanza a istanza della funzione).

:EDIT:
un altro chiarimento, se possibile: vorrei sapere quali variabili possono assumere valori non interi (razionali). Un esempio dell'utilità di questa informazione è la differenza tra l'operatore // e /
 
Ah giusto! Io intendevo che non variano durante l'esecuzione della funzione!
Grazie per la precisazione. In che linguaggio vorresti farlo?
 
ho modificato il messaggio precedente (non mi ero accorto della risposta).
Sto provando a realizzarlo in C++ (ma al momento sto usando costrutti che se non sbaglio sono compatibili con il C)
 
Whivel ha detto:
:EDIT:
un altro chiarimento, se possibile: vorrei sapere quali variabili possono assumere valori non interi (razionali). Un esempio dell'utilità di questa informazione è la differenza tra l'operatore // e /

No no solo interi, io ho usato / perché in Python 2.6 / è uguale a //
 
io leggo //mod. comunque avevo immaginato giusto.
Ho trovato discordanza tra le informazioni date ed il codice.
Per prms invece bisogna solo leggerli
Codice:
prms[nprm] = False

se non sbaglio però la lunghezza rimane fissa e sono sempre valori booleani.

So che le richieste stanno diventando tante e forse pesanti, però vorrei sapere se possibile i range di valori che possono assumere le variabili (ovviamente non esatti, mi basta una stima) oppure il tipo se possibile (booleani, interi fino a 1000000, con virgola, ecc)
 
Whivel ha detto:
Ho trovato discordanza tra le informazioni date ed il codice.
Per prms invece bisogna solo leggerli
Codice:
prms[nprm] = False

se non sbaglio però la lunghezza rimane fissa e sono sempre valori booleani.

Hai di nuovo ragione, non ricordavo bene. Comunque sì la lunghezza è fissa e i valori sono sempre booleani.

So che le richieste stanno diventando tante e forse pesanti, però vorrei sapere se possibile i range di valori che possono assumere le variabili (ovviamente non esatti, mi basta una stima) oppure il tipo se possibile (booleani, interi fino a 1000000, con virgola, ecc)

Meglio! Così viene fuori un buon lavoro!
num dipende da val e quindi è meglio mettere un unsigned long
mod e rescnt vanno bene anche int o short int
maxprms unsigned long
primes unsigned long perché i valori arrivano fino a num
residues int
x, r unsigned int
qq e rr unsigned int o int

Per quanto riguarda l'implementazione mi ricordo (quando avevo provato a farlo in C ma era saltata fuori una schifezza) che era un casino con primes, perché non si sa la lunghezza precisa, ma solo che è minore di maxprms e quindi bisognava riallocare... qualcosa di simile.
 
Pythoner ha detto:
pos è più complicato: mi sembra non si possa fare in C però tanto l'i-esimo elemento di pos ha valore i-1 (p.e. il quinto vale 4), quindi basta sostituire pos con i-1.

Analizzando il sorgente (chiedo scusa per il tempo impiegato) mi sembra che non è vero che pos = i-1, ma che pos[residui]=i-1.
quindi ad esempio con i=47 pos[[residues[47]]=47-1 => pos[209]=46.

un'altra domanda: il ciclo
for i in xrange(rescnt): pos[residues] = i-1 # riempie l'hash
va da 0 a 47, ma residues sono 49 elementi. l'elemento di residues che vale 211 non viene considerato: è giusto?
 
Whivel ha detto:
Analizzando il sorgente (chiedo scusa per il tempo impiegato) mi sembra che non è vero che pos = i-1, ma che pos[residui]=i-1.
quindi ad esempio con i=47 pos[[residues[47]]=47-1 => pos[209]=46.


Giusto! Non devi chiedere scusa tu ma io che sono stato impreciso.

un'altra domanda: il ciclo
for i in xrange(rescnt): pos[residues] = i-1 # riempie l'hash
va da 0 a 47, ma residues sono 49 elementi. l'elemento di residues che vale 211 non viene considerato: è giusto?


Sì hai inuito bene: 211 non viene considerato, serve dopo ma non è un vero e proprio residuo, perché i residui sono minori di 2*3*5*7 = 210 e 211 è necessario al funzionamento dell'algoritmo.
 
mi sono dimenticato di chiedere una cosa riguardo a pos.
abbiamo detto che alla fine pos[residues]=i-1, ma volevo sapere invece gli altri valori non inizializzati (sono inizializzati 48 su 210/211) che valore hanno (se ci si accede)? può capitare di accederci?

:EDIT:
una domanda stupida: residues sarà sempre un array ordinato? (mi hai detto che non cambia mai, ma lo chiedo lo stesso ^^)
 
Whivel ha detto:
mi sono dimenticato di chiedere una cosa riguardo a pos.
abbiamo detto che alla fine pos[residues]=i-1, ma volevo sapere invece gli altri valori non inizializzati (sono inizializzati 48 su 210/211) che valore hanno (se ci si accede)? può capitare di accederci?


No, non può capitare, perché tutti i candidati divisi per mod, danno un resto che è uno dei residui, e siccome 211 non è un residuo ma è solo di appoggio non devi metterlo in pos.

:EDIT:
una domanda stupida: residues sarà sempre un array ordinato? (mi hai detto che non cambia mai, ma lo chiedo lo stesso ^^)

Sì sempre ordinato. E si legge solo, quindi penso che vada bene una lista bidirezionale.
 
Pythoner ha detto:
Whivel ha detto:
mi sono dimenticato di chiedere una cosa riguardo a pos.
abbiamo detto che alla fine pos[residues]=i-1, ma volevo sapere invece gli altri valori non inizializzati (sono inizializzati 48 su 210/211) che valore hanno (se ci si accede)? può capitare di accederci?


No, non può capitare, perché tutti i candidati divisi per mod, danno un resto che è uno dei residui, e siccome 211 non è un residuo ma è solo di appoggio non devi metterlo in pos.


non mi quadra, perchè nel codice c'è
Codice:
 qq,rr  = divmod(product,210) # qq = product/210 rr = product % 210
           nonprmpos = qq*48 + pos[rr]
rr può avere un valore qualsiasi tra 0 e 209, quindi posso accedere a pos in una qualsiasi posizione
 
No perché non testa tutti i numeri, ma solo i candidati, e i candidati sono in questa forma:
Codice:
210*k+(1,11,13,17,19,23,29,31,37, 41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121,127,131, 137,139,143,149,151,157,163,167,169,173,179,181,187,191,193,197,199,209)

Quindi 211 non capiterà mai.
 
Pythoner ha detto:
No perché non testa tutti i numeri, ma solo i candidati, e i candidati sono in questa forma:
Codice:
210*k+(1,11,13,17,19,23,29,31,37, 41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121,127,131, 137,139,143,149,151,157,163,167,169,173,179,181,187,191,193,197,199,209)

Quindi 211 non capiterà mai.

ok ho capito (comunque 211 non l'ho mai nominato^^ io parlavo di pos[5], pos[42], ecc.)

mi sono rifatto un po di conti:
rr = product % 210
product = prime*(x+ri)

dove ri è un elemento qualsiasi dei residues (tranne il primo)

prime = x + residues[r] ==> prima = x+rj

dove r è un numero compreso tra 0 e 48 (ho rinominato residues[r] con rj)

x è sicuramente della forma 210*k con k € N.

a questo punto rr = ((210*k + rj)*(210*k+ri))%210 =
= ((210*k)^2 + 210*k*(ri+rj) + ri*rj)%210 =
= ((((210*k)*(210*k))%210) + ((210*k*(ri+rj))%210) + ((ri*rj)%210))%210 =
= (0 + 0 + ((ri*rj)%210)%210 =
= (ri*rj)%210

che può fare qualsiasi cosa (tra 0 e 209)

:EDIT:
con passaggi più semplici:
rr = ((210*k + rj)*(210*k+ri))%210 =
= (((210*k +rj)%210)*((210*k+ri)%210))%210 =
= ((rj%210) + (ri%210))%210 = SICCOME ri,rj<210
= (ri*rj)%210
 
Non proprio. Non può fare qualsiasi cosa tra 0 e 209, ma un residuo tra 0 e 209 (occorre ricordare che solo i candidati vengono divisi, non un numero qualsiasi e i candidati sono in una forma particolare - 210k + € residues), e siccome in pos i residui ci sono tutti, non darà mai errore. Fidati. Ho provato questo codice migliaia di volte.
Ho trovato molto utile il suo 2° pdf, che però è solo una bozza, ma fa un esempio e spiega in dettaglio linea per linea in modo da rendere il processo di sieving veramente comprensibile. E' in inglese.
 
ho fatto un controllo ed effettivamente quel codice restituisce comunque un valore contenuto in residues...
chiedo scusa per l'insistenza ma era importante per la scelta della struttra da usare

:EDIT:
ma a questo punto la pos la potrei costruire come un array costante, tanto è in sola lettura, dipende solo da residues che non varia mai. giusto?
 
ho finito la traduzione e sembra funzionare (dovrebbe):
1) Il programma può essere compilato sia in C che in C++ senza modifiche (ci sono egli #ifdef nel codice)
1b) Il codice quindi può essere difficilmente leggibile. Se vuoi posso riscirvere i 2 codici separatamente per i 2 linguaggi

2)Ho creato alcuni tipi intXX (u per unsigned e XX il numero di bit) per permettere la compilazioni su varie piattaforme senza creare problemi sulla dimensione dei tipi (spiegato nel codice, ma non sono certo della sua universalità)

3) per permettere di avere numeri primi >65535 ho usato interi a 32bit per num e val, ma nel calcolo di maxprms possono verificarsi overflow (non ci sono controlli al momento)
3b)non sono usati i dati long long e quindi non sono stati usati i numeri a 64bit perchè nelle architetture a 32bit solo il tipo long long è ha 64bit
 
Stato
Discussione chiusa ad ulteriori risposte.