C - Bestimmen Sie, ob eine Zahl eine Primzahl ist


75

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;
}

1
Dies ist sicherlich eher eine mathematische Frage als eine Programmierfrage.
Bdonlan

49
Hier sind einige Hinweise: int * ptr; int * ptr2; int * ptr3. Entschuldigung, konnte nicht anders. Wie groß sind die Zahlen, die Sie überprüfen werden? Und möchten Sie auch eine Heuristik oder etwas, das immer funktioniert?
AlbertoPL


12
Was bringt 'i! = Number', wenn Sie 'i <number' als Bedingung für die Ausführung der Schleife haben?
Matthieu M.

3
Beachten Sie auch, dass die Überprüfung i < numberübertrieben ist. Per Definition, wenn eine Zahl x = a * bentweder aoder bist < int(sqrt(x))und die andere größer ist. Ihre Schleife sollte also nur bis zu gehen müssen int(sqrt(x)).
Twalberg

Antworten:


153

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 boolTyp in C und nein trueoder false, 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 iimmer weniger als ist number, sodass die Prüfung i != numberimmer 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. Da sqrtes sich um eine Gleitkommaoperation handelt, die eine ganze Reihe von Feinheiten mit sich bringt, werden wir nicht wirklich berechnen sqrt(number). Stattdessen können wir das einfach überprüfen i*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 numbernegativ 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 nicht numbersigniert 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!


11
Zu Ihrer Information, die C99 - Standard definiert ein <stdbool.h> Header, liefert bool, trueund false.
Kristopher Johnson

27
Ich weiß, dass es einfacher ist, ein Quadrat als eine Quadratwurzel zu berechnen, aber das Berechnen eines Quadrats bei jeder Iteration sollte MEHR kosten als das einmalige Berechnen der Quadratwurzel und damit fertig sein: x
Matthieu M.

6
Auf einer modernen Maschine außerhalb der Reihenfolge sollte die Latenz des Mul-Befehls zum Quadrat i vollständig in der Latenz des Moduls verborgen sein, damit es keinen nennenswerten Leistungsgewinn gibt. Auf einer Maschine, die streng in Ordnung ist, kann mit einer hochgezogenen Quadratwurzel ein Gewinn erzielt werden. Dies führt jedoch möglicherweise zu Problemen mit der Gleitkomma-Ungenauigkeit, wenn der Code auf einer Plattform mit einem großen int-Typ (64 Bit oder mehr) kompiliert wurde. . All das kann erledigt werden, aber ich fand es am besten, die Dinge einfach und trivial tragbar zu halten. Wenn Sie Wert auf Geschwindigkeit legen, verwenden Sie diesen Algorithmus schließlich überhaupt nicht.
Stephen Canon

5
@ Tom Sie können viel mehr verbessern, indem Sie am Boden anhalten (sqrt (Nummer)). Nehmen Sie zum Beispiel 11, Boden (sqrt (11)) = 3. Die Zahl nach 3 ist 4, 3 * 4 = 12> 11. Wenn Sie ein naives Sieb verwenden, um die Primalität zu überprüfen, müssen Sie nur ungerade überprüfen Zahlen bis zum
Quadrat

3
-1. Die letzte Funktion gibt die falsche Antwort für 4294967291 .
Davidg

27

Ich bin überrascht, dass niemand dies erwähnt hat.

Verwenden Sie das Sieb von Eratosthenes

Einzelheiten:

  1. Grundsätzlich sind Nicht-Primzahlen durch eine andere Zahl als 1 und sich selbst teilbar
  2. Deshalb: Eine Nicht-Primzahl ist ein Produkt von Primzahlen.

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:

  1. Dieser Algorithmus / dieses Problem ist als " peinlich parallel " bekannt.
  2. Es wird eine Sammlung von Primzahlen erstellt
  3. Es ist ein Beispiel für ein dynamisches Programmierproblem
  4. Es ist schnell!

8
Es ist auch O(n)im Weltraum und solange Ihre Berechnung für einen einzelnen Wert ist, ist dies eine enorme Platzverschwendung ohne Leistungsgewinn.
R .. GitHub STOP HELPING ICE

3
(Eigentlich O(n log n)oder größer, wenn Sie große Zahlen unterstützen ...)
R .. GitHub STOP HELPING ICE

2
Wer berechnet nur 1 Wert für eine Primzahl für die Lebensdauer der Anwendung? Primzahlen sind ein guter Kandidat, um zwischengespeichert zu werden.
Mönch

