Warum erzeugt rand() + rand() negative Zahlen?

Lesezeit: 10 Minuten

Benutzeravatar von badmad
böse verrückt

Das habe ich beobachtet rand() Bibliotheksfunktion, wenn sie nur einmal innerhalb einer Schleife aufgerufen wird, erzeugt sie fast immer positive Zahlen.

for (i = 0; i < 100; i++) {
    printf("%d\n", rand());
}

Aber wenn ich zwei addiere rand() Anrufen haben die generierten Nummern jetzt negativere Nummern.

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", rand(), (rand() + rand()));
}

Kann jemand erklären, warum ich im zweiten Fall negative Zahlen sehe?

PS: Ich initialisiere den Seed vor der Schleife als srand(time(NULL)).

  • rand() kann nicht negativ sein…

    – zwanzigzitrone

    13. Juni 2015 um 17:17 Uhr

  • rand() + rand() kann überfließen

    – maskacovnik

    13. Juni 2015 um 17:17 Uhr

  • Was ist RAND_MAX für deinen Compiler? Normalerweise findest du es in stdlib.h. (Komisch: prüfen man 3 randes trägt die einzeilige Beschreibung “schlechter Zufallszahlengenerator”.)

    – Jongware

    13. Juni 2015 um 17:26 Uhr


  • tun, was jeder vernünftige Programmierer tun würde abs(rand()+rand()). Ich hätte lieber einen positiven UB als einen negativen! 😉

    – Vinicius Kamakura

    15. Juni 2015 um 17:32 Uhr

  • @hexa: das ist keine lösung für die UB, da das bei der addierung schon vorkommt. UB kannst du nicht machen definiertes Verhalten. EIN vernünftig Progrtammer würde UB höllisch meiden.

    – zu ehrlich für diese Seite

    15. Juni 2015 um 18:07 Uhr


rand() ist so definiert, dass eine Ganzzahl zwischen zurückgegeben wird 0 und RAND_MAX.

rand() + rand()

überlaufen könnte. Was Sie beobachten, ist wahrscheinlich ein Ergebnis von undefiniertes Verhalten verursacht durch Integer-Überlauf.

  • @JakubArnold: Wie wird das als Überlaufverhalten von jeder Sprache anders angegeben? Python zum Beispiel hat keine (na ja, bis zum verfügbaren Speicher), da int einfach wächst.

    – zu ehrlich für diese Seite

    15. Juni 2015 um 18:09 Uhr


  • @Olaf Es hängt davon ab, wie eine Sprache entscheidet, vorzeichenbehaftete Ganzzahlen darzustellen. Java hatte keinen Mechanismus, um einen Ganzzahlüberlauf zu erkennen (bis Java 8) und definierte ihn so, dass er um und herumläuft gehen verwendet nur die 2er-Komplementdarstellung und definiert sie als zulässig für vorzeichenbehaftete Ganzzahlüberläufe. C unterstützt offensichtlich mehr als das Zweierkomplement.

    – PP

    15. Juni 2015 um 18:28 Uhr


  • @EvanCarslake Nein, das ist kein universelles Verhalten. Was Sie sagen, handelt von der 2er-Komplementdarstellung. Aber die C-Sprache erlaubt auch andere Darstellungen. Die Spezifikation der C-Sprache besagt, dass ein Überlauf von vorzeichenbehafteten Ganzzahlen ist nicht definiert. Im Allgemeinen sollte sich also kein Programm auf ein solches Verhalten verlassen und muss sorgfältig codieren, um keinen Überlauf von vorzeichenbehafteten Ganzzahlen zu verursachen. Dies gilt jedoch nicht für Ganzzahlen ohne Vorzeichen, da sie auf eine genau definierte Weise (Reduktion Modulo 2) “umlaufen” würden. [continued]…

    – PP

    16. Juni 2015 um 17:19 Uhr


  • Dies ist das Zitat aus dem C-Standard in Bezug auf den Überlauf von vorzeichenbehafteten Ganzzahlen: Wenn während der Auswertung eines Ausdrucks eine Ausnahmebedingung auftritt (d. h. wenn das Ergebnis nicht mathematisch definiert ist oder nicht im Bereich darstellbarer Werte für seinen Typ liegt), ist das Verhalten undefiniert.

    – PP

    16. Juni 2015 um 17:19 Uhr

  • @EvanCarslake entfernt sich ein wenig von der Frage, dass die C-Compiler den Standard verwenden, und für vorzeichenbehaftete Ganzzahlen können sie davon ausgehen a + b > a wenn sie das wissen b > 0. Sie können auch davon ausgehen, dass es sich um eine später ausgeführte Aussage handelt a + 5 dann ist der aktuelle Wert dann niedriger INT_MAX - 5. Selbst auf einem 2er-Komplement-Prozessor/Interpreter ohne Traps verhält sich das Programm möglicherweise nicht so, als ob ints waren Zweierkomplement ohne Traps.

    – Maciej Piechotka

    18. Juni 2015 um 8:02 Uhr

