Ada dua cara yang jelas untuk menghasilkan angka acak dari 0 hingga 9 dengan Python. Seseorang dapat menghasilkan angka floating point acak antara 0 dan 1, kalikan dengan 10, dan bulatkan. Atau, seseorang dapat menggunakan random.randint
metode ini.
import random
def random_digit_1():
return int(10 * random.random())
def random_digit_2():
return random.randint(0, 9)
Saya ingin tahu tentang apa yang akan terjadi jika seseorang menghasilkan angka acak antara 0 dan 1, dan menyimpan digit terakhir . Saya tidak selalu berharap distribusinya seragam, tetapi saya menemukan hasilnya cukup mengejutkan.
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)
Keluaran:
Counter({1: 84206,
5: 130245,
3: 119433,
6: 129835,
8: 101488,
2: 100861,
9: 84796,
4: 129088,
7: 120048})
Histogram ditunjukkan di bawah ini. Perhatikan bahwa 0 tidak muncul, karena trailing nol terpotong. Tetapi adakah yang bisa menjelaskan mengapa angka 4, 5, dan 6 lebih umum daripada yang lainnya? Saya menggunakan Python 3.6.10, tetapi hasilnya serupa di Python 3.8.0a4.
str
mengubahnya menjadi basis-10 yang pasti akan menimbulkan masalah. misalnya 1-bit mengambang mantissab0 -> 1.0
danb1 -> 1.5
. "Digit terakhir" akan selalu0
atau5
.random.randrange(10)
bahkan lebih jelas, IMHO.random.randint
(yang memanggil dirandom.randrange
bawah tenda) adalah tambahan nanti untukrandom
modul untuk orang yang tidak mengerti bagaimana rentang bekerja dengan Python. ;)randrange
sebenarnya berada di urutan kedua, setelah mereka memutuskan bahwarandint
antarmuka adalah kesalahan.Jawaban:
Itu bukan "digit terakhir" dari nomor tersebut. Digit terakhir dari string
str
memberi Anda ketika melewati nomor tersebut.Saat Anda memanggil
str
float, Python memberi Anda cukup digit yang memanggilfloat
string akan memberi Anda float asli. Untuk tujuan ini, trailing 1 atau 9 cenderung kurang diperlukan daripada digit lainnya, karena trailing 1 atau 9 berarti angka tersebut sangat dekat dengan nilai yang Anda dapatkan dengan membulatkan angka itu. Ada kemungkinan bagus tidak ada pelampung lain yang lebih dekat, dan jika demikian, angka itu dapat dibuang tanpa mengorbankanfloat(str(original_float))
perilaku.Jika
str
memberi Anda cukup digit untuk secara tepat mewakili argumen, digit terakhir hampir selalu menjadi 5, kecuali ketikarandom.random()
mengembalikan 0,0, dalam hal ini digit terakhir adalah 0. (Mengapung hanya dapat mewakili rasional dyadic , dan angka desimal bukan nol terakhir dari rasional dyadic non-integer selalu 5.) Outputnya juga akan sangat panjang, terlihat sepertiyang merupakan salah satu alasan
str
tidak melakukan itu.Jika
str
memberi Anda tepat 17 digit signifikan (cukup untuk membedakan semua nilai float satu sama lain, tetapi terkadang lebih banyak digit dari yang diperlukan), maka efek yang Anda lihat akan hilang. Akan ada distribusi angka trailing yang hampir seragam (termasuk 0).(Juga, Anda lupa bahwa
str
kadang - kadang mengembalikan string dalam notasi ilmiah, tapi itu efek kecil, karena ada kemungkinan rendah mendapatkan pelampung di mana itu akan terjadirandom.random()
.)sumber
TL; DR Contoh Anda sebenarnya tidak melihat angka terakhir. Digit terakhir mantissa terwakili biner terbatas yang dikonversi ke basis-10 harus selalu
0
atau5
.Lihatlah
cpython/floatobject.c
:Dan sekarang di
cpython/pystrtod.c
:Wikipedia menegaskan hal ini:
Jadi, ketika kita menggunakan
str
(ataurepr
), kita hanya mewakili 17 digit signifikan pada basis-10. Ini berarti beberapa nomor floating point akan terpotong. Bahkan, untuk mendapatkan representasi yang tepat, Anda membutuhkan ketepatan 53 digit signifikan! Anda dapat memverifikasi ini sebagai berikut:Sekarang menggunakan presisi maksimum, inilah cara yang tepat untuk menemukan "digit terakhir":
CATATAN: Seperti yang ditunjukkan oleh user2357112, implementasi yang benar untuk dilihat adalah
PyOS_double_to_string
danformat_float_short
, tapi saya akan membiarkan yang saat ini karena mereka lebih menarik secara pedagogis.sumber
str(some_float)
penggunaan pembulatan angka hingga cukup untuk pulang pergi .PyOS_double_to_string
. Implementasi itu telahfloat(str(x)) == x
. Sebagian besar, jawaban ini hanya untuk menunjukkan asumsi ("digit terakhir dari representasi persis") yang dibuat dalam pertanyaan itu salah, karena hasil yang benar adalah hanya5
s (dan tidak mungkin0
).