Die eleganteste Art, Primzahlen zu erzeugen [closed]
Lesezeit: 10 Minuten
David Johnston
Was ist der eleganteste Weg, diese Funktion zu implementieren:
ArrayList generatePrimes(int n)
Diese Funktion generiert die erste n Primzahlen (Bearbeiten: wo n>1), Also generatePrimes(5) wird ein zurückgeben ArrayList mit {2, 3, 5, 7, 11}. (Ich mache das in C #, aber ich bin mit einer Java-Implementierung zufrieden – oder einer anderen ähnlichen Sprache für diese Angelegenheit (also nicht Haskell)).
Ich weiß, wie man diese Funktion schreibt, aber als ich es letzte Nacht gemacht habe, war es nicht so schön, wie ich gehofft hatte. Hier ist, was ich herausgefunden habe:
ArrayList generatePrimes(int toGenerate)
{
ArrayList primes = new ArrayList();
primes.Add(2);
primes.Add(3);
while (primes.Count < toGenerate)
{
int nextPrime = (int)(primes[primes.Count - 1]) + 2;
while (true)
{
bool isPrime = true;
foreach (int n in primes)
{
if (nextPrime % n == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
break;
}
else
{
nextPrime += 2;
}
}
primes.Add(nextPrime);
}
return primes;
}
Ich mache mir keine allzu großen Sorgen um die Geschwindigkeit, obwohl ich nicht möchte, dass sie offensichtlich ineffizient ist. Es ist mir egal, welche Methode verwendet wird (naiv oder Sieb oder irgendetwas anderes), aber ich möchte, dass es ziemlich kurz und offensichtlich ist, wie es funktioniert.
Bearbeiten: Danke an alle, die geantwortet haben, obwohl viele meine eigentliche Frage nicht beantwortet haben. Um es noch einmal zu wiederholen, ich wollte ein nettes, sauberes Stück Code, das eine Liste von Primzahlen generiert. Ich weiß bereits, wie man es auf verschiedene Arten macht, aber ich neige dazu, Code zu schreiben, der nicht so klar ist, wie er sein könnte. In diesem Thread wurden einige gute Optionen vorgeschlagen:
Eine schönere Version dessen, was ich ursprünglich hatte (Peter Smit, jmservera und Rekreativc)
Eine sehr saubere Umsetzung des Siebes von Eratosthenes (starblue)
Verwenden Sie Javas BigIntegers und nextProbablePrime für sehr einfachen Code, obwohl ich mir nicht vorstellen kann, dass er besonders effizient ist (dfa)
Verwenden Sie LINQ, um die Liste der Primzahlen träge zu generieren (Maghis)
Viele Primzahlen in eine Textdatei schreiben und bei Bedarf einlesen (darin)
Bearbeiten 2Hinweis: Ich habe in C# einige der hier angegebenen Methoden und eine weitere Methode implementiert, die hier nicht erwähnt wird. Sie alle finden den ersten n Primzahlen effektiv (und ich habe eine anständige Methode, um die Grenze für die Siebe zu finden).
nein, und es ist auch nicht für Project Euler 🙂
– David Johnston
25. Juni 09 um 9:45 Uhr
Es wäre besser, wenn ich ienumerable zurückgeben und nacheinander nachgeben würde
– Felice Pollano
6. Juli 12 um 16:22 Uhr
Was ich gerne wissen würde ist, was ist das am wenigsten elegante Art, Primzahlen zu erzeugen. Ich denke etwas mit einer Access-Datenbank?
– j_random_hacker
20. August 13 um 22:36 Uhr
zum vergleich a 2008 Haskell-Code von BMeph: nubBy (((>1).).gcd) [2..]. Es lässt nur Nicht-Duplikate unter den natürlichen Zahlen, beginnend mit 2, während jede Zahl, deren als Duplikat betrachtet wird gcd mit einer der zuvor gefundenen Zahlen größer als 1 ist. Es ist sehr ineffizient, quadratisch in der Anzahl der erzeugten Primzahlen. Aber es ist elegant.
– Will Ness
22. August 13 um 14:21 Uhr
am meisten elegantIMO, ist Haskells import Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (p-> [p*p, p*p+p*2..]) ) } aber das ist selbstverständlich komplett meinungsbasiert.
– Will Ness
25. August 15 um 10:51 Uhr
sternenblau
Verwenden Sie die Schätzung
pi(n) = n / log(n)
für die Anzahl der Primzahlen bis n eine Grenze finden und dann ein Sieb verwenden. Die Schätzung unterschätzt die Anzahl der Primzahlen bis n etwas, sodass das Sieb etwas größer als nötig ist, was in Ordnung ist.
Dies ist mein Standard-Java-Sieb, das auf einem normalen Laptop die erste Million Primzahlen in etwa einer Sekunde berechnet:
public static BitSet computePrimes(int limit)
{
final BitSet primes = new BitSet();
primes.set(0, false);
primes.set(1, false);
primes.set(2, limit, true);
for (int i = 0; i * i < limit; i++)
{
if (primes.get(i))
{
for (int j = i * i; j < limit; j += i)
{
primes.clear(j);
}
}
}
return primes;
}
Das ist eine sehr schöne Umsetzung des Siebes von Eratosthenes
– David Johnston
25. Juni 09 um 10:55 Uhr
sollte es nicht ausreichen, während zu loopen i <= Math.sqrt(limit) in der äußeren Schleife?
– Christoph
26. Juni 09 um 8:04 Uhr
@David Johnstone Nein, pi (n) = n / log (n) unterschätzt die Anzahl der Primzahlen bis n, was in die entgegengesetzte Richtung geht. Ich bin froh, dass Sie eine viel schönere Annäherung gefunden haben.
– sternenblau
4. Juli 09 um 9:07 Uhr
Wenn Sie bereit sind, alle Vielfachen von 2 in einer eigenen Schleife zu entfernen, können Sie j + = 2 * i als Schleifeninkrement verwenden, um zusätzliche Laufzeit zu sparen, und Sie können dies einmal mit einer Bitverschiebung berechnen
– Nick Larsen
16. August 10 um 14:24 Uhr
Durch Ersetzen BitSet durch eine Klasse, die Radfaktorisierung für 2, 3 und 5 implementiert, wird es fast dreimal schneller.
– sternenblau
26. Oktober 10 um 14:15 Uhr
Vielen Dank an alle, die hilfreiche Antworten gegeben haben. Hier sind meine Implementierungen einiger verschiedener Methoden, um die erste zu finden n Primzahlen in C#. Die ersten beiden Methoden sind so ziemlich das, was hier gepostet wurde. (Die Namen der Poster stehen neben dem Titel.) Ich plane, irgendwann das Sieb von Atkin zu machen, obwohl ich vermute, dass es nicht ganz so einfach sein wird wie die Methoden hier derzeit. Wenn jemand eine Möglichkeit sieht, eine dieser Methoden zu verbessern, würde ich es gerne wissen 🙂
Die erste Primzahl ist 2. Fügen Sie diese zu einer Liste von Primzahlen hinzu. Die nächste Primzahl ist die nächste Zahl, die durch keine Zahl auf dieser Liste ohne Rest teilbar ist.
public static List<int> GeneratePrimesNaive(int n)
{
List<int> primes = new List<int>();
primes.Add(2);
int nextPrime = 3;
while (primes.Count < n)
{
int sqrt = (int)Math.Sqrt(nextPrime);
bool isPrime = true;
for (int i = 0; (int)primes[i] <= sqrt; i++)
{
if (nextPrime % primes[i] == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
primes.Add(nextPrime);
}
nextPrime += 2;
}
return primes;
}
Dies wurde optimiert, indem nur auf Teilbarkeit bis zur Quadratwurzel der getesteten Zahl getestet wurde; und indem nur ungerade Zahlen getestet werden. Dies kann weiter optimiert werden, indem nur Zahlen des Formulars getestet werden 6k+[1, 5]oder 30k+[1, 7, 11, 13, 17, 19, 23, 29] oder bald.
Sieb des Eratosthenes (sternblau)
Dies findet alle Primzahlen zu k. Um eine Liste der ersten zu machen n Primzahlen, müssen wir zuerst den Wert von approximieren nte Primzahl. Die folgende Methode, wie hier beschrieben, tut dies.
public static int ApproximateNthPrime(int nn)
{
double n = (double)nn;
double p;
if (nn >= 7022)
{
p = n * Math.Log(n) + n * (Math.Log(Math.Log(n)) - 0.9385);
}
else if (nn >= 6)
{
p = n * Math.Log(n) + n * Math.Log(Math.Log(n));
}
else if (nn > 0)
{
p = new int[] { 2, 3, 5, 7, 11 }[nn - 1];
}
else
{
p = 0;
}
return (int)p;
}
// Find all primes up to and including the limit
public static BitArray SieveOfEratosthenes(int limit)
{
BitArray bits = new BitArray(limit + 1, true);
bits[0] = false;
bits[1] = false;
for (int i = 0; i * i <= limit; i++)
{
if (bits[i])
{
for (int j = i * i; j <= limit; j += i)
{
bits[j] = false;
}
}
}
return bits;
}
public static List<int> GeneratePrimesSieveOfEratosthenes(int n)
{
int limit = ApproximateNthPrime(n);
BitArray bits = SieveOfEratosthenes(limit);
List<int> primes = new List<int>();
for (int i = 0, found = 0; i < limit && found < n; i++)
{
if (bits[i])
{
primes.Add(i);
found++;
}
}
return primes;
}
Sieb von Sundaram
Ich habe nur entdeckt dieses Sieb vor kurzem, aber es kann ganz einfach implementiert werden. Meine Implementierung ist nicht so schnell wie das Sieb des Eratosthenes, aber deutlich schneller als die naive Methode.
public static BitArray SieveOfSundaram(int limit)
{
limit /= 2;
BitArray bits = new BitArray(limit + 1, true);
for (int i = 1; 3 * i + 1 < limit; i++)
{
for (int j = 1; i + j + 2 * i * j <= limit; j++)
{
bits[i + j + 2 * i * j] = false;
}
}
return bits;
}
public static List<int> GeneratePrimesSieveOfSundaram(int n)
{
int limit = ApproximateNthPrime(n);
BitArray bits = SieveOfSundaram(limit);
List<int> primes = new List<int>();
primes.Add(2);
for (int i = 1, found = 1; 2 * i + 1 <= limit && found < n; i++)
{
if (bits[i])
{
primes.Add(2 * i + 1);
found++;
}
}
return primes;
}
FYI – Ich musste Ihren Hauptschleifenzähler auf “for (int i = 0; i * i <= limit && i * i > 0; i++)” ändern, um einen Überlauf zu verhindern.
– Jacobs Datenlösungen
28. Dezember 2011 um 20:48 Uhr
Diese Implementierung des Siebs von Sundaram ist eine der wenigen richtigen, die es gibt. Die meisten von ihnen verwenden beim Rechnen falsche Grenzen für i und j i+j+2*i*j was zu einer falschen Ausgabe führt.
– jahackbeth
10. Februar 15 um 4:48 Uhr
Gruselcoder
Wiederbelebung einer alten Frage, aber ich bin beim Spielen mit LINQ darüber gestolpert.
Dieser Code erfordert .NET4.0 oder .NET3.5 mit parallelen Erweiterungen
public List<int> GeneratePrimes(int n) {
var r = from i in Enumerable.Range(2, n - 1).AsParallel()
where Enumerable.Range(1, (int)Math.Sqrt(i)).All(j => j == 1 || i % j != 0)
select i;
return r.ToList();
}
Warum ist dies nicht die akzeptierte Antwort? Der Code hier ist viel kürzer, eleganter und viel schneller als der Code in der akzeptierten Antwort. Ich wünschte, ich könnte mehr als einmal upvoten!
– Avrohom Jisroel
5. Mai 20 um 22:23 Uhr
Peter Schmitt
Du bist auf dem guten Weg.
Einige Kommentare
primes.Add(3); bewirkt, dass diese Funktion für Zahl = 1 nicht funktioniert
Sie müssen die Division nicht mit Primzahlen testen, die größer sind als die Quadratwurzel der zu testenden Zahl.
Vorgeschlagener Code:
ArrayList generatePrimes(int toGenerate)
{
ArrayList primes = new ArrayList();
if(toGenerate > 0) primes.Add(2);
int curTest = 3;
while (primes.Count < toGenerate)
{
int sqrt = (int) Math.sqrt(curTest);
bool isPrime = true;
for (int i = 0; i < primes.Count && primes.get(i) <= sqrt; ++i)
{
if (curTest % primes.get(i) == 0)
{
isPrime = false;
break;
}
}
if(isPrime) primes.Add(curTest);
curTest +=2
}
return primes;
}
public class PrimeGenerator implements Iterator<BigInteger>, Iterable<BigInteger> {
private BigInteger p = BigInteger.ONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public BigInteger next() {
p = p.nextProbablePrime();
return p;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Iterator<BigInteger> iterator() {
return this;
}
}
@Test
public void printPrimes() {
for (BigInteger p : new PrimeGenerator()) {
System.out.println(p);
}
}
Miller-Rabbin ist sehr schnell und der Code ist sehr einfach. Durch ausreichende Iterationen ist es zuverlässig genug, um mit zufälligen CPU-Ausfällen in Bezug auf die Wahrscheinlichkeit eines Fehlalarms zu konkurrieren. Der Nachteil des Algorithmus ist, dass es schwierig ist zu verstehen, warum er tatsächlich funktioniert.
– Brian
7. Dezember 10 um 15:06 Uhr
RaphM
Auf keinen Fall effizient, aber vielleicht am lesbarsten:
public static IEnumerable<int> Range(int from, int to = int.MaxValue)
{
for (int i = from; i <= to; i++) yield return i;
}
Eigentlich nur eine Variation einiger Beiträge hier mit schönerer Formatierung.
Miller-Rabbin ist sehr schnell und der Code ist sehr einfach. Durch ausreichende Iterationen ist es zuverlässig genug, um mit zufälligen CPU-Ausfällen in Bezug auf die Wahrscheinlichkeit eines Fehlalarms zu konkurrieren. Der Nachteil des Algorithmus ist, dass es schwierig ist zu verstehen, warum er tatsächlich funktioniert.
Der einfachste, aber eleganteste Weg, ALLE PRIMES zu berechnen, wäre dieser Weg, aber dieser Weg ist langsam und die Speicherkosten sind für höhere Zahlen viel höher, weil die Fakultätsfunktion (!) verwendet wird … aber es demonstriert eine Variation des Wilson-Theorems in einer Anwendung zu Erzeuge alle Primzahlen durch einen in Python implementierten Algorithmus
#!/usr/bin/python
f=1 # 0!
p=2 # 1st prime
while True:
if f%p%2:
print p
p+=1
f*=(p-2)
.
7972700cookie-checkDie eleganteste Art, Primzahlen zu erzeugen [closed]yes
nein, und es ist auch nicht für Project Euler 🙂
– David Johnston
25. Juni 09 um 9:45 Uhr
Es wäre besser, wenn ich ienumerable zurückgeben und nacheinander nachgeben würde
– Felice Pollano
6. Juli 12 um 16:22 Uhr
Was ich gerne wissen würde ist, was ist das am wenigsten elegante Art, Primzahlen zu erzeugen. Ich denke etwas mit einer Access-Datenbank?
– j_random_hacker
20. August 13 um 22:36 Uhr
zum vergleich a 2008 Haskell-Code von BMeph:
nubBy (((>1).).gcd) [2..]
. Es lässt nur Nicht-Duplikate unter den natürlichen Zahlen, beginnend mit 2, während jede Zahl, deren als Duplikat betrachtet wirdgcd
mit einer der zuvor gefundenen Zahlen größer als 1 ist. Es ist sehr ineffizient, quadratisch in der Anzahl der erzeugten Primzahlen. Aber es ist elegant.– Will Ness
22. August 13 um 14:21 Uhr
am meisten elegantIMO, ist Haskells
import Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (p-> [p*p, p*p+p*2..]) ) }
aber das ist selbstverständlich komplett meinungsbasiert.– Will Ness
25. August 15 um 10:51 Uhr