zu ehrlich für den Benutzer-Avatar dieser Seite
zu ehrlich für diese Seite

Das Problem ist die Zugabe. rand() gibt ein zurück int Wert von 0...RAND_MAX. Wenn Sie also zwei davon hinzufügen, werden Sie bis zu erhalten RAND_MAX * 2. Wenn das überschritten wird INT_MAXüberschreitet das Ergebnis der Addition den gültigen Bereich an int kann halten. Der Überlauf von vorzeichenbehafteten Werten ist ein undefiniertes Verhalten und kann dazu führen, dass Ihre Tastatur in fremden Sprachen mit Ihnen spricht.

Da es hier keinen Gewinn bringt, zwei zufällige Ergebnisse zu addieren, ist die einfache Idee, es einfach nicht zu tun. Alternativ können Sie jedes Ergebnis umwandeln unsigned int vor der Addition, wenn das die Summe halten kann. Oder verwenden Sie einen größeren Typ. Beachten Sie, dass long ist nicht unbedingt breiter als intgleiches gilt für long long wenn int ist mindestens 64 Bit!

Fazit: Verzichten Sie einfach auf den Zusatz. Mehr “Zufälligkeit” bietet es nicht. Wenn Sie mehr Bits benötigen, können Sie die Werte verketten sum = a + b * (RAND_MAX + 1)aber das erfordert wahrscheinlich auch einen größeren Datentyp als int.

Ihr angegebener Grund ist, ein Null-Ergebnis zu vermeiden: Das lässt sich nicht vermeiden, indem man die Ergebnisse von zwei addiert rand() Anrufe, da beide Null sein können. Stattdessen können Sie einfach erhöhen. Wenn RAND_MAX == INT_MAXdies ist nicht möglich int. Jedoch, (unsigned int)rand() + 1 wird es sehr, sehr wahrscheinlich tun. Wahrscheinlich (nicht endgültig), weil es erforderlich ist UINT_MAX > INT_MAXwas für alle mir bekannten Implementierungen gilt (die einige eingebettete Architekturen, DSPs und alle Desktop-, Mobil- und Serverplattformen der letzten 30 Jahre abdecken).

Warnung:

Obwohl hier bereits Kommentare eingestreut wurden, beachten Sie bitte, dass das Hinzufügen von zwei zufälligen Werten dies tut nicht erhalten eine gleichmäßige Verteilung, aber eine dreieckige Verteilung wie das Werfen von zwei Würfeln: zu bekommen 12 (zwei Würfel) beide Würfel müssen zeigen 6. zum 11 Es gibt bereits zwei mögliche Varianten: 6 + 5 oder 5 + 6etc.

Also ist der Zusatz auch unter diesem Aspekt schlecht.

