2016-04-25 4 views
-1

Ich arbeite am Projekt Euler # 10, in dem ich gebeten werde, die Summe aller Primzahlen unter 2.000.000 zu finden. Aus irgendeinem Grund kann ich meinen Code nicht zum Funktionieren bringen. Ich bin mir ziemlich sicher, dass ich nicht verstehe, welche Datentypen zu verwenden sind, aber weder int, long oder long long scheint zu funktionieren. Jede Hilfe oder Beratung wäre sehr willkommen. Hier ist mein Code. Vielen Dank.Projekt Euler # 10 Summe der Primzahlen

int main(int argc, char *argv[]) 
{ 
int primes[100] = {2, 3, 5, 7}; 
//array of primes that have been found 
int fillcell = 4; 
//cell we are looking to fill 
int testnum = 8; 
//number being tested to see if it's a prime 
int divprime; 
int sum = 17; 
for(testnum = 8, fillcell = 4 ; testnum < 2000000 ; ++fillcell) 
{ 
    std::cout << "fillcell " << fillcell << std::endl; 
    for(; primes[fillcell] == 0 ; ++testnum) 
    { 
     std::cout << "testnum " << testnum << std::endl; 
     for(divprime = 0 ; testnum % primes[divprime] != 0 ; ++divprime) 
     { 
      std::cout << "divprime " << divprime << std::endl; 
      if(primes[divprime] >= sqrt(testnum)) 
      { 
       primes[fillcell] = testnum; 
       sum = sum + testnum; 
       std::cout << "prime found " << testnum << std::endl; 
       break; 
      } 
     } 
    } 
} 
std::cout << "sum" << sum << std::endl; 
} 
+0

Können Sie hinzufügen, was Sie bekommen und was Sie bekommen möchten? – roadrunner66

Antwort

2

Versuchen Sie nicht zu laufen, bevor Sie die Kunst des Gehens gelernt haben.

Verwenden Sie keine Arrays mit fester Größe wie int primes[100], es sei denn, Sie müssen.

Ein Grund dafür ist, dass dies erfordert, dass Sie, der Programmierer, die erforderliche Größe bestimmen vorne - zum Beispiel durch in die nth Prime Page gehen und herauszufinden, dass es 148.933 Primzahlen unter 2000000

Es auch erfordert, der Programmierer, um zusätzliche Prüfungen zu Ihrem Code hinzuzufügen, um sicherzustellen, dass die Array-Zugriffe die Grenzen des Arrays nicht überschreiten (es sei denn, Sie verwenden eine Sprache, die dies für Sie tut, wie Java oder C#). Ein weiterer Grund besteht darin, dass Sie Code zur Buchführung hinzufügen müssen, d. H. Um zu verfolgen, wie viele Zellen des Arrays gegenwärtig belegt sind.

Zu guter Letzt führt das Zuweisen eines Arrays aus 148933 Ganzzahlen als eine automatische Variable (d. H. Auf dem Stapel) wahrscheinlich zu einem Absturz, weil es den Stapel bläst.

Wenn Sie std::vector<> verwenden, verschwinden alle diese Kopfschmerzen sofort und Ihr Code wird viel einfacher.

Beginnen Sie mit einem einfachen Plan und implementieren Sie die Schritte mit separaten Code. Es ist leichter, die Übersicht zu behalten, wenn jeder Code eine einfache, klar definierte Verantwortung hat. Schwieriger wird es, wenn man alles in einen großen, schlechten Klumpen verwickelt. Wenn Sie zum Beispiel die Primzahlen speichern, die Sie in einem Vektor gefunden haben, können Sie sich dort die Zahlen ansehen, um zu sehen, ob alles in Ordnung ist, und sie vielleicht mit bekannten Listen von Primzahlen vergleichen (wie The First 10,000 Primes oder die Primzahlen) bis zu 1.000.000.000.000 bei primos.mat.br). Du kannst schauen, aber du musst nicht. Wenn Sie alles mit Ausgabecode durchsetzen, dann müssen Sie immer alle ansehen. Wenn Sie sie nur zur Summe hinzufügen, können Sie sie nicht sehen, es sei denn, Sie debuggen Ihr Programm und folgen jedem einzelnen Schritt.

Formulieren Sie Ihren Plan als Pseudocode, damit Sie ihn auf einen Blick erfassen und vollständig verstehen können. Wenn Sie keinen Plan haben oder ihn nicht verstehen, ist das Ergebnis sehr wahrscheinlich cr * p.

for each candidate n between 2 and 2000000 
    for each known prime p up to sqrt(n) 
     if p divides n 
      break 
    if no divisor p was found // must be a new prime 
     add n to the list of found primes 

Offensichtlich ist das Kriterium ‚wenn kein Teiler p gefunden wurde‘ erfordert, dass Sie eine Flagge zu verwenden, wie divisor_found, die zu false vor der inneren Schleife initialisiert werden. Daher die erste Verfeinerung:

