So generieren Sie eine zufällige Ganzzahl aus einem Bereich

Lesezeit: 2 Minuten

Benutzeravatar von Jamie Keeling
Jamie Keeling

Dies ist eine Fortsetzung einer zuvor geposteten Frage:

Wie erzeuge ich eine Zufallszahl in C?

Ich möchte in der Lage sein, eine Zufallszahl aus einem bestimmten Bereich zu generieren, z. B. 1 bis 6, um die Seiten eines Würfels nachzuahmen.

Wie würde ich vorgehen?

  • Wenn Sie sich die zweite Antwort auf die Frage ansehen, auf die Sie sich beziehen, haben Sie die Antwort. rand() % 6.

    – Mats Fredriksson

    24. März 2010 um 16:58 Uhr

  • Ich verstand nicht, wie es funktionierte, also beschloss ich, der Klarheit halber eine separate Frage zu stellen.

    – Jamie Keeling

    24. März 2010 um 17:29 Uhr

  • Zufälliger Gedanke: Wenn Sie einen zufälligen Querschnitt von Programmierern befragen, würden Sie feststellen, dass eine zufällige Anzahl von ihnen zufällig über Möglichkeiten nachdenkt, zufällig Zahlen zu generieren. Wenn man bedenkt, dass das Universum von präzisen und vorhersagbaren Gesetzen bestimmt wird, ist es nicht interessant, dass wir versuchen, Dinge zufälliger zu erzeugen? Fragen wie diese bringen immer die über 10.000 Poster zum Vorschein.

    – Armstärkste

    24. März 2010 um 19:00 Uhr


  • @Mats rand() % 6 kann eine 0 zurückgeben. Nicht gut für einen Würfel.

    – neu123456

    5. März 2011 um 19:33 Uhr

  • Können Sie stackoverflow.com/a/6852396/419 als akzeptierte Antwort anstelle der darauf verlinkten Antwort markieren 🙂 Danke.

    – Kev

    27. Juni 2012 um 13:15 Uhr

Benutzeravatar von Ryan Reich
Ryan Reich

Alle bisherigen Antworten sind mathematisch falsch. Rückkehr rand() % N gibt nicht einheitlich eine Zahl im Bereich an [0, N) unless N divides the length of the interval into which rand() returns (i.e. is a power of 2). Furthermore, one has no idea whether the moduli of rand() are independent: it’s possible that they go 0, 1, 2, ..., which is uniform but not very random. The only assumption it seems reasonable to make is that rand() puts out a Poisson distribution: any two nonoverlapping subintervals of the same size are equally likely and independent. For a finite set of values, this implies a uniform distribution and also ensures that the values of rand() are nicely scattered.

This means that the only correct way of changing the range of rand() is to divide it into boxes; for example, if RAND_MAX == 11 and you want a range of 1..6, you should assign {0,1} to 1, {2,3} to 2, and so on. These are disjoint, equally-sized intervals and thus are uniformly and independently distributed.

The suggestion to use floating-point division is mathematically plausible but suffers from rounding issues in principle. Perhaps double is high-enough precision to make it work; perhaps not. I don’t know and I don’t want to have to figure it out; in any case, the answer is system-dependent.

The correct way is to use integer arithmetic. That is, you want something like the following:

#include <stdlib.h> // For random(), RAND_MAX

// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]

