Goldbach Vermutung



  • Bengo schrieb:

    Hab mal die jetige MillierRabin (mit basis 2) funktion mit der divisionprime Funktion gleichgesetzt. Es gibt bei mir aber ein paar mehr unstimmigkeiten, als es eigentlich geben sollte:
    2027, 2039, 2047 (ok das ist normal), und dann gibt es noch ein paar mehr

    Nee.
    Die Unstimmigkeiten (Lügner-Zahlen, die man in der Liste nachschauen muss) fangen so an: https://oeis.org/A001262 (Die ersten 10000 hier: https://oeis.org/A001262/b001262.txt )
    Also wenn er bei 2047 falsch liegt, ist alles. ok. 3277 kann er sich auch leisten. Aber keine anderen dazwischen.



  • Die Tests deuten darauf hin, dass die "übersehenen" Primzahlen (also Rückgabe von false, obwohl die Primzahl true ist) bei 2^32 beginnen.

    Ausschnitt (der durch den Milli-Miller-Rabin übersehenen Primes):
    ` ALARM 4299154420 3 4299154417 time: 1 ms

    ALARM 4299154422 5 4299154417 time: 1 ms

    ALARM 4299154424 7 4299154417 time: 2 ms`

    Im Bereich darunter habe ich bisher noch nichts gefunden. Tests laufen weiter.

    Ich hatte den Miller-Rabin-Test bool isPrime(number) bisher so verstanden, dass man sich auf ein false verlassen kann, auf ein true jedoch nicht völlig sicher. Dies ist eindeutig aber nicht der Fall. Dies wirkt sich nun nach meinen bisherigen Untersuchungen ab 2^32 (erste Primes werden übersehen) und 2^64 / 2 (starke Verlangsamung der Prüfung der Goldbach-Vermutung durch Übersehen von Primzahlen in der Nähe von number) aus.

    @Volkard: Diese Lügner-Listen sind doch zur Absicherung von true gedacht, also 2047 (= 23 * 89) ist keine Prime usw., habe ich das richtig verstanden?



  • Ich habs schon irgentwo man geschreiben, der Algrotithmus quadriert im schlimmsten Fall seine Eingabezahl und wenn die größer ist, als der zahlbereich des Speichertypes, dann kann das Programm nicht mehr richtig funktioneren.
    Du kannst also maximal für 2^32 garantieren, dass der algrotihmus bei einer primzahl auch true ausgibt.



  • Das Hauptproblem für die Analyse der Goldbach-Vermtung ist, wie oben gezeigt, dass wir im Bereich 10^19 keine rasche Reaktion mehr sehen, weil die Primzahlen in der Nähe (im code: i-a) der untersuchten Goldbach-Geradzahl ein false erhalten, obwohl es sich um eine Primzahl handelt. Da könnte für diesen Bereich der Untersuchung die Divisionsmethode vorteilhaft sein. Kann man diesen Fall rasch erkennen und zur Disviiosons-Methode switchen, oder soll man ab ca. 2^64 / 2 generell für die Primzahlen a bis 100 das entsprechende i-a mit der Divisionsmethode testen, denn da findet sich fast immer etwas?!

    Man könnte auch per MT beide Methoden starten ("prime check race") und jeweils abbrechen, wenn der Sieger durchs Ziel geht.



  • Denke dem MillerRabin Test sollte man sowieso vorher einen Divisionstest mit den Zahlen 2,3,5,7,11 voranstellen, das geht schnell und kann schon einen Großteil der zusammengesetzten Zahlen vorab erkennen. Aber an einer BigInterger Lib wirst du nicht vorbeikommen, wenn du die Grenze 2^32 bzw. 2^64 überschreiten willst. Hab selbst noch keine benutzt, kann dir daher da auch keine gute Empfehlung machen.



  • Hab mal die mzp_class der gmp genutzt:

    #include <iostream>
    #include <cmath>
    #include <gmpxx.h>
    
    using namespace std;
    
    mpz_class mulmod(mpz_class a, mpz_class b, mpz_class mod)
    {
        mpz_class x = 0;
        mpz_class y = a % mod;
    
        while (b > 0)
        {
            if (b % 2 == 1)
            {
                x = (x + y) % mod;
            }
            y = (y * 2) % mod;
            b /= 2;
        }
        return x % mod;
    }
    
    mpz_class powmod(mpz_class x, mpz_class exp, mpz_class mod)
    {
        mpz_class res = 1;
    
        for (; exp; exp >>= 1)
        {
            if (exp % 2 == 1)
                res = mulmod(res, x, mod);
    
            x = mulmod(x, x, mod);
        }
    
        return res;
    }
    
    bool IsPrimeMillerRabinOptimized(mpz_class number, mpz_class base)
    {
        mpz_class d = number - 1;
        int counter = 0;
        while (d % 2 == 0)
        {
            d /= 2;
            ++counter;
        }
    
        mpz_class temp = powmod(base, d, number);
    
        if (temp == 1 || temp == number -1) //Hier
        {
            return true;
        }
    
        for (int i = 1; i < counter; ++i)
        {
            temp = (temp * temp) % number;
    
            if (temp == number - 1)
            {
                return true;
            }
        }
        return false;
    }
    
    bool IsPrimeDivisionTest(mpz_class number)
    {
        if (number<2)    return false;
        if (number == 2)   return true;
        if (number % 2 == 0) return false;
    
        for (mpz_class i=3; i*i<=number; i+=2)
        {
            if (number%i == 0) return false;
        }
        return true;
    }
    
    int main(int argc, char** argv) {
    
        for (int i = 2; i < 10000; ++i) {
            cout << i<<":"<< (int)(IsPrimeMillerRabinOptimized(i, 2)==IsPrimeDivisionTest(i))<< endl;
        }
    
    }
    

    Bei der Zahl 100000000000000000039, gibt der Miller zur Basis 2, aus, dass es eine Primzahl sein könnte. Die einfache Division kommt auf meinem Computer nicht mehr zum Ende. Und die zahl ist tatsächlich prim.

    Auch die zahl 1000000000000000000000000000, macht keine Probleme.

    Die Zahl 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019, ist prim zur Basis 2. Wird auch von http://primzahlen.zeta24.com/de/online_primzahltest.php bestätigt. Der Test dauert etwa eine halbe Sekunde.



  • Bengo: Ja, Du hast Recht. Wir müssen eine Stufe höher klettern. Probiere bitte https://mattmccutchen.net/bigint/ C++ Big Integer Library als Vergleich dazu aus, welches schneller ist (GMP soll schneller sein. Die Frage ist um wieviel). Schaffe es momentan nicht gmpxx/gmp unter Windows zum Laufen zu bekommen.

    Recht einfach für Dich:

    //#include <gmpxx.h>
    #include "BigIntegerLibrary.hh"
    
    #define mpz_class BigUnsigned
    

    Auf diese Weise findet man alle Lügner (falsche Primes nach Miller-Rabin) bis 10^6:

    int main(int argc, char** argv)
    {
    
         for (int i = 3; i < 1000000; ++i)
         {
             if (((int)IsPrimeMillerRabinOptimized(i, 2) == 1 && (int)IsPrimeDivisionTest(i) == 0))
                 cout << i << " Luegner" << endl; // Rabin-Miller Lügner
             if (((int)IsPrimeMillerRabinOptimized(i, 2) == 0 && (int)IsPrimeDivisionTest(i) == 1))
                 cout << i << " Ueberseher" << endl; // Rabin-Miller Primzahlen-Überseher
         }     
    
         wait();
     }
    

    `15841 Luegner

    29341 Luegner

    42799 Luegner

    49141 Luegner

    52633 Luegner

    65281 Luegner

    74665 Luegner

    80581 Luegner

    85489 Luegner

    88357 Luegner

    90751 Luegner

    104653 Luegner

    130561 Luegner

    196093 Luegner

    220729 Luegner

    233017 Luegner

    252601 Luegner

    253241 Luegner

    256999 Luegner

    271951 Luegner

    280601 Luegner

    314821 Luegner

    357761 Luegner

    390937 Luegner

    458989 Luegner

    476971 Luegner

    486737 Luegner

    489997 Luegner

    514447 Luegner

    580337 Luegner

    635401 Luegner

    647089 Luegner

    741751 Luegner

    800605 Luegner

    818201 Luegner

    838861 Luegner

    873181 Luegner

    877099 Luegner

    916327 Luegner

    976873 Luegner

    983401 Luegner`



  • Erhard Henkes schrieb:

    Schaffe es momentan nicht gmpxx/gmp unter Windows zum Laufen zu bekommen.

    Da gibt es eine ganz einfache Lösung: Linux benutzen! Ich brauchte exakt nur sudo apt-get install libgmp3-dev eingeben (Ubuntu), Enter drücken und im Code includieren, und die gmp Library in der IDE hinzufügen.

    Eigentlich sollte es jetzt keine Fälle mehr geben, in denen zu unrecht false zurückgegeben wird. Also ich hab keine mehr gefunden.



  • Erhard Henkes schrieb:

    Schaffe es momentan nicht gmpxx/gmp unter Windows zum Laufen zu bekommen.

    GMP ist mal wieder so ein Projekt was die Existenz von Windows ignoriert. Unter Windows fährt man mit MPIR (http://mpir.org/index.html#about) besser. Das Interface ist sogar kompatibel zu GMP.



  • Erhard Henkes schrieb:

    Ich weiß nicht genau, wo es anfängt, aber es ist unterhalb 2^64 / 2.

    Ja, ab dort irgendwo hab ich lauter Überseher. Und Überseher darf es gar nicht geben.
    Habs so gefixt.

    #include <iostream>
    #include <cassert>
    #include <cstdint>
    using namespace std;
    
    typedef unsigned int UInt32;
    static_assert(sizeof(UInt32)==4,"wrong size");
    
    typedef unsigned long long UInt64;
    static_assert(sizeof(UInt64)==8,"wrong size");
    
    typedef UInt64 u64;
    
    UInt64 mulmod(UInt64 a, UInt64 b, UInt64 m)
    {
    	UInt64 r;
    	asm
    	("mulq %2;"
    	 "divq %3;"
    	 : "=&d"(r), "+%a"(a)
    	 : "rm"(b), "rm"(m)
    	 : "cc"
    	);
    	return r;
    }
    /*uint64_t powmod(uint64_t x, uint64_t exp, uint64_t mod)
    {
    	uint64_t res = 1;
    	for(; exp; exp >>= 1)
    	{
    		if(exp & 1)
    			res = mulmod(res, x, mod);
    		x = mulmod(x, x, mod);
    	}
    	return res;
    }*/
    UInt64 powmod(UInt64 base,UInt64 exp,UInt64 modul){
        //rechnet 'base hoch exp mod modul'
        UInt64 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 isSPRP(UInt64 n,UInt64 a)
    {
    	if(a%n==0) return true;
      UInt64 d=n-1;
      UInt64 ad;
      UInt64 s=0;
    
      // break down n-1 into d*(2^s). Linux ffs() can be better
      while (0==(d&1)) {
        ++s; d>>=1;
      }
    
      ad=(powmod(a%n, d, n)); // (a^d) mod n
    
      if (ad==1) return 1; // 1 == a^d mod n
      if (s>0 && ad==(n-1)) return 1; // -1 == a^d mod n (tests (a^d)^(2^0))
      for (UInt64 r=1; r<s; ++r) {
        // ad is currently ad^(2^(r-1)) so we square it to get ad^(2^r));
        ad=mulmod(ad,ad,n);
        if (ad==(n-1)) return 1; //tests (a^d)^(2^(r+1)))
      }
      return 0; // false, composite
    }
    /*bool IsPrimeMillerRabinOptimized(uint64_t number, uint64_t base){
    	return isSPRP(number,base);
    }*/
    
    bool IsPrimeMillerRabinOptimized(uint64_t number, uint64_t base)
    {
    	uint64_t d = number - 1;
    	int counter = 0;
    	while(d % 2 == 0)
    	{
    		d /= 2;
    		++counter;
    	}
    	uint64_t temp = powmod(base, d, number);
    	if(temp == 1 || temp == number - 1)  //Hier
    	{
    		return true;
    	}
    	for(int i = 0; i < counter; ++i)
    	{
    		temp = (temp * temp) % number;
    		if(temp == number - 1)
    		{
    			return true;
    		}
    	}
    	return false;
    }
    
    bool IsPrimeDivisionTest(uint64_t number)
    {
    	if(number<2)    return false;
    	if(number == 2)   return true;
    	if(number % 2 == 0) return false;
    	for(uint64_t i=3; i*i<=number; i+=2)
    	{
    		if(number%i == 0) return false;
    	}
    	return true;
    }
    
    int main()
    {
    	for(uint64_t i = uint64_t(1)<<32; ; ++i)
    	{
    		if(IsPrimeMillerRabinOptimized(i, 2) && !IsPrimeDivisionTest(i))
    			cout << i << " Luegner" << endl; // Rabin-Miller Lügner
    		if(!IsPrimeMillerRabinOptimized(i, 2) && IsPrimeDivisionTest(i))
    			cout << i << " Ueberseher" << endl; // Rabin-Miller Primzahlen-Überseher
    	}
    	wait();
    }
    


  • Also der reihe nach: Die Tests bis 2^32 mit uint64_t ergeben allesamt keine falschen Rückgaben von false. Oberhalb von 2^32 tauchen falsche false auf. Daher benötigt man schnelle BigInteger. gmp soll wohl das schnelleste sein. Ich versuche es mal einzubauen.

    Ah, neuer Post von Volkard.



  • Erhard Henkes schrieb:

    Also der reihe nach: Die Tests bis 2^32 mit uint64_t ergeben allesamt keine falschen Rückgaben von false. Oberhalb von 2^32 tauchen falsche false auf. Daher benötigt man schnelle BigInteger. gmp soll wohl das schnelleste sein. Ich versuche es mal einzubauen.

    genau, da 2^32 im Quadrat 2^64 ergibt. Wenn es noch größer wird, dann gibt es Fehler durch einen overflow. Mit einer BigInteger Library kann ein overflow nicht passieren und auch die Werte oberhalb von 2^32 sind korrekt.



  • OT aber an denjenigen, der diesen Code ursprünglich geschrieben hat:

    #include <cstdint>
    // [...] 
    typedef unsigned int UInt32;
    static_assert(sizeof(UInt32)==4,"wrong size");
    
    typedef unsigned long long UInt64;
    static_assert(sizeof(UInt64)==8,"wrong size");
    

    Warum läd man den cstdint Header, hat einen Compiler der offensichtlich C++11 unterstützt, und nutzt dennoch nicht die uint32_t und uint64_t Typen?



  • sebi707 schrieb:

    OT aber an denjenigen, der diesen Code ursprünglich geschrieben hat:

    #include <cstdint>
    // [...] 
    typedef unsigned int UInt32;
    static_assert(sizeof(UInt32)==4,"wrong size");
     
    typedef unsigned long long UInt64;
    static_assert(sizeof(UInt64)==8,"wrong size");
    

    Warum läd man den cstdint Header, hat einen Compiler der offensichtlich C++11 unterstützt, und nutzt dennoch nicht die uint32_t und uint64_t Typen?

    *meld*
    Ursprünglich war das Teil einer lib, die nur ganz wenige Standard-Headers oder gar keine benutzt.
    Jemand hier benutzte u64, also schnell "typedef UInt64 u64;" nachgezogen.
    Die meisten benutzen uint64_t, also das include nachgezogen.

    Jetzt kann ich nach Belieben von beiden anderen CopyPasten und es geht.

    Nach dem include hätte ich meine typedef umbauen sollen, stimmt, so isses gar häßlich.



  • Ich brauche Hilfe, da MS VS den asm code nicht annimmt bei x64.

    UInt64 mulmod(UInt64 a, UInt64 b, UInt64 m)
    {
      UInt64 r;
      asm
      ("mulq %2;"
      "divq %3;"
      : "=&d"(r), "+%a"(a)
      : "rm"(b), "rm"(m)
      : "cc"
    );
    return r;
    }
    

    Könnt ihr das in C übersetzen? Ich schaffe das leider nicht.

    Mit

    uint64_t mulmod(uint64_t a, uint64_t b, uint64_t mod)
    {
    	uint64_t x = 0;
    	uint64_t y = a % mod;
    
    	while (b > 0)
    	{
    		if (b % 2 == 1)
    		{
    			x = (x + y) % mod;
    		}
    		y = (y * 2) % mod;
    		b /= 2;
    	}
    	return x % mod;
    }
    

    Gibt es massenweise Überseher ab 2^32. Möchte aber nicht auf x86 zurück, weil x64 so schön schnell ist.


  • Mod

    Ich schreibe gerade u.a. mulmod für u128 (dazu kommt wahrscheinlich bald ein Thread). Dasselbe geht mit u64 . Folgendes habe ich u.a. als non-assembler Variante stehen:

    u64 mulmod_peasant(u64 a, u64 b, u64 m) {
    	u64 res = 0;
    	for (b %= m; a != 0; a >>= 1) {
    		if (a & 1) {
    			if (res >= m - b)
    				res -= m;
    			res += b;
    		}
    		b += b - (b >= m / 2) * m;
    	}
    	return res;
    }
    

    Das ist jetzt aber auch guarded.



  • Es kommt so oder so ab 2^32 zu Fehlern, da bei MillerRabin Zahlen entstehen, die bis zum Quadrat der Testzahl gehen, und da es ohne BigInteger eben nur bis 2^64 geht, wirst du immer auf dieses Problem stoßen.
    Mit Assemblercode kann man vielleicht bei der modpow Funktion und dem temp = temp *temp % number, ohne dieses Quadrat auskommen. Bringt dir aber nicht so viel, weil du sowieso früher oder später BigInteger nutzen wirst 😉



  • Ja, mit mulmod_peasant kommt es auch sofort zu Übersehern. Also ran an BigInt.



  • Ich finde, IsPrime muss erstmal beschleunigt werden.

    int main() {
    	ifstream luegner("2-SPRP-2-to-64.txt");//https://miller-rabin.appspot.com/
    	UInt64 n;
    	size_t count=0;
    	while(luegner>>n) {
    		++count;
    		cout<<count<<' '<<n<<endl;
    	}
    }
    

    Ausgabe letzte Zeile:
    31894014 18446744066047760377
    Leider mag man so eine große Lügner-Tabelle nicht im RAM halten.

    while(luegner>>n) {
    		if(isSPRP(n,3)) continue;
    		++count;
    		cout<<count<<' '<<n<<endl;
    	}
    

    1501720 18446732893888604471
    Jup, dahin geht der Weg.

    while(luegner>>n) {
    		if(n%3==0) continue;
    		if(!isSPRP(n,3)) continue;
    		++count;
    		cout<<count<<' '<<n<<endl;
    	}
    

    1501720 18446732893888604471
    Bringt nix.

    while(luegner>>n) {
    		if(n%3==0) continue;
    		if(n%5==0) continue;
    		if(n%7==0) continue;
    		if(n%11==0) continue;
    		if(n%13==0) continue;
    		if(n%17==0) continue;
    		if(n%19==0) continue;
    		if(n%23==0) continue;
    		if(n%29==0) continue;
    		if(n%31==0) continue;
    		if(n%37==0) continue;
    		if(n%41==0) continue;
    		if(n%43==0) continue;
    		if(!isSPRP(n,3)) continue;
    		++count;
    		cout<<count<<' '<<n<<endl;
    	}
    

    1501438 18446732893888604471
    Bringt nix.

    131157 18446602774641402961
    Ok, wenig genug, um sie im RAM zu halten.

    Und dann im Array der fantastisch guten Lügner mit binary search schauen.

    Oder die harte Tour:

    while(luegner>>n) {
    		if(!isSPRP(n,3)) continue;
    		if(!isSPRP(n,5)) continue;
    		if(!isSPRP(n,7)) continue;
    		if(!isSPRP(n,11)) continue;
    		if(!isSPRP(n,13)) continue;
    		if(!isSPRP(n,17)) continue;
    		if(!isSPRP(n,19)) continue;
    		++count;
    		cout<<count<<' '<<n<<endl;
    	}
    

    1 341550071728321
    2 84983557412237221
    3 230245660726188031
    4 1134931906634489281
    5 1144336081150073701
    6 1167748053436849501
    7 1646697619851137101
    8 3825123056546413051
    9 4265186605968234451
    10 5474093792130026911
    11 7033671664103127781
    12 7361235187296010651
    13 8276442534101054431
    14 14688059738864848381
    15 16043083915816662841

    So in dieser Richtung sollte man gehen und einen deterministischen Primzahlentester bauen, der bis 2^64 sicher nicht lügt.



  • volkard schrieb:

    Erhard Henkes schrieb:

    Ich weiß nicht genau, wo es anfängt, aber es ist unterhalb 2^64 / 2.

    Ja, ab dort irgendwo hab ich lauter Überseher. Und Überseher darf es gar nicht geben.
    Habs so gefixt.

    /*bool IsPrimeMillerRabinOptimized(uint64_t number, uint64_t base){
    	return isSPRP(number,base);
    }*/
    
    bool IsPrimeMillerRabinOptimized(uint64_t number, uint64_t base)
    {
    	uint64_t d = number - 1;
    	int counter = 0;
    	while(d % 2 == 0)
    	{
    		d /= 2;
    		++counter;
    	}
    	uint64_t temp = powmod(base, d, number);
    	if(temp == 1 || temp == number - 1)  //Hier
    	{
    		return true;
    	}
    	for(int i = 0; i < counter; ++i)
    	{
    		temp = (temp * temp) % number;
    		if(temp == number - 1)
    		{
    			return true;
    		}
    	}
    	return false;
    }
    

    Sorry.
    Die obere Version, die auskommentierte war ok. Die Untere hat Überseher. Liegt gar nicht an mulmod.


Anmelden zum Antworten