Es gibt zwei offensichtliche Möglichkeiten, in Python eine zufällige Ziffer von 0 bis 9 zu generieren. Man könnte eine zufällige Gleitkommazahl zwischen 0 und 1 erzeugen, mit 10 multiplizieren und abrunden. Alternativ könnte man die random.randint
Methode verwenden.
import random
def random_digit_1():
return int(10 * random.random())
def random_digit_2():
return random.randint(0, 9)
Ich war gespannt, was passieren würde, wenn man eine Zufallszahl zwischen 0 und 1 generiert und die letzte Ziffer behält . Ich hatte nicht unbedingt erwartet, dass die Verteilung einheitlich sein würde, aber ich fand das Ergebnis ziemlich überraschend.
from random import random, seed
from collections import Counter
seed(0)
counts = Counter(int(str(random())[-1]) for _ in range(1_000_000))
print(counts)
Ausgabe:
Counter({1: 84206,
5: 130245,
3: 119433,
6: 129835,
8: 101488,
2: 100861,
9: 84796,
4: 129088,
7: 120048})
Ein Histogramm ist unten gezeigt. Beachten Sie, dass 0 nicht angezeigt wird, da nachgestellte Nullen abgeschnitten werden. Aber kann jemand erklären, warum die Ziffern 4, 5 und 6 häufiger sind als die anderen? Ich habe Python 3.6.10 verwendet, aber die Ergebnisse in Python 3.8.0a4 waren ähnlich.
str
konvertiert es in Basis-10, was Probleme verursachen kann. zB eine 1-Bit-Float-Mantisseb0 -> 1.0
undb1 -> 1.5
. Die "letzte Ziffer" ist immer0
oder5
.random.randrange(10)
ist noch offensichtlicher, IMHO.random.randint
(wasrandom.randrange
unter der Haube aufgerufen wird) war eine spätere Ergänzung desrandom
Moduls für Leute, die nicht verstehen, wie Bereiche in Python funktionieren. ;)randrange
kam tatsächlich an zweiter Stelle, nachdem sie entschieden hatten, dass dierandint
Schnittstelle ein Fehler war.Antworten:
Das ist nicht "die letzte Ziffer" der Nummer. Das ist die letzte Ziffer der Zeichenfolge,
str
die Sie erhalten haben, als Sie die Nummer übergeben haben.Wenn Sie
str
einen Float aufrufen , gibt Python Ihnen genügend Ziffern, damit Sie beim Aufrufenfloat
der Zeichenfolge den ursprünglichen Float erhalten. Zu diesem Zweck ist eine nachfolgende 1 oder 9 weniger wahrscheinlich als andere Ziffern erforderlich, da eine nachfolgende 1 oder 9 bedeutet, dass die Zahl sehr nahe an dem Wert liegt, den Sie durch Abrunden dieser Ziffer erhalten würden. Es besteht eine gute Chance, dass keine anderen Floats näher sind, und wenn ja, kann diese Ziffer verworfen werden, ohne dasfloat(str(original_float))
Verhalten zu beeinträchtigen .Wenn
str
Sie genügend Ziffern hätten, um das Argument genau darzustellen, wäre die letzte Ziffer fast immer 5, außer wennrandom.random()
0.0 zurückgegeben wird. In diesem Fall wäre die letzte Ziffer 0. (Floats können nur dyadische Rationalitäten darstellen und die letzte Dezimalstelle ungleich Null von Ein nicht ganzzahliger dyadischer Rational ist immer 5.) Die Ausgaben wären auch extrem lang und würden so aussehenDas ist einer der Gründe, warum
str
das nicht so ist.Wenn
str
Sie genau 17 signifikante Ziffern erhalten (genug, um alle Gleitkommawerte voneinander zu unterscheiden, aber manchmal mehr Ziffern als erforderlich), verschwindet der Effekt, den Sie sehen. Es würde eine nahezu gleichmäßige Verteilung der nachfolgenden Ziffern (einschließlich 0) geben.(Außerdem haben Sie vergessen, dass
str
manchmal eine Zeichenfolge in wissenschaftlicher Notation zurückgegeben wird, aber das ist ein geringfügiger Effekt, da die Wahrscheinlichkeit gering ist, dass ein Float entsteht, bei dem dies passieren würderandom.random()
.)quelle
TL; DR In Ihrem Beispiel wird nicht die letzte Ziffer angezeigt. Die letzte Ziffer einer endlichen binär dargestellten Mantisse, die in Basis 10 umgewandelt wurde, sollte immer
0
oder sein5
.Schauen Sie sich an
cpython/floatobject.c
:Und jetzt bei
cpython/pystrtod.c
:Wikipedia bestätigt dies:
Wenn wir also
str
(oderrepr
) verwenden, repräsentieren wir nur 17 signifikante Ziffern in Basis-10. Dies bedeutet, dass ein Teil der Gleitkommazahl abgeschnitten wird. Um die genaue Darstellung zu erhalten, benötigen Sie eine Genauigkeit von 53 signifikanten Stellen! Sie können dies wie folgt überprüfen:Wenn Sie nun die maximale Genauigkeit verwenden, finden Sie hier den richtigen Weg, um die "letzte Ziffer" zu finden:
HINWEIS: Wie von user2357112 hervorgehoben, sind
PyOS_double_to_string
und die richtigen Implementierungen zu betrachtenformat_float_short
, aber ich werde die aktuellen Implementierungen belassen, da sie pädagogisch interessanter sind.quelle
str(some_float)
Verwendung von Rundungen mit gerade genug Ziffern für die Rundreise .PyOS_double_to_string
. Diese Implementierung ist zugunsten dieserfloat(str(x)) == x
. Meistens sollte diese Antwort nur zeigen, dass die in der Frage gemachte Annahme ("letzte Ziffer der exakten Darstellung") falsch war, da das richtige Ergebnis nur5
s (und unwahrscheinlich0
) ist.