Ich versuche, eine Methode zu finden, die eine ganze Zahl verwendet und einen Booleschen Wert zurückgibt, um zu sagen, ob die Zahl eine Primzahl ist oder nicht, und ich weiß nicht viel C; Würde es jemandem etwas ausmachen, mir einige Hinweise zu geben?
Grundsätzlich würde ich das in C # so machen:
static bool IsPrime(int number)
{
for (int i = 2; i < number; i++)
{
if (number % i == 0 && i != number)
return false;
}
return true;
}
i < number
übertrieben ist. Per Definition, wenn eine Zahlx = a * b
entwedera
oderb
ist< int(sqrt(x))
und die andere größer ist. Ihre Schleife sollte also nur bis zu gehen müssenint(sqrt(x))
.Antworten:
OK, also vergiss C. Angenommen, ich gebe dir eine Nummer und frage dich, ob es eine Primzahl ist. Wie machst du das? Schreiben Sie die Schritte klar auf und sorgen Sie sich dann darum, sie in Code zu übersetzen.
Sobald Sie den Algorithmus festgelegt haben, können Sie leichter herausfinden, wie ein Programm geschrieben wird, und andere können Ihnen dabei helfen.
Bearbeiten: Hier ist der C # -Code, den Sie gepostet haben:
static bool IsPrime(int number) { for (int i = 2; i < number; i++) { if (number % i == 0 && i != number) return false; } return true; }
Dies ist C fast so wie es ist; Es gibt keinen
bool
Typ in C und neintrue
oderfalse
, daher müssen Sie ihn ein wenig ändern (bearbeiten: Kristopher Johnson weist korrekt darauf hin, dass C99 den Header stdbool.h hinzugefügt hat). Da einige Benutzer keinen Zugriff auf eine C99-Umgebung haben (Sie sollten jedoch eine verwenden!), Nehmen wir diese geringfügige Änderung vor:int IsPrime(int number) { int i; for (i=2; i<number; i++) { if (number % i == 0 && i != number) return 0; } return 1; }
Dies ist ein perfekt gültiges C-Programm, das macht, was Sie wollen. Wir können es ohne großen Aufwand ein wenig verbessern. Beachten Sie zunächst, dass dies
i
immer weniger als istnumber
, sodass die Prüfungi != number
immer erfolgreich ist. wir können es loswerden.Außerdem müssen Sie die Teiler nicht bis zum Ende ausprobieren
number - 1
. Sie können die Überprüfung beenden, wenn Sie sqrt (Nummer) erreichen. Dasqrt
es sich um eine Gleitkommaoperation handelt, die eine ganze Reihe von Feinheiten mit sich bringt, werden wir nicht wirklich berechnensqrt(number)
. Stattdessen können wir das einfach überprüfeni*i <= number
:int IsPrime(int number) { int i; for (i=2; i*i<=number; i++) { if (number % i == 0) return 0; } return 1; }
Eine letzte Sache; Es gab einen kleinen Fehler in Ihrem ursprünglichen Algorithmus! Wenn
number
negativ oder null oder eins ist, behauptet diese Funktion, dass die Zahl eine Primzahl ist. Sie möchten wahrscheinlich richtig damit umgehen, und Sie möchten möglicherweise nichtnumber
signiert werden, da Sie sich eher nur um positive Werte kümmern:int IsPrime(unsigned int number) { if (number <= 1) return 0; // zero and one are not prime unsigned int i; for (i=2; i*i<=number; i++) { if (number % i == 0) return 0; } return 1; }
Dies ist definitiv nicht der schnellste Weg, um zu überprüfen, ob eine Zahl eine Primzahl ist, aber es funktioniert und ist ziemlich einfach. Wir mussten Ihren Code kaum ändern!
quelle
bool
,true
undfalse
.Ich bin überrascht, dass niemand dies erwähnt hat.
Verwenden Sie das Sieb von Eratosthenes
Einzelheiten:
Das Sieb von Eratosthenes findet eine Primzahl und speichert sie. Wenn eine neue Zahl auf Primzahl geprüft wird, werden alle vorherigen Primzahlen mit der Liste der bekannten Primzahlen verglichen.
Gründe dafür:
quelle
O(n)
im Weltraum und solange Ihre Berechnung für einen einzelnen Wert ist, ist dies eine enorme Platzverschwendung ohne Leistungsgewinn.O(n log n)
oder größer, wenn Sie große Zahlen unterstützen ...)static const
Bitmap mit den Primzahlen haben und diese verwenden, anstatt sie zur Laufzeit zu füllen.Stephen Canon hat es sehr gut beantwortet!
Aber
Dies ist dreimal so schnell wie das Testen aller m bis zu √n.
int IsPrime(unsigned int number) { if (number <= 3 && number > 1) return 1; // as 2 and 3 are prime else if (number%2==0 || number%3==0) return 0; // check if number is divisible by 2 or 3 else { unsigned int i; for (i=5; i*i<=number; i+=6) { if (number % i == 0 || number%(i + 2) == 0) return 0; } return 1; } }
quelle
0
wenn (Nummer == 1), da 1 keine Primzahl ist.quelle
Dieses Programm ist sehr effizient, um eine einzelne Zahl auf Primalitätsprüfung zu prüfen.
bool check(int n){ if (n <= 3) { return n > 1; } if (n % 2 == 0 || n % 3 == 0) { return false; } int sq=sqrt(n); //include math.h or use i*i<n in for loop for (int i = 5; i<=sq; i += 6) { if (n % i == 0 || n % (i + 2) == 0) { return false; } } return true; }
quelle
i=2
bis geheni<=ceil(sqrt(n))
. Sie haben in Ihrem Test zwei Zahlen verpasst: Zuerst wird gegossen, um(int)
densqrt(n)
Rumpf als Dezimalzahl zu definieren. Zweitens haben Sie verwendeti<sq
, wann es sein solltei<=sq
. Nehmen wir nun eine Zahl an, die zu diesem Problem passt. Eine zusammengesetzte Zahln
, dieceil(sqrt(n))
den kleineren Faktor hat. Ihre innere Schleife läuft wie folgt: (5, 7), (11, 13), (17, 19), (23, 25), (29, 31), (35, 37), (41, 43), und so weitern%i
undn%(i+2)
. Angenommen, wir bekommensqrt(1763)=41.98
. Als1763=41*43
eine zusammengesetzte Zahl. Ihre Schleife läuft nur bis(35, 37)
und schlägt fehl.ceil()
ich das Problem sorgfältig analysiert hatte, stellte ich fest, dass es zwar von vielen Websites empfohlen wird, aber einfach übertrieben ist. Sie können nur trunk und testeni<=sqrt(n)
und es wird in Ordnung sein. Die Testfälle sind große Tween-Primzahlen. Beispiel:86028221*86028223=7400854980481283
undsqrt(7400854980481283)~86028222
. Und das kleinere Wissen zwischen Primzahlen2
und3
gibt,sqrt(6)=2.449
dass Stamm noch verlassen wird2
. (Aber kleiner ist kein Testfall, nur ein Vergleich, um einen Punkt zu machen). Ja, der Algorithmus ist jetzt korrekt. Keine Notwendigkeit zu verwendenceil()
.Überprüfen Sie den Modul jeder Ganzzahl von 2 bis zur Wurzel der zu überprüfenden Zahl.
Wenn der Modul gleich Null ist, ist er keine Primzahl.
Pseudocode:
bool IsPrime(int target) { for (i = 2; i <= root(target); i++) { if ((target mod i) == 0) { return false; } } return true; }
quelle
Nachdem ich diese Frage gelesen hatte, war ich fasziniert von der Tatsache, dass einige Antworten eine Optimierung durch Ausführen einer Schleife mit Vielfachen von 2 * 3 = 6 boten.
Also erstelle ich eine neue Funktion mit der gleichen Idee, aber mit Vielfachen von 2 * 3 * 5 = 30.
int check235(unsigned long n) { unsigned long sq, i; if(n<=3||n==5) return n>1; if(n%2==0 || n%3==0 || n%5==0) return 0; if(n<=30) return checkprime(n); /* use another simplified function */ sq=ceil(sqrt(n)); for(i=7; i<=sq; i+=30) if (n%i==0 || n%(i+4)==0 || n%(i+6)==0 || n%(i+10)==0 || n%(i+12)==0 || n%(i+16)==0 || n%(i+22)==0 || n%(i+24)==0) return 0; return 1; }
Durch Ausführen beider Funktionen und Überprüfen der Zeiten konnte ich feststellen, dass diese Funktion wirklich schneller ist. Sehen wir uns 2 Tests mit 2 verschiedenen Primzahlen an:
$ time ./testprimebool.x 18446744069414584321 0 f(2,3) Yes, its prime. real 0m14.090s user 0m14.096s sys 0m0.000s $ time ./testprimebool.x 18446744069414584321 1 f(2,3,5) Yes, its prime. real 0m9.961s user 0m9.964s sys 0m0.000s $ time ./testprimebool.x 18446744065119617029 0 f(2,3) Yes, its prime. real 0m13.990s user 0m13.996s sys 0m0.004s $ time ./testprimebool.x 18446744065119617029 1 f(2,3,5) Yes, its prime. real 0m10.077s user 0m10.068s sys 0m0.004s
Also dachte ich, würde jemand zu viel gewinnen, wenn er verallgemeinert würde? Ich habe mir eine Funktion ausgedacht, die zuerst eine Belagerung durchführt, um eine bestimmte Liste von Urprimzahlen zu bereinigen, und dann diese Liste verwendet, um die größere zu berechnen.
int checkn(unsigned long n, unsigned long *p, unsigned long t) { unsigned long sq, i, j, qt=1, rt=0; unsigned long *q, *r; if(n<2) return 0; for(i=0; i<t; i++) { if(n%p[i]==0) return 0; qt*=p[i]; } qt--; if(n<=qt) return checkprime(n); /* use another simplified function */ if((q=calloc(qt, sizeof(unsigned long)))==NULL) { perror("q=calloc()"); exit(1); } for(i=0; i<t; i++) for(j=p[i]-2; j<qt; j+=p[i]) q[j]=1; for(j=0; j<qt; j++) if(q[j]) rt++; rt=qt-rt; if((r=malloc(sizeof(unsigned long)*rt))==NULL) { perror("r=malloc()"); exit(1); } i=0; for(j=0; j<qt; j++) if(!q[j]) r[i++]=j+1; free(q); sq=ceil(sqrt(n)); for(i=1; i<=sq; i+=qt+1) { if(i!=1 && n%i==0) return 0; for(j=0; j<rt; j++) if(n%(i+r[j])==0) return 0; } return 1; }
Ich gehe davon aus, dass ich den Code nicht optimiert habe, aber es ist fair. Nun die Tests. Aufgrund des vielen dynamischen Speichers habe ich erwartet, dass die Liste 2 3 5 etwas langsamer ist als die fest codierte Liste 2 3 5. Aber es war in Ordnung, wie Sie unten sehen können. Danach wurde die Zeit immer kleiner und gipfelte in der besten Liste:
Mit 8,6 Sekunden. Wenn also jemand ein fest codiertes Programm erstellen würde, das eine solche Technik verwendet, würde ich empfehlen, die Liste 2 3 und 5 zu verwenden, da der Gewinn nicht so groß ist. Aber auch, wenn Sie bereit sind zu codieren, ist diese Liste in Ordnung. Das Problem ist, dass Sie nicht alle Fälle ohne eine Schleife angeben
ORs
können , oder Ihr Code wäre sehr groß (es würde 1658879 geben , das heißt||
in der jeweiligen internenif
). Die nächste Liste:Die Zeit wurde mit 13 Sekunden immer größer. Hier der ganze Test:
$ time ./testprimebool.x 18446744065119617029 2 3 5 f(2,3,5) Yes, its prime. real 0m12.668s user 0m12.680s sys 0m0.000s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 f(2,3,5,7) Yes, its prime. real 0m10.889s user 0m10.900s sys 0m0.000s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 f(2,3,5,7,11) Yes, its prime. real 0m10.021s user 0m10.028s sys 0m0.000s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 f(2,3,5,7,11,13) Yes, its prime. real 0m9.351s user 0m9.356s sys 0m0.004s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 f(2,3,5,7,11,13,17) Yes, its prime. real 0m8.802s user 0m8.800s sys 0m0.008s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 f(2,3,5,7,11,13,17,19) Yes, its prime. real 0m8.614s user 0m8.564s sys 0m0.052s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 f(2,3,5,7,11,13,17,19,23) Yes, its prime. real 0m13.013s user 0m12.520s sys 0m0.504s $ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 29 f(2,3,5,7,11,13,17,19,23,29) q=calloc(): Cannot allocate memory
PS. Ich habe (r) nicht absichtlich freigegeben und diese Aufgabe dem Betriebssystem übergeben, da der Speicher freigegeben wird, sobald das Programm beendet wird, um etwas Zeit zu gewinnen. Es wäre jedoch ratsam, es freizugeben, wenn Sie beabsichtigen, Ihren Code nach der Berechnung weiter auszuführen.
BONUS
int check2357(unsigned long n) { unsigned long sq, i; if(n<=3||n==5||n==7) return n>1; if(n%2==0 || n%3==0 || n%5==0 || n%7==0) return 0; if(n<=210) return checkprime(n); /* use another simplified function */ sq=ceil(sqrt(n)); for(i=11; i<=sq; i+=210) { if(n%i==0 || n%(i+2)==0 || n%(i+6)==0 || n%(i+8)==0 || n%(i+12)==0 || n%(i+18)==0 || n%(i+20)==0 || n%(i+26)==0 || n%(i+30)==0 || n%(i+32)==0 || n%(i+36)==0 || n%(i+42)==0 || n%(i+48)==0 || n%(i+50)==0 || n%(i+56)==0 || n%(i+60)==0 || n%(i+62)==0 || n%(i+68)==0 || n%(i+72)==0 || n%(i+78)==0 || n%(i+86)==0 || n%(i+90)==0 || n%(i+92)==0 || n%(i+96)==0 || n%(i+98)==0 || n%(i+102)==0 || n%(i+110)==0 || n%(i+116)==0 || n%(i+120)==0 || n%(i+126)==0 || n%(i+128)==0 || n%(i+132)==0 || n%(i+138)==0 || n%(i+140)==0 || n%(i+146)==0 || n%(i+152)==0 || n%(i+156)==0 || n%(i+158)==0 || n%(i+162)==0 || n%(i+168)==0 || n%(i+170)==0 || n%(i+176)==0 || n%(i+180)==0 || n%(i+182)==0 || n%(i+186)==0 || n%(i+188)==0 || n%(i+198)==0) return 0; } return 1; }
Zeit:
$ time ./testprimebool.x 18446744065119617029 7 h(2,3,5,7) Yes, its prime. real 0m9.123s user 0m9.132s sys 0m0.000s
quelle
101
-199
Primals scheitern hier alle, weil101 % (11+90)
.n%(i+86)
oder überprüfenn > i+k
check235()
für die Primzahlen 7, 11, 13, 17, 19, 23 und 29 aufi+arr[k] >= n
if
with-Konstanten vom Compiler besser optimiert werden können. Ich habe bearbeitet, um eine Ausnahme hinzuzufügen und die aktuelle Struktur beizubehalten. Aber ich stimme zu, mit einem Array kann es für menschliche Augen besser sein.Ich möchte nur hinzufügen, dass keine gerade Zahl (Takt 2) eine Primzahl sein kann. Dies führt zu einer anderen Bedingung vor der for-Schleife. Der Endcode sollte also so aussehen:
int IsPrime(unsigned int number) { if (number <= 1) return 0; // zero and one are not prime if ((number > 2) && ((number % 2) == 0)) return 0; //no even number is prime number (bar 2) unsigned int i; for (i=2; i*i<=number; i++) { if (number % i == 0) return 0; } return 1; }
quelle
int is_prime(int val) { int div,square; if (val==2) return TRUE; /* 2 is prime */ if ((val&1)==0) return FALSE; /* any other even number is not */ div=3; square=9; /* 3*3 */ while (square<val) { if (val % div == 0) return FALSE; /* evenly divisible */ div+=2; square=div*div; } if (square==val) return FALSE; return TRUE; }
Die Behandlung von 2 und geraden Zahlen wird aus der Hauptschleife herausgehalten, die nur ungerade Zahlen geteilt durch ungerade Zahlen behandelt. Dies liegt daran, dass eine ungerade Zahl modulo eine gerade Zahl immer eine Antwort ungleich Null gibt, wodurch diese Tests überflüssig werden. Oder anders ausgedrückt, eine ungerade Zahl kann durch eine andere ungerade Zahl gleichmäßig teilbar sein, jedoch niemals durch eine gerade Zahl (E * E => E, E * O => E, O * E => E und O * O. => O).
Eine Division / ein Modul ist in der x86-Architektur sehr kostspielig, obwohl die Kosten unterschiedlich sind (siehe http://gmplib.org/~tege/x86-timing.pdf ). Multiplikationen sind dagegen recht günstig.
quelle
Überlauffehler vermeiden
unsigned i, number; ... for (i=2; i*i<=number; i++) { // Buggy for (i=2; i*i<=number; i += 2) { // Buggy // or for (i=5; i*i<=number; i+=6) { // Buggy
Diese Formen sind falsch, wenn
number
es sich um eine Primzahl handelt undi*i
nahe am Maximalwert des Typs liegt.Das Problem besteht bei allen Ganzzahltypen
signed, unsigned
und darüber hinaus.Beispiel:
Sei
UINT_MAX_SQRT
als Boden der Quadratwurzel der maximale ganzzahlige Wert. ZB 65535 beiunsigned
32-Bit.Bei
for (i=2; i*i<=number; i++)
tritt dieser 10 Jahre alte Fehler auf, weil die nächste Iteration zu einem Multiplikationsüberlauf führt, wennUINT_MAX_SQRT*UINT_MAX_SQRT <= number
undnumber
eine Primzahl ist. Wäre der Typ ein vorzeichenbehafteter Typ gewesen, wäre der Überlauf UB. Bei vorzeichenlosen Typen ist dies selbst kein UB, aber die Logik ist zusammengebrochen. Die Interaktionen werden fortgesetzt, bis ein abgeschnittenes Produkt überschritten wirdnumber
. Ein falsches Ergebnis kann auftreten.unsigned
Versuchen Sie mit 32-Bit 4.294.967.291, was eine Primzahl ist.Wenn
some_integer_type_MAX
ein Mersenne Prime gewesen ist ,i*i<=number
ist das nie wahr.Um diesen Fehler zu vermeiden, beachten , dass
number%i
,number/i
ist effizient auf viele Compiler wie die Berechnungen des Quotienten und den Rest zusammen getan, so dass keine zusätzlichen Kosten entstehen sowohl gegenüber zu tun , nur 1.Eine einfache Komplettlösung:
bool IsPrime(unsigned number) { for(unsigned i = 2; i <= number/i; i++){ if(number % i == 0){ return false; } } return number >= 2; }
quelle
Mit Sieve of Eratosthenes ist die Berechnung im Vergleich zum "bekannten" Primzahlalgorithmus wesentlich schneller.
Durch die Verwendung von Pseudocode aus dem Wiki ( https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes ) kann ich die Lösung auf C # finden.
public bool IsPrimeNumber(int val) { // Using Sieve of Eratosthenes. if (val < 2) { return false; } // Reserve place for val + 1 and set with true. var mark = new bool[val + 1]; for(var i = 2; i <= val; i++) { mark[i] = true; } // Iterate from 2 ... sqrt(val). for (var i = 2; i <= Math.Sqrt(val); i++) { if (mark[i]) { // Cross out every i-th number in the places after i (all the multiples of i). for (var j = (i * i); j <= val; j += i) { mark[j] = false; } } } return mark[val]; }
IsPrimeNumber (1000000000) benötigt 21s 758ms.
HINWEIS : Der Wert kann je nach Hardwarespezifikation variieren.
quelle