n° 186
Luglio/Agosto 2013
Giugno 19, 2013, 04:05:33 am *
Benvenuto! Accedi o registrati.
Hai dimenticato l'e-mail di attivazione?

Accesso con nome utente, password e durata della sessione
Notizia:
 
   Indice   Linux Windows Techassistance Gameassistance videogame hardware Aiuto Ricerca Agenda Downloads Accedi Registrati  

* Messaggi recenti
Messaggi recenti
Pagine: [1]   Vai giù
  Stampa  
Autore Discussione: Algoritmo Numeri Primi  (Letto 2306 volte)
0 utenti e 1 Utente non registrato stanno visualizzando questa discussione.
MarcoMacUser
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 23


Mostra profilo E-mail
« inserita:: Dicembre 27, 2011, 01:58:13 pm »

Ciao a tutti. Sono di nuovo alle prese con i numeri primi.
Ho creato un programma in C che, tramite il crivello di Eratostene trova il numero di numeri primi tra 3 e un numero.
Il sostanza, il programma immagazzina in un vettore tutti i numeri dispari tra 3 e N, poi trovo tutti i numeri primi compresi tra 3 e la radice quadrata di N e li metto in una lista.
A questo punto cancello dal vettore tutti i numeri multipli dei primi nella lista e i numeri rimanenti sono i primi.
A questo punto, però, mi si pone un problema: La consegna iniziale dell'esercizio, era di trovare il numero di numeri primi tra 3 e un numero N con N tra le 14 e le 19 cifre quindi un long int.
Se non sbaglio a fare i conti:
Mettendo come N il numero 12345678912345 (14 cifre), e immagazzinando nel vettore solo i dispari, dovrei fare:

(12345678912345 / 2) = 6,17283946 × 10^12
cosi trovo il numero dei dispari, e poi:
(6,17283946 × 10^12) * 8 = 4.93827156 × 10^13
Infatti ogni long int è 8 byte.
Se non sbaglio a contare gli "zeri" dovrebbe essere un numero vicino a 50'000'000'000'000 Byte --> 50 TeraByte.
 Ghigno Qualcuno ha 50 Tera Byte di RAM da prestarmi?  Ghigno

Il mio pensiero, a l'inizio, fu quello di dividere il vettore in blocchi da poche decine di migliaia di numeri e poi, per compensare un po sotto l'aspetto velocità, avrei messo dei thread che cancellavano i numeri (il thread1 cancella tutti i multipli di 3 e 5, il thread2 cancella tutti i multipli di 7 e 11 e così via).
Secondo voi è la soluzione ottimale o dovrei optare per un programma più CPU Bound?
Il fatto è che mi piaceva l'idea del crivello di Eratostene per prendere un po più di famigliarità con le liste e i puntatori.

Grazie a tutti e buone feste!
Registrato
celeborn85
Global Moderator
Hero Member
*****

Karma: +57/-13
Scollegato Scollegato

Messaggi: 2127


Mostra profilo
« Risposta #1 inserita:: Dicembre 27, 2011, 02:53:23 pm »

Prima di tutto, perché usare dei long int (che non è neanche detto siano di 64 bit), quando il tipo di informazione da mantenere per ogni singolo valore è se il numero è primo o meno? Il numero stesso è già infatti determinato dalla posizione all'interno dell'array. Quando si devono mantenere così tanti valori booleani è consigliabile utilizzare un solo bit per ogni valore riducendo drasticamente i tuoi requisiti di memoria.

Inoltre, programmi che non mantengono tutti i dati che utilizzano in memoria in contemporanea sono abbastanza comuni. Quello che devi fare è utilizzare il tuo disco fisso per mantenere blocchi di valori sui quali non stai lavorando e riordinare un po' le operazioni in modo da massimizzare l'utilizzo di un singolo blocco. Se cerchi "cache oblivious sieve of eratosthenes" dovresti trovare qualcosa.
Registrato

I moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.
MarcoMacUser
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 23


Mostra profilo E-mail
« Risposta #2 inserita:: Dicembre 27, 2011, 04:24:31 pm »

Prima di tutto, perché usare dei long int (che non è neanche detto siano di 64 bit), quando il tipo di informazione da mantenere per ogni singolo valore è se il numero è primo o meno? Il numero stesso è già infatti determinato dalla posizione all'interno dell'array. Quando si devono mantenere così tanti valori booleani è consigliabile utilizzare un solo bit per ogni valore riducendo drasticamente i tuoi requisiti di memoria.

Inoltre, programmi che non mantengono tutti i dati che utilizzano in memoria in contemporanea sono abbastanza comuni. Quello che devi fare è utilizzare il tuo disco fisso per mantenere blocchi di valori sui quali non stai lavorando e riordinare un po' le operazioni in modo da massimizzare l'utilizzo di un singolo blocco. Se cerchi "cache oblivious sieve of eratosthenes" dovresti trovare qualcosa.

Ho provato questa via alternativa: Creo un vettore di N posizioni tutte inizializzate a true. Scorro l'indici del vettore da 2 a N e se l'indice non è primo metto a False la cella del vettore corrispondente. Alla fine del programma conto i TRUE rimasti e ho il numero di numeri primi. La cosa funziona alla perfezione con numeri piccoli, ma con numeri grandi (anche ad esempio 123456 che il precedente programma faceva), mi da un segmentation fault Triste Ora provo a cercare "cache oblivious sieve of eratosthenes".
Intanto se hai soluzione per il segmentation fault fammi sapere Sorriso
Grazie!!
Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +205/-15
Scollegato Scollegato

Messaggi: 2709


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #3 inserita:: Dicembre 27, 2011, 05:25:23 pm »

Devi usare un bit array, non certo una strana emulazione di array booleano che spreca almeno 8 bit per ogni singolo valore logico T/F.

In linea di principio, e tralasciando i dettagli su come l'hardware o il compilatore effettuano un eventuale packing in memoria, la massima efficienza si ottiene creando un array del tipo nativo più grande allocabile in una singola cella fisica di memoria, quindi su macchine contemporanee si parla di interi non segnati allineati a 32 o 64 bit. A questo punto, si ha una densità notevole di flag booleani, che vanno manipolati sfruttando in modo intelligente i bit meno significativi del loro indirizzo logico tramite modulo implicito (trattandosi di potenze intere del due, tutto si riduce a shift e maschere AND).

Ad esempio, se si vuole accedere - per fissare le idee - al flag in cinquantesima posizione entro un bit array di uint32, si avrà:
50d = 00110010b
La parte in rosso indica l'offset del bit all'interno del byte. La restante parte dell'indirizzo, opportunamente divisa per 32 tramite uno shift destro, rappresenta direttamente la posizione della entry nell'array.
Un'ottima implementazione di bit array, sempre valida, era contenuta negli storici snippets.
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io? Occhiolino
MarcoMacUser
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 23


Mostra profilo E-mail
« Risposta #4 inserita:: Dicembre 27, 2011, 09:04:24 pm »

Devi usare un bit array, non certo una strana emulazione di array booleano che spreca almeno 8 bit per ogni singolo valore logico T/F.

In linea di principio, e tralasciando i dettagli su come l'hardware o il compilatore effettuano un eventuale packing in memoria, la massima efficienza si ottiene creando un array del tipo nativo più grande allocabile in una singola cella fisica di memoria, quindi su macchine contemporanee si parla di interi non segnati allineati a 32 o 64 bit. A questo punto, si ha una densità notevole di flag booleani, che vanno manipolati sfruttando in modo intelligente i bit meno significativi del loro indirizzo logico tramite modulo implicito (trattandosi di potenze intere del due, tutto si riduce a shift e maschere AND).

