Programmierung zum Wochenstart
bearbeitet von
Hallo,
hier ist meine Lösung, 1:1 kopiert aus der Nachricht an 1UP. Sie verwendet BigInt, und ist vermutlich deshalb schneller, weil meine Klasse SieveFactor es vermeidet, bei der sieve(c)-Prüfung zu dividieren. Eine BigInt Division dürfte relativ langsam sein. Deswegen addiere ich hoch - also genau das, was man in Erastosthenes so macht. Die Kunst war, die Siebschleife jedes Primfaktors unterbrechbar zu machen und nur bei Bedarf so weit wie nötig laufen zu lassen.
Die Geschwindigkeit lässt sich übrigens beinahe verdoppeln (bei mir von 3500ms auf 2000ms für die Primzahlen bis 1000.000), wenn man Speicher investiert und dem SieveFactor ein Property fsquare gönnt, in dem das Quadrat des Faktors steht. Dann muss man in isAbort nicht jedesmal neu quadrieren. Ich habe das mal auskommentiert hinzugefügt. Das Doppelte des Faktors zu speichern um eine Addition in der while-Schleife zu sparen hat sich nicht gelohnt.
Akrobatische Kunststücke auf Trampolins waren mir nicht geheuer, das hab ich seingelassen.
~~~js
let getPrimes = (function() {
// SieveFactor repräsentiert eine Primzahl und ihre (2n+1)-fachen für den
// Erastothenes-Algorithmus und stellt eine Eliminier-Schleife für einen
// Primfaktor im Siebalgorithmus dar, die gerade so weit läuft wie für einen
// Kandidaten nötig ist und dann Pause macht.
// Die (2n)-fachen des Primfaktors interessieren nicht, das sind
// gerade Zahlen und werden vom Generator gar nicht erst angefragt.
class SieveFactor {
// Speichere das 2n+1-fache als value-Property
// Hat diverse Vorteile: es ist fixer und man muss nicht wissen, ob n ein BigInt ist
// oder nicht.
constructor(n) {
this.factor = n;
this.value = n;
// -> this.fsquare = n*n;
}
// Endebedingung: eine bekannte Primzahl ist größer als sqrt(Kandidat)
isAbort(c) {
return c < this.factor*this.factor; /* c < this->fsquare */
}
// Versuche den Kandidaten auszusieben. Die Methode liefert true, wenn
// die Abbruchbedingung erreicht ist, oder wenn der Kandidat ein (2n+1)-faches des
// Faktors ist.
sieve(c) {
if (this.isAbort(c))
return true;
// Ist der Value kleiner als der Kandidat, ist die Erastothenes-Schleife für
// diesen Primfaktor noch nicht weit genug gelaufen. Diese Durchläufe jetzt
// durchführen.
while (this.value < c)
this.value += this.factor + this.factor;
// Ok, jetzt kann geprüft werden ob der Kandidat ein Vielfaches des Faktors ist.
return this.value == c;
}
}
// Generatorfunktion für Primzahlen.
return function* () {
// 2 ist eine.
yield BigInt(2);
// Gefundene Siebfaktoren
let factors = [];
// Ab geht's, Endlosschleife ab 3 in 2-er Schritten
for (let candidate = BigInt(3); true; candidate += BigInt(2)) {
// Versuche den Kandidaten auszusieben
let factor = factors.find(s => s.sieve(candidate));
// Kein Sandkorn oder eins mit Abbruch-Bedingung -> Kandidat ist prim
if (!factor || factor.isAbort(candidate)) {
yield candidate;
factors.push(new SieveFactor(candidate));
}
}
};
})();
// Test, liefert bei mir 78499 Primzahlen in 2 Sekunden.
console.clear();
console.log("begin");
let num = 0;
let start = Date.now();
for (let p of getPrimes()) {
//console.log(p);
num++;
if (p > 1000000) break;
}
console.log("found " + num + " primes in " + (Date.now()-start) + "ms");
~~~
_Rolf_
--
sumpsi - posui - clusi