Algorithmus um kleine Zahlen in "gute" Zufallszahlen zu transformieren



  • Hallo,
    ich möchte eine Funktion zum en- und decrypten von strings schreiben. Dazu muss ich Zahlen, die relativ nah beieinander liegen (zB sich um den Wert 5 unterscheiden) in möglichst unterschiedliche Zahlen transformieren. Dabei soll das ganze Sprektrum von 0 bis unsigned_int_max abgedeckt sein.
    Beispiel:

    Gewünschte Ausgabe:
    seed: 1   Ausgabe: 324
    seed: 2   Ausgabe: 3850275047
    seed: 3   Ausgabe: 439560
    usw
    

    Gibt es so einen Algorithmus? Ich habe schon sämtliche Bitwise Operatoren und + - * / usw ausprobiert, aber immer lagen die Ergebnisse recht nah beieinander.
    Beispiel:

    //Beispiel
    unsigned int g3_rand(char b)
    {
        unsigned int a = static_cast<unsigned int>(b);
        a = ~a;
        a += 1680712364u;
        a *= 1680712363u;
        a &= 0xABAFACAA;
        a<<=1;
        a|= 123;
        a ^= 34;
        return a;
    }
    

    Hat jemand eine Idee, wie man sowas realisieren kann?



  • Such dir eine Funktion die nicht "stetig" ist bzw. in numerischer Ausdrucksweise einen instabilen Algorithmus, das heißt, dass kleine Änderungen in der Eingabe zu nicht vorhersagbaren Ausgaben führen.

    Vielleicht einfach mal nach unstable algorithms suchen.



  • einfach mit einer ungeraden zahl multiplizieren. die größte bereichsexpansion hast du mit (sqrt(5)-1)/2 * 2^32, also 2654435769.
    zum dekodieren mußt du nur die inverse finden, höhö (extended gcd).



  • Interessant. wie kommst du auf die Formel (sqrt(5)-1)/2 * Max ? Hast du mal nen Link oder Stichwort zum Nachlesen?



  • Gilder schrieb:

    Interessant. wie kommst du auf die Formel (sqrt(5)-1)/2 * Max ? Hast du mal nen Link oder Stichwort zum Nachlesen?

    das ist der goldene schnitt (golden ratio), sozusagen die irrationalste zahl.



  • Ich habe jetzt mal folgendes probiert mit recht guten Ergebnissen (Anmerkung: Performance spielt eigentlich keine Rolle).
    Hat jemand noch Verbesserungsvorschläge oder Tipps für mich?

    unsigned int funktion (const char b )
    {
        unsigned int tmp = (static_cast<unsigned int>(b));
    
        tmp += 2654435767u;
        tmp <<= 1;
        tmp ^= 2654435765u;
        tmp *= 2654435769u;
        tmp |= 2654435768u;
        tmp = ~tmp;
    
        return tmp;
    }
    
    int main()
    {
        std::cout<<funktion('a')<<std::endl;
        std::cout<<funktion('b')<<std::endl;
        std::cout<<funktion('c')<<std::endl;
        std::cout<<funktion('d')<<std::endl;
        std::cout<<funktion('e')<<std::endl;
        std::cout<<funktion('f')<<std::endl;
    
        /*
        * Ausgabe:
        *  1103135810
        *  21004864
        *  1082688582
        *  4227140
        *  1610612738
        *  562037760
        */
    }
    


  • Gilder schrieb:

    Hat jemand noch Verbesserungsvorschläge oder Tipps für mich?

    Ja. Einfach auf Onkel Volkard hören. 😉

    #include <iostream>
    
    unsigned int funktion (const char b )
    {
        unsigned int tmp = (static_cast<unsigned int>(b));
        tmp *= 2654435769u;
        return tmp;
    }
    
    int main()
    {
        std::cout<<funktion('a')<<std::endl;
        std::cout<<funktion('b')<<std::endl;
        std::cout<<funktion('c')<<std::endl;
        std::cout<<funktion('d')<<std::endl;
        std::cout<<funktion('e')<<std::endl;
        std::cout<<funktion('f')<<std::endl;
    
        /*
        * Ausgabe alt:
        *  1103135810
        *    21004864
        *  1082688582
        *     4227140
        *  1610612738
        *   562037760
        * Viele Zahlen klein, nur drei Zahlen bei einer Milliarde
        */
    
        /* Ausgabe neu
        *  4077199129
        *  2436667602
        *   796136075
        *  3450571844
        *  1810040317
        *   169508790
        * Bereich von 0 bis 4.2 Milliarden gleichmäßig benutzt
        */
    }
    

Anmelden zum Antworten