2
Ein Befehlszeilenprogramm, das nach einer Abfrage beendet wird, wäre ein naheliegendes Beispiel. In jedem Fall ist es hässlich, den globalen Zustand aufrechtzuerhalten, und sollte immer als Kompromiss betrachtet werden. Und ich würde sogar sagen, dass das zur Laufzeit erzeugte Sieb im Wesentlichen nutzlos ist. Wenn Ihre Hauptkandidaten klein genug sind, um ein Sieb dieser Größe in den Speicher zu integrieren, sollten Sie nur eine static constBitmap mit den Primzahlen haben und diese verwenden, anstatt sie zur Laufzeit zu füllen.
R .. GitHub STOP HELPING ICE

1
Das Sieb von Eratosthenes ist ein guter (gut, gut) Weg, um das Problem zu lösen, "alle Primzahlen bis zu n zu erzeugen ". Es ist eine verschwenderische Art, das Problem zu lösen "is n prime?"
Hobbs

16

Stephen Canon hat es sehr gut beantwortet!

Aber

  • Der Algorithmus kann weiter verbessert werden, indem beobachtet wird, dass alle Primzahlen die Form 6k ± 1 haben, mit Ausnahme von 2 und 3.
  • Dies liegt daran, dass alle ganzen Zahlen für eine ganze Zahl k als (6k + i) ausgedrückt werden können und für i = –1, 0, 1, 2, 3 oder 4; 2 Teilungen (6k + 0), (6k + 2), (6k + 4); und 3 Teilungen (6k + 3).
  • Eine effizientere Methode besteht also darin, zu testen, ob n durch 2 oder 3 teilbar ist, und dann alle Zahlen der Form 6k ± 1 ≤ √n zu überprüfen.
  • 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; 
        }
    }
    

2
Sie sollten zurückgeben, 0wenn (Nummer == 1), da 1 keine Primzahl ist.
Ahmad Ibrahim

Diese Art von Optimierungen sind für diese Aufgabe IMO irrelevant: Warum bei der Form 6k ± 1 mit Ausnahme von 2 und 3 anhalten, die sich in n ^ 2 mod 6 = 1 dreht , wenn Sie n ^ 4 mod 30 = 1 außer 2,3 haben können , 5 ... in der Tat können Sie für immer gehen, weil Sie Primzahlen verwenden, um diese Optimierung
durchzuführen

1
@ GhilesZ: Ich bin anderer Meinung, dies ist sehr relevant für das Problem und mit einem einzigen "||" ermöglicht es der Basisschleife, effektiv dreimal schneller zu laufen.
verdy_p

Zusätzlich gibt es für Nummer == 1 korrekt 0 (Nicht-Primzahl) mit dem getesteten Geständnis "(Nummer% 2 == 0)" zurück, si es gibt überhaupt keinen Fehler
verdy_p

Das Eratosthen-Methoid ist eine völlig andere Methode, bei der ein großes O (n) -Array von Booleschen Werten zugewiesen werden muss. Aufgrund indizierter Zugriffe ist es nicht unbedingt schneller. Dieser Code ist in Ordnung, da er zuerst den Fall der beiden ersten Primzahlen 2 und 3 optimiert (deshalb wird die Schleife um 2 * 3 schrittweise ausgeführt).
verdy_p

10
  1. Erstellen Sie eine Tabelle mit kleinen Primzahlen und prüfen Sie, ob diese Ihre Eingabenummer teilen.
  2. Wenn die Zahl bis 1 überlebt hat, versuchen Sie Pseudo-Primalitätstests mit zunehmender Basis. Siehe zum Beispiel den Miller-Rabin-Primalitätstest .
  3. Wenn Ihre Zahl bis 2 überlebt hat, können Sie daraus schließen, dass es sich um eine Primzahl handelt, wenn sie unter einigen bekannten Grenzen liegt. Andernfalls lautet Ihre Antwort nur "wahrscheinlich prim". Einige Werte für diese Grenzen finden Sie auf der Wiki-Seite.

3
+1: Kompletter Overkill für das, was der Fragesteller gefragt hat, aber trotzdem richtig.
Stephen Canon

Beachten Sie, dass Guy L. kürzlich vorgeschlagen hat, Miller-Rabin auch in einer Antwort zu verwenden, und auf rosettacode.org/wiki/Miller-Rabin_primality_test#C verlinkt ist - dies zeigt eine Implementierung in C unter Verwendung von GMP . Der Eintrag enthält auch eine Reihe von Implementierungen in einer Vielzahl anderer Sprachen.
Jonathan Leffler

4

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;
}

1
Um eine Primzahl zu testen, sollten Sie den ganzen Weg von i=2bis gehen i<=ceil(sqrt(n)). Sie haben in Ihrem Test zwei Zahlen verpasst: Zuerst wird gegossen, um (int)den sqrt(n)Rumpf als Dezimalzahl zu definieren. Zweitens haben Sie verwendet i<sq, wann es sein sollte i<=sq. Nehmen wir nun eine Zahl an, die zu diesem Problem passt. Eine zusammengesetzte Zahl n, die ceil(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 weiter n%iund n%(i+2). Angenommen, wir bekommen sqrt(1763)=41.98. Als 1763=41*43eine zusammengesetzte Zahl. Ihre Schleife läuft nur bis (35, 37)und schlägt fehl.
DrBeco

@ DrBeco schöne Beobachtung! Danke zum Beispiel. hat den Code aktualisiert.
GorvGoyl

2
Nachdem 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 testen i<=sqrt(n)und es wird in Ordnung sein. Die Testfälle sind große Tween-Primzahlen. Beispiel: 86028221*86028223=7400854980481283und sqrt(7400854980481283)~86028222. Und das kleinere Wissen zwischen Primzahlen 2und 3gibt, sqrt(6)=2.449dass Stamm noch verlassen wird 2. (Aber kleiner ist kein Testfall, nur ein Vergleich, um einen Punkt zu machen). Ja, der Algorithmus ist jetzt korrekt. Keine Notwendigkeit zu verwenden ceil().
DrBeco

3

Ü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;
}

2
Der Nachteil ist natürlich, dass das Quadrat bei jeder Iteration berechnet wird, was es sehr verlangsamt.
Rich Bradshaw

9
Jeder vernünftige Compiler sollte in der Lage sein, zu erkennen, dass root (target) eine Schleifeninvariante ist, und diese hochziehen.
Stephen Canon

1
(und wenn Sie einen Compiler haben, der diese Optimierung nicht durchführen kann, sollten Sie unbedingt einen Fehler melden, damit der Compiler-Autor weiß, dass diese Optimierung fehlt.)
Stephen Canon

zusammen mit vielen anderen möglichen (Mikro-) Optimierungen. Wenn Sie das sqrt manuell vor der for-Anweisung erhalten, können Sie auch den Mod davon überprüfen (und false zurückgeben, wenn 0).
Matt Lacey

2
Was ist, wenn der Zielwert 1 ist?
ffffff01

3

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:

2 3 5 7 11 13 17 19

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 ORskönnen , oder Ihr Code wäre sehr groß (es würde 1658879 geben , das heißt ||in der jeweiligen internen if). Die nächste Liste:

2 3 5 7 11 13 17 19 23

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

Bonus: 101- 199Primals scheitern hier alle, weil 101 % (11+90).
vp_arth

1
müssen anhalten n%(i+86)oder überprüfenn > i+k
vp_arth

Gut gemacht, Sir. Ich werde einen Blick darauf werfen. Vielen Dank. Das gleiche Problem tritt mit der Funktion check235()für die Primzahlen 7, 11, 13, 17, 19, 23 und 29 auf
DrBeco

Lösung: Sie müssen diese Erinnerungen in ein Array verschieben, durchlaufen und die Iteration i+arr[k] >= n
unterbrechen,

Ich habe darüber nachgedacht, aber ich möchte kein Array, da die ifwith-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.
DrBeco

2

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;
}

1
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.


1

Ü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 numberes sich um eine Primzahl handelt und i*inahe am Maximalwert des Typs liegt.

Das Problem besteht bei allen Ganzzahltypen signed, unsignedund darüber hinaus.

Beispiel:

Sei UINT_MAX_SQRTals Boden der Quadratwurzel der maximale ganzzahlige Wert. ZB 65535 bei unsigned32-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, wenn UINT_MAX_SQRT*UINT_MAX_SQRT <= numberund numbereine 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 wird number. Ein falsches Ergebnis kann auftreten. unsignedVersuchen Sie mit 32-Bit 4.294.967.291, was eine Primzahl ist.

Wenn some_integer_type_MAXein Mersenne Prime gewesen ist , i*i<=numberist das nie wahr.


Um diesen Fehler zu vermeiden, beachten , dass number%i, number/iist 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;
}

0

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.