Beachten Sie auch, dass die Ergebnisse rand() generiert sind nicht unabhängig voneinander, da sie von a generiert werden Pseudozufallszahlengenerator. Beachten Sie auch, dass der Standard die Qualität oder gleichmäßige Verteilung der berechneten Werte nicht spezifiziert.

  • @badmad: Was ist, wenn beide Aufrufe 0 zurückgeben?

    – zu ehrlich für diese Seite

    13. Juni 2015 um 18:08 Uhr


  • @badmad: Ich frage mich nur, ob UINT_MAX > INT_MAX != false wird durch die Norm gewährleistet. (Klingt wahrscheinlich, aber nicht sicher, ob erforderlich). Wenn dies der Fall ist, können Sie einfach ein einzelnes Ergebnis umwandeln und erhöhen (in dieser Reihenfolge!).

    – zu ehrlich für diese Seite

    13. Juni 2015 um 18:16 Uhr

  • Wenn Sie eine ungleichmäßige Verteilung wünschen, können Sie mehrere Zufallszahlen hinzufügen: stackoverflow.com/questions/30492259/…

    – Coeur

    16. Juni 2015 um 0:09 Uhr

  • um 0 zu vermeiden, ein einfaches “während das Ergebnis 0 ist, erneut würfeln” ?

    – Olivier Dulac

    16. Juni 2015 um 6:32 Uhr

  • Sie hinzuzufügen ist nicht nur ein schlechter Weg, um 0 zu vermeiden, sondern führt auch zu einer ungleichmäßigen Verteilung. Sie erhalten eine Verteilung wie beim Würfeln: 7 ist 6-mal so wahrscheinlich wie 2 oder 12.

    – Barmar

    16. Juni 2015 um 20:30 Uhr

Benutzeravatar von David Hammen
David Hammen

Dies ist eine Antwort auf eine Klarstellung der Frage, die im Kommentar zu dieser Antwort gestellt wurde,

Der Grund, warum ich hinzugefügt habe, war, ‘0’ als Zufallszahl in meinem Code zu vermeiden. rand()+rand() war die Quick-Dirty-Lösung, die mir sofort in den Sinn kam.

Das Problem war, 0 zu vermeiden. Es gibt (mindestens) zwei Probleme mit der vorgeschlagenen Lösung. Einer ist, wie die anderen Antworten zeigen, das rand()+rand() kann undefiniertes Verhalten hervorrufen. Der beste Rat ist, niemals undefiniertes Verhalten hervorzurufen. Ein weiteres Problem ist, dass es dafür keine Garantie gibt rand() wird nicht zweimal hintereinander 0 erzeugen.

Das Folgende lehnt Null ab, vermeidet undefiniertes Verhalten und ist in den allermeisten Fällen schneller als zwei Aufrufe rand():

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);

  • Wie wäre es mit rand() + 1?

    – fragenSieger

    14. Juni 2015 um 9:35 Uhr

  • @askvictor Das könnte überlaufen (obwohl es unwahrscheinlich ist).

    – Gerrit

    14. Juni 2015 um 10:25 Uhr

  • @gerrit – hängt von MAX_INT und RAND_MAX ab

    – fragenSieger

    14. Juni 2015 um 10:26 Uhr

  • @gerrit, ich wäre überrascht, wenn sie es sind nicht das gleiche, aber ich nehme an, das ist ein Ort für Pedanten 🙂

    – fragenSieger

    14. Juni 2015 um 10:31 Uhr

  • Wenn RAND_MAX==MAX_INT, wird rand() + 1 mit genau der gleichen Wahrscheinlichkeit überlaufen wie der Wert von rand() 0 ist, was diese Lösung völlig sinnlos macht. Wenn Sie bereit sind, es zu riskieren und die Möglichkeit eines Überlaufs zu ignorieren, können Sie rand() auch so verwenden, wie es ist, und die Möglichkeit ignorieren, dass es 0 zurückgibt.

    – Emil Jeřábek

    14. Juni 2015 um 16:47 Uhr

Benutzeravatar von Khaled.K
Khaled.K

Grundsätzlich rand() Zahlen zwischen produzieren 0 und RAND_MAXund 2 RAND_MAX > INT_MAX in Ihrem Fall.

Sie können mit dem Maximalwert Ihres Datentyps modulieren, um einen Überlauf zu verhindern. Dies wird natürlich die Verteilung der Zufallszahlen stören, aber rand ist nur ein Weg, um schnell Zufallszahlen zu erhalten.

#include <stdio.h>
#include <limits.h>

int main(void)
{
    int i=0;

    for (i=0; i<100; i++)
        printf(" %d : %d \n", rand(), ((rand() % (INT_MAX/2))+(rand() % (INT_MAX/2))));

    for (i=0; i<100; i++)
        printf(" %d : %ld \n", rand(), ((rand() % (LONG_MAX/2))+(rand() % (LONG_MAX/2))));

    return 0;
}

