sono andato a vedere l'implementazione del crivello, anche per fare un paragone con la mia
potresti migliorare quanto hai già fatto usando una frazione della memoria, ci stavo pensando su: invece di memorizzare interi, potresti usare le sequenze di bit (in C++ è il tipo di dato
bitset); in pratica un 1/true dice che un certo intero è primo, 0/false che non lo è;
per semplificare cosa voglio dire, faccio una corrispondenza 1:1 tra vettore di bit e numeri interi
l'indice
i del bitset corrispone all'intero
i
il vettore va inizializzato con tutti 1 (presumi falsamente che tutti gli interi siano primi), poi si mettono a zero i primi 2 bit perché 0 e 1 non sono primi; supponendo che il bitseti si chiami
p
0011111111111111111111....
dice che 0 e 1 non sono primi (perché p[0]=0, p[1]=0) mentre tutti gli altri sono primi
l'indice successivo è il 2, p[2] dice che il numero 2 è primo --> crivelli il seguito del bitset eliminando multipli di 2 (eliminare = metti il bit a 0)
il bit successivo ti dice che 3 è primo --> crivelli i multipli di 3
il bit successivo (corrisponde al numero 4) è stato crivellato dal 2 --> non primo perché p[4]=0
e così via...
alla fine hai un bitset simile a questo (i primi 2 bit sono lo 0 e l'1 che non sono primi):
001101010001010001010001000001010000010001010001000...
che corrsisponde ai numeri
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 (primi <=50)
in questo modo, per interi a 32 bit (o più: 64...), con il bitset usi un solo bit per numero, invece se usi un array di int ne usi almeno 32
questo significa che riduci l'uso della memoria di 32 volte
io l'esempio l'ho fatto usando anche i pari e includendo 0 e 1, potresti fare la stessa cosa usando il tuo approccio che usa solo i dispari ed il calcolo degli indici;
ovviamente vanno prese accorgimenti: con il primo bitset costruisci un vector "di base" (da usare in seguito) dove gli interi vanno memorizzati per esteso (basta fare pushback degli indici: nell'esempio precedente l'indice 29 è il primo 29); per le espansioni usi il bitset, e alla fine lo scorri per ricreare gli interi.
Questo metodo perde un po' in efficienza perché alla fine di ogni espansione va comunque aggiornato il vector iniziale con i nuovi primi trovati), per le espansioni successive risparmi memoria.