long random_at_most(long max) { unsigned long // max <= RAND_MAX < ULONG_MAX, das ist also in Ordnung.  num_bins = (unsigned long) max + 1, num_rand = (unsigned long) RAND_MAX + 1, bin_size = num_rand / num_bins, defekt = num_rand % num_bins;  lang x;  tun { x = zufällig ();  } // Dies ist sorgfältig geschrieben, um nicht überzulaufen, während (num_rand - defekt <= (unsigned long)x);  // Gekürzte Division ist beabsichtigt return x/bin_size;  }

Die Schleife ist notwendig, um eine perfekt gleichmäßige Verteilung zu erhalten. Wenn Ihnen zum Beispiel Zufallszahlen von 0 bis 2 gegeben werden und Sie nur Einsen von 0 bis 1 wollen, ziehen Sie einfach weiter, bis Sie keine 2 erhalten; Es ist nicht schwer zu überprüfen, ob dies mit gleicher Wahrscheinlichkeit 0 oder 1 ergibt. Diese Methode wird auch in dem Link beschrieben, den nos in ihrer Antwort angegeben hat, obwohl sie anders codiert ist. Ich benutze random() statt rand() da es eine bessere Verteilung hat (wie in der Manpage für rand()).

Wenn Sie zufällige Werte außerhalb des Standardbereichs erhalten möchten [0, RAND_MAX], dann müssen Sie etwas kniffliges tun. Am zweckmäßigsten ist es vielleicht, eine Funktion zu definieren random_extended() das zieht n Bits (mit random_at_most()) und kehrt zurück [0, 2**n), and then apply random_at_most() with random_extended() in place of random() (and 2**n - 1 in place of RAND_MAX) to pull a random value less than 2**n, assuming you have a numerical type that can hold such a value. Finally, of course, you can get values in [min, max] verwenden min + random_at_most(max - min)einschließlich negativer Werte.

  • @Adam Rosenfield,@Ryan Reich: In einer verwandten Frage, auf die Adam geantwortet hatte: stackoverflow.com/questions/137783/… die am meisten positiv bewertete Antwort: Die Verwendung von „Modulus“ wäre dann falsch, nein? Um 1..7 aus 1..21 zu generieren, sollte das von Ryan beschriebene Verfahren verwendet werden. Bitte korrigieren Sie mich, wenn ich falsch liege.

    – Arvind

    12. April 2013 um 17:22 Uhr

  • Bei weiterer Überprüfung ist ein weiteres Problem hier, dass dies nicht funktioniert, wenn max - min > RAND_MAXwas schwerwiegender ist als das oben genannte Problem (z. B. VC++ hat RAND_MAX von nur 32767).

    – zwischenjay

    31. Oktober 2013 um 15:15 Uhr


  • Die While-Schleife könnte besser lesbar gemacht werden. Anstatt eine Zuweisung in der Bedingung auszuführen, möchten Sie wahrscheinlich a do {} while().

    – der JPster

    31. Dezember 2014 um 9:45 Uhr

  • Hey, diese Antwort wird im Comet OS-Buch zitiert;) Das erste Mal sehe ich das in einem Lehrbuch

    – vpuente

    10. Oktober 2017 um 8:13 Uhr

  • Es wird auch im OSTEP-Buch zitiert 🙂 pages.cs.wisc.edu/~remzi/OSTEP (Kapitel 9, Seite 4)

    – Rafaskar

    20. November 2018 um 18:26 Uhr


Benutzeravatar von theJPster
der JPster

Nach der Antwort von @Ryan Reich dachte ich, ich würde meine bereinigte Version anbieten. Die erste Begrenzungsprüfung ist angesichts der zweiten Begrenzungsprüfung nicht erforderlich, und ich habe sie eher iterativ als rekursiv gemacht. Es gibt Werte im Bereich zurück [min, max]wo max >= min und 1+max-min < RAND_MAX.