Vielleicht könnten Sie einen ziemlich kniffligen Ansatz versuchen, indem Sie sicherstellen, dass der von sum of 2 rand() zurückgegebene Wert niemals den Wert von RAND_MAX überschreitet. Ein möglicher Ansatz wäre sum = rand()/2 + rand()/2; Dies würde sicherstellen, dass für einen 16-Bit-Compiler mit einem RAND_MAX-Wert von 32767, selbst wenn beide Rand zufällig 32767 zurückgeben, selbst dann (32767/2 = 16383) 16383 + 16383 = 32766, also keine negative Summe resultieren würde.

  • Das OP wollte 0 aus den Ergebnissen ausschließen. Die Addition liefert auch keine gleichmäßige Verteilung von Zufallswerten.

    – zu ehrlich für diese Seite

    16. Juni 2015 um 23:20 Uhr

  • @Olaf: Es gibt keine Garantie, dass zwei aufeinanderfolgende Anrufe zu rand() werden nicht beide Null ergeben, daher ist der Wunsch, Null zu vermeiden, kein guter Grund, zwei Werte zu addieren. Andererseits wäre der Wunsch nach einer ungleichmäßigen Verteilung ein guter Grund, zwei zufällige Werte hinzuzufügen, wenn man sicherstellt, dass kein Überlauf auftritt.

    – Superkatze

    13. Juni 2018 um 16:53 Uhr

Benutzeravatar von Kevin Fegan
Kevin Fegan

Der Grund, warum ich hinzugefügt habe, war, ‘0’ als Zufallszahl in meinem Code zu vermeiden. rand()+rand() war die Quick-Dirty-Lösung, die mir sofort in den Sinn kam.

Eine einfache Lösung (okay, nennen Sie es einen “Hack”), die niemals ein Nullergebnis erzeugt und niemals überläuft, ist:

x=(rand()/2)+1    // using divide  -or-
x=(rand()>>1)+1   // using shift which may be faster
                  // compiler optimization may use shift in both cases

Dadurch wird Ihr Maximalwert begrenzt, aber wenn Sie sich nicht darum kümmern, sollte dies für Sie gut funktionieren.

  • Das OP wollte 0 aus den Ergebnissen ausschließen. Die Addition liefert auch keine gleichmäßige Verteilung von Zufallswerten.

    – zu ehrlich für diese Seite

    16. Juni 2015 um 23:20 Uhr

  • @Olaf: Es gibt keine Garantie, dass zwei aufeinanderfolgende Anrufe zu rand() werden nicht beide Null ergeben, daher ist der Wunsch, Null zu vermeiden, kein guter Grund, zwei Werte zu addieren. Andererseits wäre der Wunsch nach einer ungleichmäßigen Verteilung ein guter Grund, zwei zufällige Werte hinzuzufügen, wenn man sicherstellt, dass kein Überlauf auftritt.

    – Superkatze

    13. Juni 2018 um 16:53 Uhr

Benutzeravatar von Edward Karak
Eduard Karak

Um 0 zu vermeiden, versuchen Sie Folgendes:

int rnumb = rand()%(INT_MAX-1)+1;

Sie müssen einschließen limits.h.

  • Das verdoppelt die Wahrscheinlichkeit, 1 zu erhalten. Es ist im Grunde dasselbe (aber möglicherweise langsamer) wie das bedingte Hinzufügen von 1 if rand() ergibt 0.

    – zu ehrlich für diese Seite

    16. Juni 2015 um 23:18 Uhr

  • Ja, du hast Recht Olaf. Wenn rand() = 0 oder INT_MAX -1 ist, ist rnumb 1.

    – Doni

    17. Juni 2015 um 18:47 Uhr

  • Noch schlimmer, wenn ich darüber nachdenke. Es wird tatsächlich die Wahrscheinlichkeit für verdoppeln 1 und 2 (alles angenommen RAND_MAX == INT_MAX). Ich habe die vergessen - 1.

    – zu ehrlich für diese Seite

    17. Juni 2015 um 19:01 Uhr


  • Das -1 hier dient kein Wert. rand()%INT_MAX+1; würde immer noch nur Werte in der generieren [1…INT_MAX] Angebot.

    – chux – Wiedereinsetzung von Monica

    16. Februar 2018 um 22:56 Uhr

1427000cookie-checkWarum erzeugt rand() + rand() negative Zahlen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy