Comment générer des nombres premiers en utilisant la règle 6*k +- 1
nous savons que tous les nombres premiers supérieurs à 3 peuvent être générés en utilisant:
6 * k + 1
6 * k - 1
Cependant nous tous les nombres générés à partir des formules ci-dessus ne sont pas premiers.
For Example:
6 * 6 - 1 = 35 which is clearly divisible by 5.
pour éliminer de telles conditions, j'ai utilisé une méthode de tamis et de supprimer les nombres qui sont des facteurs des nombres générés à partir de la formule ci-dessus.
en Utilisant les faits:
Un nombre est dit premier s'il a pas de facteurs premiers.
- comme nous pouvons générer tous les nombres premiers en utilisant les formules ci-dessus.
- si nous pouvons supprimer tous les multiples des nombres ci-dessus nous sommes laissés avec seulement les nombres premiers.
pour générer des nombres premiers inférieurs à 1000.
ArrayList<Integer> primes = new ArrayList<>();
primes.add(2);//explicitly add
primes.add(3);//2 and 3
int n = 1000;
for (int i = 1; i <= (n / 6) ; i++) {
//get all the numbers which can be generated by the formula
int prod6k = 6 * i;
primes.add(prod6k - 1);
primes.add(prod6k + 1);
}
for (int i = 0; i < primes.size(); i++) {
int k = primes.get(i);
//remove all the factors of the numbers generated by the formula
for(int j = k * k; j <= n; j += k)//changed to k * k from 2 * k, Thanks to DTing
{
int index = primes.indexOf(j);
if(index != -1)
primes.remove(index);
}
}
System.out.println(primes);
cependant, cette méthode génère les nombres premiers correctement. Cela va beaucoup plus vite car nous n'avons pas besoin de vérifier tous les numéros que nous vérifions Dans un tamis.
ma question Est Que Est-ce que je manque une affaire de bord? Ce serait beaucoup mieux, mais je n'ai jamais vu quelqu'un l'utiliser. Suis-je en train de faire quelque chose de mal?
cette approche peut-elle être beaucoup plus optimisée?
prendre un boolean[]
au lieu d'un ArrayList
est beaucoup plus rapide.
int n = 100000000;
boolean[] primes = new boolean[n + 1];
for (int i = 0; i <= n; i++)
primes[i] = false;
primes[2] = primes[3] = true;
for (int i = 1; i <= n / 6; i++) {
int prod6k = 6 * i;
primes[prod6k + 1] = true;
primes[prod6k - 1] = true;
}
for (int i = 0; i <= n; i++) {
if (primes[i]) {
int k = i;
for (int j = k * k; j <= n && j > 0; j += k) {
primes[j] = false;
}
}
}
for (int i = 0; i <= n; i++)
if (primes[i])
System.out.print(i + " ");
7 réponses
vous n'avez pas besoin d'ajouter tous les candidats possibles au tableau. Vous pouvez créer un ensemble pour stocker tous les non premiers.
vous pouvez aussi commencer à vérifier à k * k
, plutôt que 2 * k
public void primesTo1000() {
Set<Integer> notPrimes = new HashSet<>();
ArrayList<Integer> primes = new ArrayList<>();
primes.add(2);//explicitly add
primes.add(3);//2 and 3
for (int i = 1; i < (1000 / 6); i++) {
handlePossiblePrime(6 * i - 1, primes, notPrimes);
handlePossiblePrime(6 * i + 1, primes, notPrimes);
}
System.out.println(primes);
}
public void handlePossiblePrime(
int k, List<Integer> primes, Set<Integer> notPrimes) {
if (!notPrimes.contains(k)) {
primes.add(k);
for (int j = k * k; j <= 1000; j += k) {
notPrimes.add(j);
}
}
}
code non testé, coins de contrôle
Voici une version en embout du tamis comme suggéré dans la réponse référencé par @Will Ness . Plutôt que de retourner le N th prime, cette version renvoie une liste de primes à n:
public List<Integer> primesTo(int n) {
List<Integer> primes = new ArrayList<>();
if (n > 1) {
int limit = (n - 3) >> 1;
int[] sieve = new int[(limit >> 5) + 1];
for (int i = 0; i <= (int) (Math.sqrt(n) - 3) >> 1; i++)
if ((sieve[i >> 5] & (1 << (i & 31))) == 0) {
int p = i + i + 3;
for (int j = (p * p - 3) >> 1; j <= limit; j += p)
sieve[j >> 5] |= 1 << (j & 31);
}
primes.add(2);
for (int i = 0; i <= limit; i++)
if ((sieve[i >> 5] & (1 << (i & 31))) == 0)
primes.add(i + i + 3);
}
return primes;
}
il semble y avoir un bug dans votre code mis à jour qui utilise un tableau booléen (il ne renvoie pas tous les nombres premiers).
public static List<Integer> booleanSieve(int n) {
boolean[] primes = new boolean[n + 5];
for (int i = 0; i <= n; i++)
primes[i] = false;
primes[2] = primes[3] = true;
for (int i = 1; i <= n / 6; i++) {
int prod6k = 6 * i;
primes[prod6k + 1] = true;
primes[prod6k - 1] = true;
}
for (int i = 0; i <= n; i++) {
if (primes[i]) {
int k = i;
for (int j = k * k; j <= n && j > 0; j += k) {
primes[j] = false;
}
}
}
List<Integer> primesList = new ArrayList<>();
for (int i = 0; i <= n; i++)
if (primes[i])
primesList.add(i);
return primesList;
}
public static List<Integer> bitPacking(int n) {
List<Integer> primes = new ArrayList<>();
if (n > 1) {
int limit = (n - 3) >> 1;
int[] sieve = new int[(limit >> 5) + 1];
for (int i = 0; i <= (int) (Math.sqrt(n) - 3) >> 1; i++)
if ((sieve[i >> 5] & (1 << (i & 31))) == 0) {
int p = i + i + 3;
for (int j = (p * p - 3) >> 1; j <= limit; j += p)
sieve[j >> 5] |= 1 << (j & 31);
}
primes.add(2);
for (int i = 0; i <= limit; i++)
if ((sieve[i >> 5] & (1 << (i & 31))) == 0)
primes.add(i + i + 3);
}
return primes;
}
public static void main(String... args) {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
for (int i = 0; i < 10; i++) {
int n = (int) Math.pow(10, i);
Stopwatch timer = Stopwatch.createUnstarted();
timer.start();
List<Integer> result = booleanSieve(n);
timer.stop();
System.out.println(result.size() + "\tBoolean: " + timer);
}
for (int i = 0; i < 10; i++) {
int n = (int) Math.pow(10, i);
Stopwatch timer = Stopwatch.createUnstarted();
timer.start();
List<Integer> result = bitPacking(n);
timer.stop();
System.out.println(result.size() + "\tBitPacking: " + timer);
}
});
}
0 Boolean: 38.51 μs
4 Boolean: 45.77 μs
25 Boolean: 31.56 μs
168 Boolean: 227.1 μs
1229 Boolean: 1.395 ms
9592 Boolean: 4.289 ms
78491 Boolean: 25.96 ms
664116 Boolean: 133.5 ms
5717622 Boolean: 3.216 s
46707218 Boolean: 32.18 s
0 BitPacking: 117.0 μs
4 BitPacking: 11.25 μs
25 BitPacking: 11.53 μs
168 BitPacking: 70.03 μs
1229 BitPacking: 471.8 μs
9592 BitPacking: 3.701 ms
78498 BitPacking: 9.651 ms
664579 BitPacking: 43.43 ms
5761455 BitPacking: 1.483 s
50847534 BitPacking: 17.71 s
maintenant, regardons ces mêmes nombres, quand nous utilisons le tamis de l'algorithme D'Eratosthène:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
après suppression de 2:
5 ,
6, 7 ,8, 9 ,10, 11 ,12, 13 ,14, 15 ,16, 17 ,18, 19 ,20, 21 ,22, 23 ,24, 25
après suppression de 3:
5 ,
6, 7 ,8,9,10, 11 ,12, 13 ,14,15,16, 17 ,18, 19 ,20,21,22, 23 ,24, 25
c'est le même que le premier set! Remarquez qu'ils comprennent tous les deux 25, ce qui n'est pas premier. Si nous y réfléchissons, c'est un résultat évident. Considérez n'importe quel groupe de 6 nombres consécutifs:
6k - 3, 6k - 2, 6k - 1, 6k, 6k + 1, 6k + 2
Si nous prenons un peu, nous obtenons:
3*(2k-1), 2*(3k - 1), 6k - 1, 6*(k), 6k + 1, 2*(3k + 1)
dans tout groupe de 6 nombres consécutifs, trois d'entre eux seront divisibles par deux, et deux d'entre eux seront divisibles par trois. Ce sont exactement les chiffres que nous avons retiré jusqu'à présent! Par conséquent:
votre algorithme pour utiliser seulement 6k - 1
et 6k + 1
est exactement le même que les deux premiers tours du tamis D'Erathosthène.
c'est une belle amélioration de la vitesse sur le tamis, aussi, parce que nous n'avons pas à ajouter tous ces éléments supplémentaires juste pour les enlever. Cela explique pourquoi votre algorithme fonctionne et pourquoi il ne manque aucun cas; parce que c'est exactement la même chose que le tamis.
de toute façon, je suis d'accord qu'une fois que vous avez généré des nombres premiers, votre boolean
chemin est de loin le la plus rapide. j'ai mis en place un benchmark en utilisant votre ArrayList
chemin, votre boolean[]
chemin, et mon propre chemin en utilisant LinkedList
et iterator.remove()
(parce que les retraits sont rapides dans un LinkedList
. Voici le code de mon harnais d'essai. Notez que j'ai effectué le test 12 fois pour s'assurer que le JVM est réchauffé, et j'imprime la taille de la liste et changer la taille de n
pour tenter d'empêcher trop prédiction de branche optimisation. Vous pouvez également obtenir plus rapidement dans les trois méthodes, en utilisant += 6
dans la graine initiale, au lieu de prod6k
:
import java.util.*;
public class PrimeGenerator {
public static List<Integer> generatePrimesArrayList(int n) {
List<Integer> primes = new ArrayList<>(getApproximateSize(n));
primes.add(2);// explicitly add
primes.add(3);// 2 and 3
for (int i = 6; i <= n; i+=6) {
// get all the numbers which can be generated by the formula
primes.add(i - 1);
primes.add(i + 1);
}
for (int i = 0; i < primes.size(); i++) {
int k = primes.get(i);
// remove all the factors of the numbers generated by the formula
for (int j = k * k; j <= n; j += k)// changed to k * k from 2 * k, Thanks
// to DTing
{
int index = primes.indexOf(j);
if (index != -1)
primes.remove(index);
}
}
return primes;
}
public static List<Integer> generatePrimesBoolean(int n) {
boolean[] primes = new boolean[n + 5];
for (int i = 0; i <= n; i++)
primes[i] = false;
primes[2] = primes[3] = true;
for (int i = 6; i <= n; i+=6) {
primes[i + 1] = true;
primes[i - 1] = true;
}
for (int i = 0; i <= n; i++) {
if (primes[i]) {
int k = i;
for (int j = k * k; j <= n && j > 0; j += k) {
primes[j] = false;
}
}
}
int approximateSize = getApproximateSize(n);
List<Integer> primesList = new ArrayList<>(approximateSize);
for (int i = 0; i <= n; i++)
if (primes[i])
primesList.add(i);
return primesList;
}
private static int getApproximateSize(int n) {
// Prime Number Theorem. Round up
int approximateSize = (int) Math.ceil(((double) n) / (Math.log(n)));
return approximateSize;
}
public static List<Integer> generatePrimesLinkedList(int n) {
List<Integer> primes = new LinkedList<>();
primes.add(2);// explicitly add
primes.add(3);// 2 and 3
for (int i = 6; i <= n; i+=6) {
// get all the numbers which can be generated by the formula
primes.add(i - 1);
primes.add(i + 1);
}
for (int i = 0; i < primes.size(); i++) {
int k = primes.get(i);
for (Iterator<Integer> iterator = primes.iterator(); iterator.hasNext();) {
int primeCandidate = iterator.next();
if (primeCandidate == k)
continue; // Always skip yourself
if (primeCandidate == (primeCandidate / k) * k)
iterator.remove();
}
}
return primes;
}
public static void main(String... args) {
int initial = 4000;
for (int i = 0; i < 12; i++) {
int n = initial * i;
long start = System.currentTimeMillis();
List<Integer> result = generatePrimesArrayList(n);
long seconds = System.currentTimeMillis() - start;
System.out.println(result.size() + "\tArrayList Seconds: " + seconds);
start = System.currentTimeMillis();
result = generatePrimesBoolean(n);
seconds = System.currentTimeMillis() - start;
System.out.println(result.size() + "\tBoolean Seconds: " + seconds);
start = System.currentTimeMillis();
result = generatePrimesLinkedList(n);
seconds = System.currentTimeMillis() - start;
System.out.println(result.size() + "\tLinkedList Seconds: " + seconds);
}
}
}
Et les résultats des derniers essais:
3432 ArrayList Seconds: 430
3432 Boolean Seconds: 0
3432 LinkedList Seconds: 90
3825 ArrayList Seconds: 538
3824 Boolean Seconds: 0
3824 LinkedList Seconds: 81
4203 ArrayList Seconds: 681
4203 Boolean Seconds: 0
4203 LinkedList Seconds: 100
4579 ArrayList Seconds: 840
4579 Boolean Seconds: 0
4579 LinkedList Seconds: 111
Il y a plusieurs choses qui pourraient être optimisés.
pour commencer, les opérations" contains "et" removeAll " sur un ArrayList sont assez coûteuses (linéaire pour le premier, pire cas quadratique pour le second) de sorte que vous pourriez ne pas vouloir utiliser L'ArrayList pour cela. Un hachage-ou TreeSet a de meilleures complexités pour cela, étant presque constante (les complexités de hachage sont bizarres) et logarithmique je pense
, Vous pouvez regarder dans le tamis de tamis D'Eratosthène si vous voulez un tamis plus efficace altogeter, mais ce serait en outre le point de votre question sur le 6K +-1 tour. Il est légèrement mais pas remarquablement plus de mémoire cher que votre solution, mais beaucoup plus rapide.
cette approche peut-elle être beaucoup plus optimisée?
la réponse est oui.
je vais commencer par dire que est une bonne idée d'utiliser le tamis sur un sous-ensemble de nombre dans une certaine gamme, et votre suggestion fait exactement cela.
Lire sur génération de nombres Premiers :
...De plus, basé sur le formalismes de tamis, quelques séquences entières (séquence A240673 in OEIS ) sont construits qui pourraient également être utilisés pour produire des amorces à certains intervalles.
la signification de ce paragraphe est que votre approche de commencer par une liste réduite d'entiers a été effectivement adoptée par l'Académie, mais leurs techniques sont plus efficaces (mais aussi, naturellement, plus complexe).
vous pouvez générer vos numéros d'essai avec une roue, en ajoutant 2 et 4 alternativement, ce qui élimine la multiplication en 6 * k +/- 1.
public void primesTo1000() {
Set<Integer> notPrimes = new HashSet<>();
ArrayList<Integer> primes = new ArrayList<>();
primes.add(2); //explicitly add
primes.add(3); //2 and 3
int step = 2;
int num = 5 // 2 and 3 already handled.
while (num < 1000) {
handlePossiblePrime(num, primes, notPrimes);
num += step; // Step to next number.
step = 6 - step; // Step by 2, 4 alternately.
}
System.out.println(primes);
}
probablement la structure de référence standard la plus appropriée pour le tamis D'Eratosthène est le BitSet . Voici ma solution:
static BitSet genPrimes(int n) {
BitSet primes = new BitSet(n);
primes.set(2); // add 2 explicitly
primes.set(3); // add 3 explicitly
for (int i = 6; i <= n ; i += 6) { // step by 6 instead of multiplication
primes.set(i - 1);
primes.set(i + 1);
}
int max = (int) Math.sqrt(n); // don't need to filter multiples of primes bigger than max
// this for loop enumerates all set bits starting from 5 till the max
// sieving 2 and 3 is meaningless: n*6+1 and n*6-1 are never divisible by 2 or 3
for (int i = primes.nextSetBit(5); i >= 0 && i <= max; i = primes.nextSetBit(i+1)) {
// The actual sieve algorithm like in your code
for(int j = i * i; j <= n; j += i)
primes.clear(j);
}
return primes;
}
Utilisation:
BitSet primes = genPrimes(1000); // generate primes up to 1000
System.out.println(primes.cardinality()); // print number of primes
// print all primes like {2, 3, 5, ...}
System.out.println(primes);
// print all primes one per line
for(int prime = primes.nextSetBit(0); prime >= 0; prime = primes.nextSetBit(prime+1))
System.out.println(prime);
// print all primes one per line using java 8:
primes.stream().forEach(System.out::println);
la version à base booléenne peut fonctionner plus rapidement pour les petites valeurs n
, mais si vous avez besoin, par exemple, d'un million de nombres premiers, BitSet
sera plus performant en plusieurs fois et fonctionne réellement correctement. Voici le benchmark boiteux:
public static void main(String... args) {
long start = System.nanoTime();
BitSet res = genPrimes(10000000);
long diff = System.nanoTime() - start;
System.out.println(res.cardinality() + "\tBitSet Seconds: " + diff / 1e9);
start = System.nanoTime();
List<Integer> result = generatePrimesBoolean(10000000); // from durron597 answer
diff = System.nanoTime() - start;
System.out.println(result.size() + "\tBoolean Seconds: " + diff / 1e9);
}
sortie:
664579 BitSet Seconds: 0.065987717
664116 Boolean Seconds: 0.167620323
664579 est le nombre correct de nombres premiers inférieurs à 10000000.
cette méthode ci-dessous montre comment trouver les nombres premiers en utilisant la logique 6k+/-1
cela a été écrit en Python 3.6
def isPrime(n):
if(n<=1):
return 0
elif(n<4): #2 , 3 are prime
return 1
elif(n%2==0): #already excluded no.2 ,so any no. div. by 2 cant be prime
return 0
elif(n<9): #5, 7 are prime and 6,8 are excl. in the above step
return 1
elif(n%3==0):
return 1
f=5 #Till now we have checked the div. of n with 2,3 which means with 4,6,8 also now that is why f=5
r=int(n**.5) #rounding of root n, i.e: floor(sqrt(n)) r*r<=n
while(f<=r):
if(n%f==0): #checking if n has any primefactor lessthan sqrt(n), refer LINE 1
return 0
if(n%(f+2)==0): #remember her we are not incrementing f, see the 6k+1 rule to understand this while loop steps ,you will see that most values of f are prime
return 0
f=f+6
return 1
def prime_nos():
counter=2 #we know 2,3 are prime
print(2)
print(3) #we know 2,3 are prime
i=1
s=5 #sum 2+3
t=0
n=int(input("Enter the upper limit( should be > 3: "))
n=(n-1)//6 #finding max. limit(n=6*i+1) upto which I(here n on left hand side) should run
while(i<n):#2*(10**6)):
if (isPrime(6*i-1)):
counter=counter+1
print(6*i-1) #prime no
if(isPrime(6*i+1)):
counter=counter+1
print(6*i+1) #prime no
i+=1
prime_nos() #fn. call