unsigned int rand_interval(unsigned int min, unsigned int max)
{
    int r;
    const unsigned int range = 1 + max - min;
    const unsigned int buckets = RAND_MAX / range;
    const unsigned int limit = buckets * range;

    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do
    {
        r = rand();
    } while (r >= limit);

    return min + (r / buckets);
}

  • Beachten Sie, dass dies in einer Endlosschleife stecken bleibt, wenn der Bereich >= RAND_MAX ist. Frag mich woher ich das weiß :/

    – der JPster

    23. Juli 2013 um 13:44 Uhr

  • Beachten Sie, dass Sie ein int mit einem unsigned int vergleichen (r >= limit). Das Problem lässt sich leicht lösen, indem man limit ein int (und optional bucket auch) seit RAND_MAX / range < INT_MAX und buckets * range <= RAND_MAX. BEARBEITEN: Ich habe den Vorschlag eingereicht und bearbeitet.

    – rrrrrrrrrrrrrrr

    3. Januar 2017 um 10:51 Uhr


  • Die Lösung von @Ryan Reich gibt mir immer noch eine bessere (weniger voreingenommene) Verteilung

    – Wladimir

    28. Juni 2017 um 18:20 Uhr

Benutzeravatar von Sattar
Sattar

Hier ist eine Formel, wenn Sie die maximalen und minimalen Werte eines Bereichs kennen und Zahlen einschließlich zwischen dem Bereich generieren möchten:

r = (rand() % (max + 1 - min)) + min

  • Wie in Ryans Antwort erwähnt, führt dies zu einem voreingenommenen Ergebnis.

    – David Wolever

    10. Juli 2014 um 19:04 Uhr

  • Voreingenommenes Ergebnis, Potenzial int überlaufen mit max+1-min.

    – chux – Wiedereinsetzung von Monica

    30. Dezember 2014 um 21:28 Uhr

  • dies funktioniert nur mit Integer Min und Max. Wenn Min und Max Float sind, ist es nicht möglich, die %-Operation durchzuführen

    – Francesco Taioli

    11. März 2018 um 18:16 Uhr


Benutzeravatar von nos
Nr

unsigned int
randr(unsigned int min, unsigned int max)
{
       double scaled = (double)rand()/RAND_MAX;

       return (max - min +1)*scaled + min;
}

Sehen hier für andere Optionen.

Benutzeravatar von Armstrongest
Armstärkste

Würdest du nicht einfach tun:

srand(time(NULL));
int r = ( rand() % 6 ) + 1;

% ist der Moduloperator. Im Wesentlichen wird es nur durch 6 geteilt und der Rest zurückgegeben ... von 0 - 5

  • Es wird Ergebnisse von 1 - 6 geben. Dafür ist das + 1 da.

    – Armstärkste

    24. März 2010 um 17:02 Uhr

  • Simon, zeig mir eine libc, die irgendwo verwendet wird rand() enthält die niederwertigen Bits des Zustands des Generators (wenn er ein LCG verwendet). Ich habe bisher noch keinen gesehen – alle (ja, einschließlich MSVC, wobei RAND_MAX nur 32767 ist) Löschen die niederwertigen Bits. Die Verwendung des Moduls wird aus anderen Gründen nicht empfohlen, nämlich weil dadurch die Verteilung zugunsten kleinerer Zahlen verzerrt wird.

    – Joey

    24. März 2010 um 17:09 Uhr

  • @Johannes: Man kann also mit Sicherheit sagen, dass Spielautomaten kein Modul verwenden?

    – Armstärkste

    24. März 2010 um 17:14 Uhr


  • Wie würde ich eine 0 ausschließen? Es scheint, dass, wenn ich es in einer Schleife von 30 laufen lasse, vielleicht beim zweiten oder dritten Mal, wenn es läuft, eine 0 ungefähr auf halbem Weg darin ist. Ist das eine Art Floh?

    – Jamie Keeling

    24. März 2010 um 18:04 Uhr

  • @Johannes: Vielleicht ist es heutzutage kein so großes Problem, aber traditionell ist die Verwendung der niederwertigen Bits nicht ratsam. c-faq.com/lib/randrange.html

    – jamesdlin

    24. März 2010 um 18:17 Uhr

Benutzeravatar der Community
Gemeinschaft

