2010-06-14 8 views
10

Als ich für this question forschte und den Quellcode in random.py las, begann ich mich zu fragen, ob sich randrange und randint wirklich als "beworben" verhalten. Ich bin sehr geneigt, dies zu glauben, aber die Art, wie ich es gelesen habe, ist randrange im Wesentlichen alsKönnte random.randint (1,10) jemals 11 zurückgeben?

start + int(random.random()*(stop-start)) 

(unter der Annahme, ganzzahlige Werte für start und stop) umgesetzt, so randrange(1, 10) sollte eine Zufallszahl zwischen 1 und 9 zurück.

randint(start, stop)randrange(start, stop+1) ruft, wodurch eine Zahl zwischen 1 zurückkehrt und 10.

Meine Frage ist nun:

Wenn random() jemals 1.0 zurückgeben würde, würde randint(1,10)11 zurückgeben, oder?

+0

Übrigens, 'int (random.random() * n)' ist immer noch keine perfekte Methode, um ganze Zahlen zu erzeugen, die gleichmäßig in 'range (n)' verteilt sind; Es gibt einen Bias, der für kleine "n" bedeutungslos ist, aber signifikant wird, wenn "n" groß wird. Ich habe dafür einen Python-Bug unter http://bugs.python.org/issue9025 geöffnet. –

+0

@Mark Dickinson: Danke! Das ist faszinierend. –

+0

@ Mark Dickinson: [Dieser Fehler ist ab heute behoben] (http://docs.python.org/dev/whatsnew/3.2.html#random). –

Antwort

26

Von random.py und die Dokumentation:

"""Get the next random number in the range [0.0, 1.0).""" 

Die ) zeigt an, dass das Intervall exklusive 1,0. Das heißt, es wird nie 1.0 zurückgeben.

Dies ist eine allgemeine Konvention in der Mathematik ist, [ und ] ist inklusive, während ( und ) exklusiv ist, und die zwei Arten von Klammern können als (a, b] oder [a, b) gemischt werden. Werfen Sie einen Blick auf wikipedia: Interval (mathematics) für eine formelle Erklärung.

+0

Ich hatte das nicht "gefangen" (und selbst wenn ich es getan hätte, hätte ich seine Bedeutung nicht gewusst, also vielen Dank für diese aufschlussreiche Antwort). –

+1

@Tim: Zu Ihrer Information, es gibt verschiedene Konventionen. Eine andere gebräuchliche Konvention ist das Invertieren der eckigen Klammern, so dass '[a, b [' ein halboffenes Intervall ist, das äquivalent zu '[a, b)' ist. –

+5

Dies ist nicht ganz ausreichend, da es nicht offensichtlich ist, dass "0.0 <= x <1.0" bedeutet, dass 0 <= x * n

3

Von Python Dokumentation:

Fast alle Modulfunktionen hängen von der Grundfunktion random(), die ein zufälliges Schwimmers gleichmäßig in dem halboffenen Bereich erzeugt [0,0, 1,0).

Wie fast jeden PRNG von Float-Zahlen ..

12

Andere Antworten haben darauf hingewiesen, dass das Ergebnis der random() ist immer streng weniger als 1.0; Aber das ist nur die halbe Geschichte.

Wenn Sie randrange(n) als int(random() * n) Berechnen Sie auch Notwendigkeit, dass für jeden Python Schwimmer wissen x0.0 <= x < 1.0 erfüllt werden kann und eine positive ganze Zahl n, es ist wahr, dass 0.0 <= x * n < n, so dass int(x * n) ist strikt kleiner als n.

Es gibt zwei Dinge, die hier schief gehen könnten: Erstens, wenn wir x * n berechnen, wird n implizit in einen Float konvertiert. Für groß genug n kann diese Konvertierung den Wert ändern.Aber wenn Sie sich die Python-Quelle ansehen, werden Sie sehen, dass sie nur die int(random() * n)-Methode für n kleiner als 2**53 verwendet (hier und im Folgenden nehme ich an, dass die Plattform IEEE 754-Doubles verwendet), das ist der Bereich, in dem die Konvertierung von n zu einem Schwimmer ist garantiert, keine Informationen zu verlieren (weil n kann genau wie ein Schwimmer dargestellt werden).

Die zweite Sache, die schief gehen könnte ist, dass das Ergebnis der Multiplikation x * n (die jetzt als Produkt von Floats durchgeführt wird, nicht vergessen) wird wahrscheinlich nicht genau darstellbar sein, so dass es einige Rundungen beteiligt sein wird. Wenn x nah genug an 1.0 ist, ist es denkbar, dass die Rundung das Ergebnis auf n selbst runden wird.

Um zu sehen, dass dies nicht passieren kann, müssen wir nur den größtmöglichen Wert für x berücksichtigen, was (auf fast allen Rechnern, auf denen Python läuft) 1 - 2**-53 ist. Also müssen wir zeigen, dass (1 - 2**-53) * n < n für unsere positive ganze Zahl n, da es immer wahr sein wird, dass random() * n <= (1 - 2**-53) * n.

Proof (Skizze) Lassen Sie kk so dass 2**(k-1) < n <= 2**k die eindeutige ganze Zahl sein. Dann ist der nächste Schwimmer von nn - 2**(k-53). Wir müssen zeigen, dass n*(1-2**53) (d. H. Der tatsächliche, nicht gerundete Wert des Produkts) näher an n - 2**(k-53) ist als an n, so dass es immer abgerundet wird. Aber eine kleine Arithmetik zeigt, dass der Abstand von n*(1-2**-53) zu n2**-53 * n ist, während der Abstand von n*(1-2**-53) zu n - 2**(k-53)(2**k - n) * 2**-53 ist. Aber 2**k - n < n (weil wir wählten k so dass 2**(k-1) < n), so dass das Produkt ist näher an n - 2**(k-53), so dass es wird nach unten geht gerundet (vorausgesetzt, das heißt, dass die Plattform eine Form von runden zu nächsten tut) .

So sind wir sicher. Puh!


Addendum (2015.07.04): Die oben annimmt IEEE 754 binary64 Arithmetik, mit runden Bindern-to-even Rundungsmodus. Auf vielen Maschinen ist diese Annahme ziemlich sicher. Auf x86-Rechnern, die die FPU x87 für Fließkomma verwenden (z. B. verschiedene Varianten von 32-Bit-Linux), besteht jedoch die Möglichkeit, double rounding in der Multiplikation zu verwenden, was es möglich macht, um zu zu n zu runden in dem Fall, in dem random() den größtmöglichen Wert zurückgibt. Die kleinste solche n, für die dies passieren kann, ist . Siehe die Diskussion unter http://bugs.python.org/issue24546 für mehr.