for each candidate n between 2 and 2000000 
    divisor_found := false 
    for each known prime p up to sqrt(n) 
     if p divides n 
      divisor_found := true 
      break 
    if not divisor_found // must be a new prime 
     add n to the list of found primes 

Dies kann ohne weiteres implementiert werden. Die Aufzählung der Kandidaten kann durch Überspringen einiger Zahlen verbessert werden, die möglicherweise nicht prim sein kann, wie ein Vielfaches von zwei:

add 2 to the list of found primes 
for each odd number between 3 and 2000000 
    ... 

Dies sofort schneidet Ihre Arbeit in der Hälfte, und es ist das einfachste Beispiel eines 'wheel'. Für Probleme wie dieses ist es sehr praktisch, auch ein Vielfaches von 3 zu überspringen (mod 6 Rad), indem man mit 5 beginnt und abwechselnd um 2 und 4 erhöht.

add 2 and 3 to the list of found primes 
for n = 5 to 2000000 step 6 // 6 = 2 * 3 
    try n 
    try n + 2 

Hier wird die Probedivision in try getan braucht nicht 2 oder 3 als potentielle Teilern zu betrachten, weil Ihre Methode der Aufzählen Kandidaten bereits alle Multiples ausschließt. Die Erweiterung auf das Auslassen von Vielfachen von 5 ist ebenfalls ziemlich einfach.

Wenn Sie während jeder Iteration der innersten Schleife eine teure Berechnung wie sqrt(n) durchführen, wird Ihr Code auf Crawl verlangsamt. n ändert sich nicht während der Lebensdauer der inneren Schleife, also berechnen Sie den Wert einmal im Schleifenkopf, anstatt unnötigerweise die Berechnung zu wiederholen.

Wie dem auch sei, das zufällige Ausprobieren verschiedener ganzzahliger Datentypen wird Sie nirgendwo hinbringen. Wenn Werte nicht negativ werden können - wie hier - dann sollte unsigned Ihre erste Wahl sein. Auf aktuellen Systemen entspricht dies normalerweise uint32_t, was mehr als genug ist für die kleinen Zahlen, die an dieser Euler-Aufgabe beteiligt sind. Sie können sich ein wenig Ärger ersparen, indem Sie einen geeigneten Typedef einführen; entstehen müssen auf diese Weise müssen Sie nur eine einzige Definition ändern sollte:

typedef std::uint32_t num_t; 
typedef std::uint64_t sum_t; 
num_t const N = 2000000; 
... 

std::vector<num_t> primes; 
... 

for (num_t n = 3; n <= N; n += 2) 
    ... 

sum_t sum = 0; 
for (num_t p: primes) 
    sum += p; 

Ich habe eine separate sum_t auch hinzugefügt, da eine grobe Schätzung der Summe es weit über die Kapazität eines uint32_t setzt.

In jedem Fall sollten Sie ernsthaft die Sieve of Eratosthenes hier verwenden. Es ist einfacher als eine fahrbare Versuchsaufteilung und um mehrere Größenordnungen schneller - selbst das einfachste Rendering sollte diese Euler-Aufgabe in wenigen Millisekunden lösen.

+1

7 Stunden dieses Q war ruhend. Ich gehe hinein und sehe deine Antwort von "vor 37 Sekunden". Hä? Kann die Wissenschaft das erklären? :) –

+1

Vielen Dank! Das ist unglaublich hilfreich! Und das Beste von allem, ich habe es gelöst: D – Sonarbuddy

+0

@Son: Sie sind herzlich willkommen, und herzlichen Glückwunsch zu einem anderen Euler! Ich hoffe, dass wir - Will und ich - dazu beitragen können, dass diese Euler-Aufgabe für Sie angenehmer wird. Und ich empfehle, die Antwort von Will genauer zu betrachten: Sie zeigt nicht nur gut, wie das Sieb von Eratosthenes funktioniert, sondern auch die Prägnanz und Kompaktheit der Formulierung von Algorithmen in Sprachen wie Python (wegen des Fehlens des Rauschens, das die Sprachen plagt) wie C und C++, wo viel uninteressantes Zeug ausführlich beschrieben werden muss. Wenn Sie es mögen, sagen Sie uns und anderen, indem Sie es abstimmen ... – DarthGizka

1

DarthGizka gab Ihnen einige gute Ratschläge, einschließlich der Änderung Ihres Algorithmus zur Verwendung des Siebs von Eratosthenes. Hier ist meine Lösung, die ich Ihnen überlassen wird in der gewählten Sprache neu zu schreiben:

function sumPrimes(n) # sum of primes <= n 
    sum := 0 
    sieve := makeArray(2..n, True) 
    for p from 2 to n step 1 
     if sieve[p] 
      sum := sum + p 
      for i from p * p to n step p 
       sieve[i] := False 
    return sum 

Wenn n zu groß ist, um die sieve Array zu bilden, werden Sie zu segmentieren benötigen das Array. Siehe here, wenn Sie das tun müssen. Es gibt auch einen Algorithmus, eine Variante der Legendre-Methode zum Zählen von Primzahlen, die die Summe der Primzahlen berechnet, aber es ist sehr kompliziert.