Für diejenigen, die das Bias-Problem verstehen, aber die unvorhersehbare Laufzeit von auf Zurückweisung basierenden Methoden nicht ertragen können, erzeugt diese Serie eine zunehmend weniger voreingenommene zufällige Ganzzahl im [0, n-1] Intervall:

r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...

Dies geschieht durch Synthetisieren einer hochpräzisen Festkomma-Zufallszahl von i * log_2(RAND_MAX + 1) Bits (wo i ist die Anzahl der Iterationen) und eine lange Multiplikation mit durchführt n.

Wenn die Anzahl der Bits im Vergleich dazu ausreichend groß ist nwird die Verzerrung unermesslich klein.

Es spielt keine Rolle, ob RAND_MAX + 1 ist weniger als n (wie in dieser Frage), oder wenn es sich nicht um eine Zweierpotenz handelt, aber es muss darauf geachtet werden, einen ganzzahligen Überlauf zu vermeiden, wenn RAND_MAX * n ist groß.

  • Es wird Ergebnisse von 1 - 6 geben. Dafür ist das + 1 da.

    – Armstärkste

    24. März 2010 um 17:02 Uhr

  • Simon, zeig mir eine libc, die irgendwo verwendet wird rand() enthält die niederwertigen Bits des Zustands des Generators (wenn er ein LCG verwendet). Ich habe bisher noch keinen gesehen – alle (ja, einschließlich MSVC, wobei RAND_MAX nur 32767 ist) Löschen die niederwertigen Bits. Die Verwendung des Moduls wird aus anderen Gründen nicht empfohlen, nämlich weil dadurch die Verteilung zugunsten kleinerer Zahlen verzerrt wird.

    – Joey

    24. März 2010 um 17:09 Uhr

  • @Johannes: Man kann also mit Sicherheit sagen, dass Spielautomaten kein Modul verwenden?

    – Armstärkste

    24. März 2010 um 17:14 Uhr


  • Wie würde ich eine 0 ausschließen? Es scheint, dass, wenn ich es in einer Schleife von 30 laufen lasse, vielleicht beim zweiten oder dritten Mal, wenn es läuft, eine 0 ungefähr auf halbem Weg darin ist. Ist das eine Art Floh?

    – Jamie Keeling

    24. März 2010 um 18:04 Uhr

  • @Johannes: Vielleicht ist es heutzutage kein so großes Problem, aber traditionell ist die Verwendung der niederwertigen Bits nicht ratsam. c-faq.com/lib/randrange.html

    – jamesdlin

    24. März 2010 um 18:17 Uhr

Hier ist ein etwas einfacherer Algorithmus als die Lösung von Ryan Reich:

/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
    uint32_t range = (end - begin) + 1;
    uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);

    /* Imagine range-sized buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    uint32_t randVal = rand();
    while (randVal >= limit) randVal = rand();

    /// Return the position you hit in the bucket + begin as random number
    return (randVal % range) + begin;
}

Example (RAND_MAX := 16, begin := 2, end := 7)
    => range := 6  (1 + end - begin)
    => limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)

The limit is always a multiple of the range,
so we can split it into range-sized buckets:
    Possible-rand-output: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
    Buckets:             [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
    Buckets + begin:     [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]

1st call to rand() => 13
    → 13 is not in the bucket-range anymore (>= limit), while-condition is true
        → retry...
2nd call to rand() => 7
    → 7 is in the bucket-range (< limit), while-condition is false
        → Get the corresponding bucket-value 1 (randVal % range) and add begin
    => 3

  • RAND_MAX + 1 kann leicht überlaufen int Zusatz. In diesem Fall, (RAND_MAX + 1) % range führt zu fragwürdigen Ergebnissen. In Betracht ziehen (RAND_MAX + (uint32_t)1)

    – chux – Wiedereinsetzung von Monica

    17. April 2018 um 15:03 Uhr


1422630cookie-checkSo generieren Sie eine zufällige Ganzzahl aus einem Bereich

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

Privacy policy