Ad esempio, se si vuole accedere - per fissare le idee - al flag in cinquantesima posizione entro un bit array di uint32, si avrà:
50d = 00110010b
La parte in rosso indica l'offset del bit all'interno del byte. La restante parte dell'indirizzo, opportunamente divisa per 32 tramite uno shift destro, rappresenta direttamente la posizione della entry nell'array.
Un'ottima implementazione di bit array, sempre valida, era contenuta negli storici snippets.

Grazie della risposta.
Mi chiedevo se per compiti del genere (calcolo dei numeri primi tra 0 e un numero molto grande) fosse meglio creare programmi CPU bound oppure programmi che fanno largo uso delle memoria.
Ho notato che applicando il crivello di Eratostene (nel quale il processore fa ben poco ma si occupa molta RAM) per svolgere il compito da me prefissato, i tempi si riducono notevolmente se il numero è relativamente piccolo (non più di 300.000), mentre per numeri molto più grandi (oltre le 14 cifre), un programma che sfrutta molto la CPU (nel mio caso ho creato un programma che divide in blocchi la serie di numeri da analizzare e poi dei thread opportunamente sincronizzati analizzano) la fa da padrona.
Secondo te, quindi, dovrei concentrarmi, considerando che la consegna è "trovare i numeri primi da 0 a N con N sopra le 14 cifre" su una soluzione tipo il crivello di Eratostene (ottimizzando l'algoritmo come hai detto tu sopra) oppure su una soluzione che fa largo utilizzo di CPU (ottimizzare il mio algoritmo che divide in blocchi ecc..)?
Grazie ancora!
Registrato
celeborn85
Global Moderator
Hero Member
*****

Karma: +57/-13
Scollegato Scollegato

Messaggi: 2127


Mostra profilo
« Risposta #5 inserita:: Dicembre 27, 2011, 10:00:47 pm »

Potresti in qualche modo chiarire che cosa stai cercando di fare nell'algoritmo che dici sfruttare molto la CPU? Spero non stia controllando ogni possibile divisore di ogni numero.. Credo che il giusto compromesso sia quello di utilizzare la versione cache friendly del crivello di Eratostene come ti ho già consigliato. In questa versione si calcolano prima i primi inferiori alla radice del numero e si usa poi questa informazione per trovare gli altri. In particolare si dividono gli N valori in blocchi e per ogni blocco, in parallelo, si eliminano i multipli dei primi trovati nella prima fase dell'algoritmo. Per numeri molto grandi questa versione dovrebbe essere molto superiore grazie ad un migliore uso della memoria e delle CPU rispetto alla versione classica. Cercando con google dovresti comunque trovare descrizioni molto più dettagliate rispetto alla mia descrizione.
Registrato

I moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.
MarcoMacUser
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 23


Mostra profilo E-mail
« Risposta #6 inserita:: Dicembre 28, 2011, 12:56:31 am »

Potresti in qualche modo chiarire che cosa stai cercando di fare nell'algoritmo che dici sfruttare molto la CPU? Spero non stia controllando ogni possibile divisore di ogni numero.. Credo che il giusto compromesso sia quello di utilizzare la versione cache friendly del crivello di Eratostene come ti ho già consigliato. In questa versione si calcolano prima i primi inferiori alla radice del numero e si usa poi questa informazione per trovare gli altri. In particolare si dividono gli N valori in blocchi e per ogni blocco, in parallelo, si eliminano i multipli dei primi trovati nella prima fase dell'algoritmo. Per numeri molto grandi questa versione dovrebbe essere molto superiore grazie ad un migliore uso della memoria e delle CPU rispetto alla versione classica. Cercando con google dovresti comunque trovare descrizioni molto più dettagliate rispetto alla mia descrizione.

Ecco spiegato l'algoritmo che sfrutta la CPU:
L'utente inserisce il numeri a cui vuole arrivare nel calcolo.
il programma calcola i numeri primi tra 2 e la radice quadrata di N e li salva in una lista.
Ora il programma divide in blocchi il numero e ad ogni blocco assegna un thread. (es. se inserisco 100 000, il programma suddivide il numero 100 000 in 10 blocchi da 10 000. Il primo thread controlla quali primi ci sono tra 0 e 10 000, il secondo tra 10 001 e 20 000 ecc...).
Ovviamente per evitare corse critiche ho applicato delle MUTEX.
Il singolo thread prende in considerazione i numeri dispari di ogni blocco e prova a dividere ogni numero dispari per tutta la lista dei primi (e qui dovrei migliorare l'algoritmo): se una divisone da come resto 0, vuol dire che il numero non è primo.
Ora provo ad informarmi per bene sull'algoritmo che mi hai segnalato.
Grazie Ancora!
Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +205/-15
Scollegato Scollegato

Messaggi: 2709


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #7 inserita:: Dicembre 28, 2011, 01:35:49 am »

il programma calcola i numeri primi tra 2 e la radice quadrata di N e li salva in una lista.

In quale modo li calcola? Usando un crivello? O che altro?

Il singolo thread prende in considerazione i numeri dispari di ogni blocco e prova a dividere ogni numero dispari per tutta la lista dei primi (e qui dovrei migliorare l'algoritmo)

Questo è orrendamente svantaggioso, oltre al fatto che lo slicing degli intervalli non può e non deve essere fisso: la densità dei numeri primi varia col logaritmo del valor massimo considerato, secondo la notissima stima asintotica 1/logN, dimostrata definitivamente da monsieur de la Vallée Poussin e indipendentemente da Jacques Hadamard.

Comunque sia, il crivello di Eratostene, e in generale gli algoritmi di crivello (= colino, setaccio, vaglio...) si basano sul criterio esattamente opposto: nel caso attuale, dopo aver eliminato per costruzione tutti i numeri pari, si passa a cancellare dall'array in step successivi tutti i numeri compositi (i.e. multipli dell'indice corrente), lasciando quindi alla fine, per esclusione, i soli numeri primi. La prima ottimizzazione a cui pensare è l'uso di un bit array, come già visto; in secondo luogo, si cerca come già ripetuto una versione cache-friendly o cache-aware dell'algoritmo di crivello, tale che ne consenta la parallelizzazione (in thread e/o su più processori).
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io? Occhiolino
celeborn85
Global Moderator
Hero Member
*****

Karma: +57/-13
Scollegato Scollegato

Messaggi: 2127


Mostra profilo
« Risposta #8 inserita:: Dicembre 28, 2011, 01:42:31 am »

Ti faccio notare che l'algoritmo da te descritto è praticamente quello a cui facevo riferimento e che non richiede più calcoli del crivello di eratostene in quanto è semplicemente il crivello di eratostene con l'ordine delle operazioni cambiato. Rispetto al crivello di eratostene "standard" ha però il vantaggio di accedere alla memoria in modo più localizzato. Nota che non hai bisogno di alcun tipo di sincronizzazione se i thread accedono alla lista dei primi sotto la radice del numero in sola lettura e accedono in modo indipendente al bitvector finale. Questa versione è in generale preferibile rispetto alla versione standard quando il numero massimo è molto alto. Utilizzerei comunque un metodo diverso per escludere i primi rispetto alla tua idea di dividere ogni numero per ogni primo. Per ogni primo, cercherei il primo multiplo dispari all'interno dell'intervallo e sommerei quindi al numero due volte il numero primo (in modo da avere un'altro numero dispari) per eliminare tutti i multipli successivi in modo simile a come si fa nel crivello di eratostene standard.
Registrato

I moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.
Pagine: [1]   Vai su
  Stampa  
 
Vai a:  

Copyright © 2011 Edizioni Master SpA. p.iva : 02105820787

Tutti i diritti di proprietà letteraria e artistica riservati. - Privacy



Links to Page