Goldbach Vermutung
-
Kein Plan, ob es schon gute Lügnerlisten gibt. (Wenn du suchst, besser nach Pseudoprimzahlen suchen ;))
Aber du kannst dir welche erstellen auch für den sehr hohen Bereich. Wenn du MillerRabin, mit 2,3,5,7 machst, sind die Lügner auch bis in den riesigen Bereich überschaubar. Die Liste kannst aber wahrscheinlich nur mit dem AKS-Test erstellen. Division ist wahrscheinlich zu langsam.
-
Erster Test (Notebook):
`vor Lookup: nix
vor M.R.: 3 ... 43
1000000000000000000000010 3 t: 47 ms
1000000000000000000000012 5 t: 109 ms
1000000000000000000000014 7 t: 109 ms
1000000000000000000000016 487 t: 1014 ms
1000000000000000000000018 11 t: 47 ms
1000000000000000000000020 13 t: 109 ms
vor Lookup: nix
vor M.R.: 3 ... 17
1000000000000000000000010 3 t: 47 ms
1000000000000000000000012 5 t: 109 ms
1000000000000000000000014 7 t: 109 ms
1000000000000000000000016 487 t: 1389 ms <===
1000000000000000000000018 11 t: 47 ms
1000000000000000000000020 13 t: 109 ms
vor Lookup: 3 ... 17
vor M.R.: 3 ... 43
1000000000000000000000010 313 t: 998 ms
1000000000000000000000012 269 t: 624 ms
1000000000000000000000014 271 t: 1186 ms
1000000000000000000000016 487 t: 920 ms
1000000000000000000000018 401 t: 749 ms
1000000000000000000000020 277 t: 1919 ms
---------------------------------------------`
Die aktuelle Vorgehensweise erscheint bez. Geschwindigkeit nahe am Optimum.
Vielleicht sollte man eher noch mehr Primzahlen dazu nehmen?
Probieren wir 3 ... 101, dann steigt die Zeit bei keinem Divisorfund (z.B. hier bei n-3) an, überwiegend hilft es aber:`
vor Lookup: nix
vor M.R.: 3 ... 101
1000000000000000000000010 3 t: 62 ms
1000000000000000000000012 5 t: 47 ms
1000000000000000000000014 7 t: 62 ms
1000000000000000000000016 487 t: 952 ms
1000000000000000000000018 11 t: 62 ms
1000000000000000000000020 13 t: 47 ms
`
Daher lasse ich es bei dieser Vorgehensweise:
Vor Lookup nichts, vor Miller-Rabin 3 ... 101.
-
Wo ist der Sinn, wenn du nach dem Lookup nochmal das gleiche testest, wie davor schon? Und warum baust du es nicht direkt da ein, wo der % 2 Check ist?
-
@TGGC: Ich werde das nochmal sauber messen. Das Optimum ist sicher auch nicht bei 101, sondern hängt von dem zu überprüfenden Zahlenbereich ab, aber der aktuelle Source ist IMHO recht nahe am Optimum.
Zunächst kommt noch die Miller-Rabin Liars List (2047 ... 18446744066047760377) mit 663.879.564 Bytes ins Spiel. Damit testen wir bei Rückgabe von true aus unserem Milli-Miller-Rabin-Test. Das werde ich beim Start oberhalb der letzten Zahl in der Datei (18446744066047760377) noch abschalten, da das Laden noch recht lange dauert.
@Volkard: Gibt es Liars oberhalb 2^64 zum Download?
Hier zunächst mein aktueller Sourcecode, damit wir harmonisiert bleiben:
#include <iostream> #include <iomanip> #include <cstdint> #include <cstdlib> #include <algorithm> #include <cmath> #include <ctime> #include <vector> #include <unordered_set> #include <fstream> #include "BigIntegerLibrary.hh" // https://mattmccutchen.net/bigint/ using namespace std; void wait() { cout << "Press any key to continue." << endl; cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); cin.get(); } unordered_set<uint64_t> setLiar; uint64_t lastLiar = 0; uint64_t convertBigToU64(BigUnsigned num) { uint64_t jHi = num.getBlock(1); uint64_t jLo = num.getBlock(0); return (jHi << 32 | jLo); } void convertU64ToBig(uint64_t num, BigUnsigned& b) { uint32_t numLo = (uint32_t)(num & 0xFFFFFFFF); uint32_t numHi = (uint32_t)((num >> 32) & 0xFFFFFFFF); b.setBlock(0, numLo); b.setBlock(1, numHi); } //calculates (a * b) % c BigUnsigned mulmod(BigUnsigned a, BigUnsigned b, BigUnsigned mod) { BigUnsigned x = 0; BigUnsigned y = a % mod; while (b > 0) { if (b % 2 == 1) { x = (x + y) % mod; } y = (y * 2) % mod; b /= 2; } return x % mod; } BigUnsigned powmod_Volkard(BigUnsigned base, BigUnsigned exp, BigUnsigned modul) { //rechnet 'base hoch exp mod modul' BigUnsigned a1 = base, z1 = exp, x = 1, m = modul; while (z1 != 0) { while ((z1 % 2) == 0) { z1 /= 2; a1 = mulmod(a1, a1, m); } --z1; x = mulmod(x, a1, m); } return x; } bool IsProbablyPrime_MillerRabinOptimized(BigUnsigned n) { /* example: step: "calculate s and d" n = 1001 n-1 = 1000 s = 3 d = 125 1000 / 2^3 = 125 step: "test with base a" ad = a^d mod n (cf. function powmod) ... */ if (n == 2) return true; // "s" is the logarithm to base base 2 of the biggest number 2^s dividing n-1 // d = (n-1)/(2^s) // calculate d and s BigUnsigned d = n - 1; BigUnsigned s = 0; while ((d & 1) == 0) { ++s; d >>= 1; } // We do not use random base a = {2 ... n-1}, but fix it to a = 2 BigUnsigned a = 2; // base ("witness") BigUnsigned ad = (powmod_Volkard(a%n, d, n)); // (a^d) mod n if (ad == 1) return true; // a^d mod n == 1 if ((s > 0) && (ad == (n - 1))) return true; // a^d mod n == -1 (tests (a^d)^(2^0)) // test for r = {0 ... s-1} for (BigUnsigned r = 1; r < s; ++r) { // ad is currently ad^(2^(r-1)), thus we square it to get ad^(2^r)) ad = mulmod(ad, ad, n); if (ad == (n - 1)) return true; // tests (a^d)^(2^(r+1))) } return 0; // false, composite } bool IsPrimeDivisionTest(BigUnsigned number) { if (number<2) return false; if (number == 2) return true; if (number % 2 == 0) return false; for (BigUnsigned i = 3; i*i <= number; i += 2) { if (number%i == 0) return false; } return true; } bool IsPrime(vector<bool>& primes, BigUnsigned number, BigUnsigned primeLimit, unordered_set<uint64_t>& setLiar) { // lookup from bitset (in the RAM) if (number <= primeLimit) return primes[number.toUnsignedLong()]; // division test with first prime numbers if ( /*(number % 2 == 0) || */ (number % 3 == 0) || (number % 5 == 0) || (number % 7 == 0) || (number % 11 == 0) || (number % 13 == 0) || (number % 17 == 0) || (number % 19 == 0) || (number % 23 == 0) || (number % 29 == 0) || (number % 31 == 0) || (number % 37 == 0) || (number % 41 == 0) || (number % 43 == 0) || (number % 47 == 0) || (number % 53 == 0) || (number % 59 == 0) || (number % 61 == 0) || (number % 67 == 0) || (number % 71 == 0) || (number % 73 == 0) || (number % 79 == 0) || (number % 83 == 0) || (number % 89 == 0) || (number % 97 == 0) || (number % 101 == 0) ) { return false; } // Miller-Rabin, not perfect, but fast if (!IsProbablyPrime_MillerRabinOptimized(number)) { return false; // } else { BigUnsigned bigLastLiar; BigUnsigned& refBigLastLiar = bigLastLiar; convertU64ToBig(lastLiar, refBigLastLiar); if (number > bigLastLiar) { return true; } else { if (setLiar.find(convertBigToU64(number)) != setLiar.cend()) { // cout << "ALARM: Miller-Rabin-Liar found: " << number << " "; // wait(); return false; // function tells the truth by failure list (0 ... 2^64), above lastLiar there will be "Prime Hell" again. } return true; } } } bool PrimePairFound(vector<bool>& primes, BigUnsigned i, const BigUnsigned primeLimit, unordered_set<uint64_t>& setLiar) { bool retVal = false; BigUnsigned grenze = i / 2; BigUnsigned a = 0; for (a = 3; a <= grenze; a += 2) { if (IsPrime(primes, a, primeLimit, setLiar) && IsPrime(primes, i - a, primeLimit, setLiar)) { retVal = true; break; } } cout << i << " " << setw(5) << a << " "; return retVal; } int main() { BigUnsigned startNumber = stringToBigUnsigned("1000000000000000000000000"); BigUnsigned endNumber = stringToBigUnsigned("1000000000000000000000050"); uint64_t const primeLimit = 2000000; //200000000000; cout << "We generate vector<bool>(primes) up to: " << primeLimit << endl; // (this amount depends on the capability of your RAM) vector<bool> primes(primeLimit); // processed numbers; time_t startTime, endTime; // measuring execution time uint64_t global_count = 0; // total number of primes time(&startTime); // initialize the number array primes[0] = false; primes[1] = false; for (uint64_t i = 2; i<primeLimit + 1; ++i) { primes[i] = true; } // sieving loop uint64_t iMax = (uint64_t)sqrt((double)primeLimit) + 1; for (uint64_t i = 2; i<iMax; ++i) { if (primes[i]) { for (uint64_t j = 2 * i; j<primeLimit + 1; j += i) { primes[j] = false; } } } time(&endTime); // count the number of primes for (uint64_t i = 0; i<primeLimit + 1; ++i) { if (primes[i]) global_count++; } cout << "In " << difftime(endTime, startTime) << " s we found " << global_count << " prime numbers (2 to " << primeLimit << " )." << endl << endl; // Prepare Miller-Rabin "Liar-Liste" and detect Goldbach prime pairs with Miller-Rabin test ifstream fileLiar("F:\\Daten\\Miller_Rabin_Liar\\2-SPRP-2-to-64.txt", fstream::in); // https://miller-rabin.appspot.com/ cout << "Miller-Rabin Liar file starts to be read from file." << endl; uint64_t count=0; uint64_t u64LiarNumber=0; while (!fileLiar.eof()) { fileLiar >> u64LiarNumber; // cout << u64LiarNumber << "\t"; setLiar.insert(u64LiarNumber); count++; } fileLiar.close(); lastLiar = u64LiarNumber; cout << "Miller-Rabin Liar file with " << count << " liars is read." << endl << "Last liar: " << u64LiarNumber << endl << endl; unordered_set<uint64_t>& refSetLiar = setLiar; cout << "Now Goldbach first prime number pair will be detected:" << endl; clock_t zeit1, zeit2, zeit2_old; zeit2 = zeit1 = clock(); for (BigUnsigned i = startNumber; i <= endNumber; i += 2) { if (!PrimePairFound(primes, i, BigUnsigned((unsigned long)primeLimit), refSetLiar)) { cout << "Counterevidence found for this number: " << i << endl; wait(); } zeit2_old = zeit2; zeit2 = clock(); cout << "time: " << 1000 * (zeit2 - zeit2_old) / CLOCKS_PER_SEC << " ms" << endl; } wait(); }
IMHO wäre nun MT oder verstärkte Nutzung des RAM dran.
`We generate vector<bool>(primes) up to: 2000000
In 0 s we found 148933 prime numbers (2 to 2000000 ).
Miller-Rabin Liar file starts to be read from file.
Miller-Rabin Liar file with 31894015 liars is read.
Last liar: 18446744066047760377
Now Goldbach first prime number pair will be detected:
1000000000000000000000000 257 time: 448 ms
1000000000000000000000002 349 time: 758 ms
1000000000000000000000004 307 time: 409 ms
1000000000000000000000006 263 time: 374 ms
1000000000000000000000008 311 time: 643 ms
1000000000000000000000010 3 time: 32 ms
1000000000000000000000012 5 time: 31 ms
1000000000000000000000014 7 time: 32 ms
1000000000000000000000016 487 time: 480 ms
1000000000000000000000018 11 time: 32 ms
1000000000000000000000020 13 time: 31 ms
1000000000000000000000022 613 time: 643 ms
1000000000000000000000024 17 time: 61 ms
1000000000000000000000026 19 time: 62 ms
1000000000000000000000028 331 time: 343 ms
1000000000000000000000030 23 time: 61 ms
1000000000000000000000032 379 time: 852 ms
1000000000000000000000034 337 time: 477 ms
1000000000000000000000036 29 time: 92 ms
1000000000000000000000038 31 time: 91 ms
1000000000000000000000040 631 time: 809 ms
1000000000000000000000042 389 time: 505 ms
1000000000000000000000044 37 time: 122 ms
1000000000000000000000046 349 time: 403 ms
1000000000000000000000048 41 time: 93 ms
1000000000000000000000050 43 time: 153 ms`
Im Bereich 10^24 geht es also recht flott ab. Hier mein Benchmark bei 10^120:
`
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 173 time: 30615 ms
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 709 time: 126508 ms
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 613 time: 71922 ms
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 179 time: 34387 ms
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008 181 time: 37669 ms
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010 619 time: 92918 ms`
-
@Volkard, Bengo, Arcoth: Habe ich alle wichtigen Ideen zur Beschleunigung inkorporiert oder habe ich noch etwas Wichtiges übersehen? Was könnte man sinnvoll parallel laufen lassen? Zehn Zahlen gleichzeitig testen?
Die Klasse BigInteger bietet auch einen Algo für modexp:
BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent, const BigUnsigned &modulus) { BigUnsigned ans = 1, base2 = (base % modulus).getMagnitude(); BigUnsigned::Index i = exponent.bitLength(); // For each bit of the exponent, most to least significant... while (i > 0) { i--; // Square. ans *= ans; ans %= modulus; // And multiply if the bit is a 1. if (exponent.getBit(i)) { ans *= base2; ans %= modulus; } } return ans; }
-
-
Wie gesagt, wuerde ich es nochmal mit einer loopenden Tabelle pobieren, die in den Cache passt um die Teilbarkeit durch die ersten Primzahlen zu pruefen. Das wuerde dann mehrere Divisionen durch eine Division und ein Lookup ersetzen. Gleichzeitig kann man das dann eben auch nutzen um den primes vector viel kleiner zu machen.
-
Ab dem Bereich 10^240 sind wir im Bereich ca. 1h/Geradzahl angelangt:
10^240, erste Primzahl 2351, t: 2215 s
10^241, erste Primzahl 2069, t: 3929 sHier findet man eine erste zum Paar passende Primzahl erst über 1000.
-
Das sind etwa 800bit, scheint mir so, als ob man das noch sehr optimieren kann, da mein Computer auch 4096 bit Primzahlen generieren kann.(In weniger als einer Stunde) bei SSL
-
Bengo schrieb:
Das sind etwa 800bit, scheint mir so, als ob man das noch sehr optimieren kann, da mein Computer auch 4096 bit Primzahlen generieren kann.(In weniger als einer Stunde) bei SSL
Welchen Zweck hätte eine 4096-Bit-Primzahl bei SSL? Sicher, daß Du da 4096-Bit-Primzahlen berechnen läßt?
Naja, SEHR optimieren nenne ich das nicht. Klar handoptimiertes asm an manche Stellen, MMX/SSE, Multitreading, damit kann man noch was holen. Mit der riesigen Gefahr, dass man sich dabei verzettelt und einen läppischen Faktor 10 kriegt und dafür Einbußen an der Einfachheit und Wartbarkeit hat und deswegen am Algo spart und billiardenfach lahmer ist.
-
Bitte nicht übersehen, dass wir bei 10^241 mit der ersten Primzahl 2069 in den 3929 Sekunden bei allen Primzahlen von 3 bis 2069 alle entsprechenden Partner "Geradzahl minus erste Primzahl" auf ihre Primzahlfähigkeit prüfen. Im Lookup findet da schon lange nichts mehr, man ist somit auf den kleinen Divisionstest (z.Z. 3 ... 101) angewiesen und auf den Milli-Miller-Rabin. Den Lügnertest kann man natürlich gar nicht erst laden und abschalten über 2^64.
Ich werde im nächsten Schritt das C++ 11 std::thread einbauen und verschiedene parallele Strategien testen.
Beispiele:
- mehrere Geradzahlen parallel auf Goldbach-Vermutung prüfen
- mehrere erste Primzahlen parallel auf ihren möglichen Primzahl-Partner prüfen (hier findet man aber nicht mehr die erste kleine Primzahl, sondern irgendein Paar)
-
volkard schrieb:
Bengo schrieb:
Das sind etwa 800bit, scheint mir so, als ob man das noch sehr optimieren kann, da mein Computer auch 4096 bit Primzahlen generieren kann.(In weniger als einer Stunde) bei SSL
Welchen Zweck hätte eine 4096-Bit-Primzahl bei SSL? Sicher, daß Du da 4096-Bit-Primzahlen berechnen läßt?
Naja, SEHR optimieren nenne ich das nicht. Klar handoptimiertes asm an manche Stellen, MMX/SSE, Multitreading, damit kann man noch was holen. Mit der riesigen Gefahr, dass man sich dabei verzettelt und einen läppischen Faktor 10 kriegt und dafür Einbußen an der Einfachheit und Wartbarkeit hat und deswegen am Algo spart und billiardenfach lahmer ist.
Stimmt hast recht, das RSA Modul hat 4096bit, die Primzahlen dann aber immernoch mehr als 2048bit.
-
Die Thread-Unterstützung habe ich mal eingebaut (noch nicht ideal) und im Bereich 10^120 bis 10^120 + 20 getestet (11 Geradzahlen, 11 Threads). Das läuft gut, man er hält die Ergebnisse in der gleichen Zeit wie vorher die langsamste Zahl in diesem Bereich. 11 Threads sind bei mir mit 12 virtuellen Kernen optimal. Hier das Programm, falls es jemand ausprobieren/optimieren will:
#include "stdafx.h" // MS VC++ #define NOMINMAX // due to conflict with max() #include <windows.h> // warning: no C++ standard! #include <iostream> #include <iomanip> #include <cstdint> #include <cstdlib> #include <cassert> #include <algorithm> #include <cmath> #include <ctime> #include <vector> #include <unordered_set> #include <fstream> #include <thread> #include <mutex> #include "BigIntegerLibrary.hh" // https://mattmccutchen.net/bigint/ //#define MILLER_RABIN_LIAR_CHECK using namespace std; void setConsole() // windows.h { _CONSOLE_SCREEN_BUFFER_INFO info; HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(handle, &info); COORD c; c.X = std::max<SHORT>(info.dwSize.X, 150); c.Y = std::max<SHORT>(info.dwSize.Y, 1000); SetConsoleScreenBufferSize(handle, c); SMALL_RECT RECT; RECT.Left = 0; RECT.Top = 0; RECT.Bottom = std::max(info.srWindow.Bottom - info.srWindow.Top, 40 - 1); RECT.Right = std::max(c.X - 1, 100 - 1); SetConsoleWindowInfo(handle, TRUE, &RECT); } void textcolor(unsigned short color = 15) // windows.h { SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), color); } void wait() { cout << "Press any key to continue." << endl; cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); cin.get(); } mutex mutex_; // for cout unordered_set<uint64_t> setLiar; uint64_t lastLiar = 0; uint64_t convertBigToU64(BigUnsigned num) { BigUnsigned ZweiHochVierUndSechzig = stringToBigUnsigned("18446744073709551616"); assert(num < ZweiHochVierUndSechzig); uint64_t jHi = num.getBlock(1); uint64_t jLo = num.getBlock(0); return (jHi << 32 | jLo); } void convertU64ToBig(uint64_t num, BigUnsigned& b) { uint32_t numLo = (uint32_t)(num & 0xFFFFFFFF); uint32_t numHi = (uint32_t)((num >> 32) & 0xFFFFFFFF); b.setBlock(0, numLo); b.setBlock(1, numHi); } //calculates (a * b) % c BigUnsigned mulmod(BigUnsigned a, BigUnsigned b, BigUnsigned mod) { BigUnsigned x = 0; BigUnsigned y = a % mod; while (b > 0) { if (b % 2 == 1) { x = (x + y) % mod; } y = (y * 2) % mod; b /= 2; } return x % mod; } BigUnsigned powmod_Volkard(BigUnsigned base, BigUnsigned exp, BigUnsigned modul) { //rechnet 'base hoch exp mod modul' BigUnsigned a1 = base, z1 = exp, x = 1, m = modul; while (z1 != 0) { while ((z1 % 2) == 0) { z1 /= 2; a1 = mulmod(a1, a1, m); } --z1; x = mulmod(x, a1, m); } return x; } bool IsProbablyPrime_MillerRabinOptimized(BigUnsigned n) { /* example: step: "calculate s and d" n = 1001 n-1 = 1000 s = 3 d = 125 1000 / 2^3 = 125 step: "test with base a" ad = a^d mod n (cf. function powmod) ... */ if (n == 2) return true; // "s" is the logarithm to base base 2 of the biggest number 2^s dividing n-1 // d = (n-1)/(2^s) // calculate d and s BigUnsigned d = n - 1; BigUnsigned s = 0; while ((d & 1) == 0) { ++s; d >>= 1; } // We do not use random base a = {2 ... n-1}, but fix it to a = 2 BigUnsigned a = 2; // base ("witness") BigUnsigned ad = (powmod_Volkard(a%n, d, n)); // (a^d) mod n if (ad == 1) return true; // a^d mod n == 1 if ((s > 0) && (ad == (n - 1))) return true; // a^d mod n == -1 (tests (a^d)^(2^0)) // test for r = {0 ... s-1} for (BigUnsigned r = 1; r < s; ++r) { // ad is currently ad^(2^(r-1)), thus we square it to get ad^(2^r)) ad = mulmod(ad, ad, n); if (ad == (n - 1)) return true; // tests (a^d)^(2^(r+1))) } return 0; // false, composite } bool IsPrimeDivisionTest(BigUnsigned number) { if (number<2) return false; if (number == 2) return true; if (number % 2 == 0) return false; for (BigUnsigned i = 3; i*i <= number; i += 2) { if (number%i == 0) return false; } return true; } bool IsPrime(vector<bool>& primes, BigUnsigned number, BigUnsigned primeLimit, unordered_set<uint64_t>& setLiar) { // lookup from bitset (in the RAM) if (number <= primeLimit) return primes[number.toUnsignedLong()]; // division test with first prime numbers if ( /*(number % 2 == 0) || */ (number % 3 == 0) || (number % 5 == 0) || (number % 7 == 0) || (number % 11 == 0) || (number % 13 == 0) || (number % 17 == 0) || (number % 19 == 0) || (number % 23 == 0) || (number % 29 == 0) || (number % 31 == 0) || (number % 37 == 0) || (number % 41 == 0) || (number % 43 == 0) || (number % 47 == 0) || (number % 53 == 0) || (number % 59 == 0) || (number % 61 == 0) || (number % 67 == 0) || (number % 71 == 0) || (number % 73 == 0) || (number % 79 == 0) || (number % 83 == 0) || (number % 89 == 0) || (number % 97 == 0) || (number % 101 == 0) ) { return false; } // Miller-Rabin, not perfect, but fast if (!IsProbablyPrime_MillerRabinOptimized(number)) { return false; // } else { if (lastLiar == 0) { return true; } BigUnsigned bigLastLiar; BigUnsigned& refBigLastLiar = bigLastLiar; convertU64ToBig(lastLiar, refBigLastLiar); if (number > bigLastLiar) { return true; } else { if (setLiar.find(convertBigToU64(number)) != setLiar.cend()) { // cout << "ALARM: Miller-Rabin-Liar found: " << number << " "; // wait(); return false; // function tells the truth by failure list (0 ... 2^64), above lastLiar there will be "Prime Hell" again. } return true; } } } void PrimePairFound(vector<bool>& primes, BigUnsigned i, const BigUnsigned primeLimit, unordered_set<uint64_t>& setLiar, bool* retVal) { *retVal = false; BigUnsigned grenze = i / 2; BigUnsigned a = 0; for (a = 3; a <= grenze; a += 2) { if (IsPrime(primes, a, primeLimit, setLiar) && IsPrime(primes, i - a, primeLimit, setLiar)) { *retVal = true; break; } } mutex_.lock(); cout << i << " " << setw(5) << a << endl; mutex_.unlock(); } int main() { setConsole(); // windows.h // big console for large numbers! ^^ textcolor(14); // windows.h // 10 = light green, 14 = light yellow BigUnsigned startNumber = stringToBigUnsigned("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); BigUnsigned endNumber = stringToBigUnsigned("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020"); const int numberOfThreads = 11; // count of even numbers from start to end = (end - start)/2 + 1 uint64_t const primeLimit = 2000000; //200000000000; cout << "We generate vector<bool>(primes) up to: " << primeLimit << endl; // (this amount depends on the capability of your RAM) vector<bool> primes(primeLimit); // processed numbers; time_t startTime, endTime; // measuring execution time uint64_t global_count = 0; // total number of primes time(&startTime); // initialize the number array primes[0] = false; primes[1] = false; for (uint64_t i = 2; i<primeLimit + 1; ++i) { primes[i] = true; } // sieving loop uint64_t iMax = (uint64_t)sqrt((double)primeLimit) + 1; for (uint64_t i = 2; i<iMax; ++i) { if (primes[i]) { for (uint64_t j = 2 * i; j<primeLimit + 1; j += i) { primes[j] = false; } } } time(&endTime); // count the number of primes for (uint64_t i = 0; i<primeLimit + 1; ++i) { if (primes[i]) global_count++; } cout << "In " << difftime(endTime, startTime) << " s we found " << global_count << " prime numbers (2 to " << primeLimit << " )." << endl << endl; #ifdef MILLER_RABIN_LIAR_CHECK // Prepare Miller-Rabin "Liar-Liste" and detect Goldbach prime pairs with Miller-Rabin test ifstream fileLiar("F:\\Daten\\Miller_Rabin_Liar\\2-SPRP-2-to-64.txt", fstream::in); // https://miller-rabin.appspot.com/ cout << "Miller-Rabin Liar file starts to be read from file." << endl; uint64_t count = 0; uint64_t u64LiarNumber = 0; while (!fileLiar.eof()) { fileLiar >> u64LiarNumber; // cout << u64LiarNumber << "\t"; setLiar.insert(u64LiarNumber); count++; } fileLiar.close(); lastLiar = u64LiarNumber; cout << "Miller-Rabin Liar file with " << count << " liars is read." << endl << "Last liar: " << u64LiarNumber << endl << endl; #endif // MILLER_RABIN_LIAR_CHECK unordered_set<uint64_t>& refSetLiar = setLiar; #ifdef MILLER_RABIN_LIAR_CHECK cout << "Now Goldbach first prime number pair will be detected:" << endl; #else cout << "Now Goldbach probable (no liar list available) first prime number pair will be detected:" << endl; #endif clock_t zeit1, zeit2, zeit2_old, delta; zeit2 = zeit1 = clock(); uint64_t sumTime = 0; for (BigUnsigned i = startNumber; i <= endNumber; i += (2 * numberOfThreads)) { bool retVal[numberOfThreads]; vector<thread> t; for (int x = 0; x<numberOfThreads; ++x) { retVal[x] = false; if ((i+2*x) <= endNumber) t.emplace_back(PrimePairFound, primes, i+2*x, BigUnsigned((unsigned long)primeLimit), refSetLiar, retVal + x); } for (int x = 0; x < numberOfThreads; ++x) { if ((i + 2 * x) <= endNumber) { t[x].join(); // we have to wait for the result of the thread! if (retVal[x] == false) cout << "Counterevidence found for this number: " << i + 2 * x << endl; } } zeit2_old = zeit2; zeit2 = clock(); delta = zeit2 - zeit2_old; sumTime += delta; cout << "time: " << 1000 * delta / CLOCKS_PER_SEC << " ms" << endl << endl; } cout << "total time: " << sumTime/1000 << " s" << endl << endl; wait(); }
`We generate vector<bool>(primes) up to: 2000000
In 0 s we found 148933 prime numbers (2 to 2000000 ).
Now Goldbach probable (no liar list available) first prime number pair will be detected:
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018 191
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 173
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 179
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008 181
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016 733
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 613
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020 193
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010 619
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012 719
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 709
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014 1153
time: 237940 ms
total time: 237 s`
-
Erhard Henkes schrieb:
Die Thread-Unterstützung habe ich mal eingebaut (noch nicht ideal) und im Bereich 10^120 bis 10^120 + 20 getestet (11 Geradzahlen, 11 Threads). Das läuft gut, man er hält die Ergebnisse in der gleichen Zeit wie vorher die langsamste Zahl in diesem Bereich. 11 Threads sind bei mir mit 12 virtuellen Kernen optimal. Hier das Programm, falls es jemand ausprobieren/optimieren will:
Hab nur 2 Kerne
-
Hab nur 2 Kerne
Dann stellst Du die numberOfThreads auf 2, 3 oder 4 (dein OS kümmert sich um das MT Scheduling) ein. Ich baue das noch um, sodass die richtige Threadanzahl verwendet wird und diese auch immer etwas zu schaffen haben.
Probiere momentan verschiedene Abläufe aus.Wichtige Frage: Kann man die Gesamtauslastung (vom Prg aus) des gesamten Programms auf einen gewissen Wert (z.B. 50 oder 80%) einstellen?
-
Hier ein erster Test für 10^240 (!):
`Now Goldbach probable (no liar list available) first prime number pair will be detected:
...04 97
...16 109
...10 103
...08 101
...14 107
...20 113
...06 2357
...18 3527
...00 2351
...02 2039
...12 5279
total time: 3654 s`
Zeitbestimmend bei der Primzahl-Paarsuche ist die Zahl 10^240 + 12, da wir erst bei 5279 fündig werden. Hier könnten vlt. noch mehr Primzahlen (bisher: 3 ... 101) beim Pre-Divisions-Check helfen.
Man sieht, dass nun die Primzahlabstände (nach unten) von der untersuchten Geradzahl nun auch mehrfach im Bereich über 1000 liegen (bei 10^120 war es nur eine von elf Zahlen). Gibt es hier eine Regel für den Abstand zwischen zwei Primzahlen in Abhängigkeit vom untersuchten Zahlenbereich?
Dieser Zahlenbereich 10^240 stellt somit in etwa die Grenze dar für das aktuelle Programm. Ich gehe davon aus, dass Goldbach mit seiner Vermutung recht behalten wird. Das Programm ist auch ein hübscher CPU-Stresstest zum Testen neuer PCs. Einfach den Bereich X bis X + 100 (X: sehr große Zahl) berechnen lassen und Zahl der Threads entsprechend auf 51 einstellen.
-
Wichtige Frage: Kann man die Gesamtauslastung (vom Prg aus) des gesamten Programms auf einen gewissen Wert (z.B. 50 oder 80%) einstellen?
macht man ja üblicherweiße über OS. alternativ kann man es ja mit nem sleep machen und zeit/tickcount nehmen
edit: so gehts ein wenig bequemer, wenn ichs richtig überflogen habe:
https://technet.microsoft.com/en-us/library/ff384148.aspx?f=255&MSPPError=-2147217396
-
Ich habe doch noch mal 10^300 geprüft:
`..04 73
..10 79
..14 83
..02 71
..20 89
..06 353
..12 359
..00 347
..16 6427
..08 3767
..18 12149
total time: 14226 s
`
Da in dieser Region der Abstand zur nächsten Primzahl (nach unten) in einem Fall (10^300 + 18) auf fünfstellige Zahlen anwächst, braucht die Berechnung fast 4 Std. Das ruft nun wirklich neue Algos auf den Plan.
-
Irgendwas stimmt mit deinem Multithreading nicht richtig. Ich hab die Diskussion hier nur so halb verfolgt, wollte aber mal das Programm ausprobieren. Neuere gcc Versionen (4.9) beschweren sich über diese Zeile:
t.emplace_back(PrimePairFound, primes, i+2*x, BigUnsigned((unsigned long)primeLimit), refSetLiar, retVal + x);
Mit einer älteren Version (4.4) compiliert es immerhin. Ausschnitt der Fehlermeldung:
main.cpp:336:125: required from here /opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<void (*(std::vector<bool>, BigUnsigned, BigUnsigned, std::unordered_set<long unsigned int>, bool*))(std::vector<bool>&, BigUnsigned, BigUnsigned, std::unordered_set<long unsigned int>&, bool*)>’ typedef typename result_of<_Callable(_Args...)>::type result_type;
Ich denke das Problem ist, dass deine
PrimePairFound
Funktion einige Parameter als Referenz nimmt. Wenn ich den Code so ändere compiliert es auch mit gcc 4.9:t.emplace_back(PrimePairFound, std::ref(primes), i+2*x, BigUnsigned((unsigned long)primeLimit), std::ref(refSetLiar), retVal + x);
Aber selbst wenn es compiliert kriege ich einen Segfault in genau dieser Zeile.
-
Im MS VS 2015 läuft es ohne Probleme. Daher ändere ich da gar nix.