Python random.getrandbits()とは?ビット単位で乱数を作る
Pythonのrandom.getrandbits()は、指定したビット数ぶんのランダムな整数を返す関数です。
randint()のように「1〜100」と範囲を指定するのではなく、「32ビットぶん」「128ビットぶん」という指定の仕方をします。
見慣れない関数かもしれませんが、実はrandomモジュールのすべての乱数の土台になっている関数です。この記事では使い方と使いどころを解説します。
random.getrandbits()の基本的な使い方
import random
print(random.getrandbits(8))
実行結果(例):
163
getrandbits(8)は8ビットぶんのランダムな整数、つまり0〜255の値を返します。
「ビット数」と「返る値の範囲」の関係は次のとおりです。
| 引数k | 返る値の範囲 | 最大値 |
|---|---|---|
| 1 | 0〜1 | 1 |
| 4 | 0〜15 | 15 |
| 8 | 0〜255 | 255 |
| 16 | 0〜65535 | 65,535 |
| 32 | 0〜4294967295 | 約43億 |
式で書くと、0以上 2k 未満の整数が返ります。
2進数で見ると分かりやすい
「ビット」という言葉がピンとこない場合は、2進数で表示してみてください。
import random
random.seed(0)
x = random.getrandbits(16)
print(x)
print(format(x, "016b"))
実行結果:
55340
1101100000101100
0と1が16個並んだものが作られ、それを10進数で表したのが55340です。
getrandbits(k)はコインをk回投げて、表裏の並びを数字として読んでいるのと同じことをしています。
getrandbits(1)でコイントスを作る
引数に1を指定すると、0か1だけが返ります。
import random
random.seed(5)
print([random.getrandbits(1) for _ in range(10)])
実行結果:
[1, 0, 1, 0, 1, 1, 1, 1, 1, 1]
表裏の判定にそのまま使えます。
import random
for _ in range(5):
if random.getrandbits(1):
print("表")
else:
print("裏")
実行結果(例):
表
裏
裏
表
表
getrandbits(1)の戻り値は0か1の整数なので、ifの条件にそのまま書けます(0は偽、1は真として扱われます)。
random.randint()との違い
同じ整数の乱数でも、指定の仕方と得意な場面が違います。
| 関数 | 指定方法 | 範囲の自由度 | 速度 |
|---|---|---|---|
getrandbits(k) |
ビット数 | 2のべき乗のみ | 速い |
randint(a, b) |
下限と上限 | 自由 | やや遅い |
「1〜6のサイコロ」のような中途半端な範囲はgetrandbits()では作れません。そこはrandint()の出番です。
一方、大量の乱数を高速に作りたい場合はgetrandbits()が有利です。
import random
import time
n = 200000
start = time.time()
for _ in range(n):
random.getrandbits(32)
print(f"getrandbits: {time.time() - start:.3f}秒")
start = time.time()
for _ in range(n):
random.randint(0, 2**32 - 1)
print(f"randint : {time.time() - start:.3f}秒")
実行結果(例):
getrandbits: 0.015秒
randint : 0.119秒
環境によりますが、8倍前後の差が出ます。
randint()は「指定された範囲にきれいに収まるように調整する」処理を挟んでいるのに対し、getrandbits()は作った乱数をそのまま返しているだけだからです。
実はrandint()やrandrange()も、内部ではgetrandbits()を呼んで整数を作っています。getrandbits()は乱数の「素材」を直接受け取る関数だと考えると分かりやすいでしょう。
使いどころ①:大きなランダムID を作る
getrandbits()はビット数に上限がないため、とてつもなく大きな乱数も作れます。
import random
print(random.getrandbits(128))
実行結果(例):
110926744133954404232495112917528596547
16進数に変換すれば、IDや識別子として使いやすい形になります。
import random
random.seed(1)
token = format(random.getrandbits(64), "016x")
print(token)
実行結果:
91b7584a2265b1f5
format(値, "016x")で16桁ゼロ埋めの16進数になります。桁数を揃えられるので、ログのリクエストIDなどに便利です。
ただし、これを認証トークンに使ってはいけません。理由は後ほど説明します。
使いどころ②:大量のテストデータを作る
ダミーデータを何万件も生成するような場面では、速度差が効いてきます。
import random
random.seed(42)
# 8ビット = 0〜255 のセンサー値を想定
values = [random.getrandbits(8) for _ in range(5)]
print(values)
実行結果:
[163, 28, 6, 189, 70]
画像のピクセル値(0〜255)やバイトデータのシミュレーションなど、もともと「〇ビット」という単位で決まっているデータとは相性が抜群です。
使いどころ③:ランダムなビット列・フラグ
複数のオン・オフをまとめて決めたいときにも使えます。
import random
random.seed(3)
flags = random.getrandbits(5)
names = ["A", "B", "C", "D", "E"]
for i, name in enumerate(names):
on = (flags >> i) & 1
print(name, "ON" if on else "OFF")
実行結果:
A ON
B ON
C ON
D OFF
E OFF
このときflagsは7、2進数では00111です。1回の呼び出しで5個ぶんのランダムなフラグが得られます。>>と& 1で、i番目のビットを取り出しています。
getrandbits()でよくあるエラー
負の数を渡す
import random
print(random.getrandbits(-1))
実行結果:
ValueError: number of bits must be non-negative
ビット数にマイナスはありません。
小数を渡す
import random
print(random.getrandbits(3.5))
実行結果:
TypeError: integer argument expected, got float
割り算の結果をそのまま渡すと起きやすいミスです。//(整数除算)かint()で整数にしてから渡します。
0を渡す(エラーにはならない)
import random
print(random.getrandbits(0))
実行結果:
0
0ビット、つまり選択肢が0しかないため、常に0が返ります。エラーではないので、ループの中でkが0になっていても気づきにくい点に注意してください。
同じ結果を再現する
getrandbits()もrandom.seed()の影響を受けます。
import random
random.seed(42)
print(random.getrandbits(1), random.getrandbits(4), random.getrandbits(32))
実行結果:
1 1 107420369
シードを固定すれば何度実行しても同じ値になります。詳しくは「Python random.seed()とは?乱数を固定して再現する方法」をご覧ください。
トークンや鍵には使わない
getrandbits(256)のように大きなビット数を指定すると、いかにも「暗号に使えそうな乱数」に見えます。しかし、これは使ってはいけません。
randomモジュールの乱数は計算で作られており、出力をある程度集めれば内部状態を復元できてしまうためです。ビット数を増やしても、この性質は変わりません。
セキュリティが関わる場面では、secretsモジュールを使います。
import secrets
# 安全な乱数ビット
print(secrets.randbits(64))
# 安全なトークン(16進文字列)
print(secrets.token_hex(16))
実行結果(例):
10496858423045781237
3f8a1c9d4e2b7605a1d8c3f9e2b74d05
| 用途 | 使う関数 |
|---|---|
| テストデータ・シミュレーション | random.getrandbits() |
| パスワード・トークン・鍵 | secrets.randbits() / secrets.token_hex() |
secrets.randbits()は、getrandbits()とまったく同じ使い方ができる安全版です。迷ったらこちらを選んでください。
random.getrandbits()のまとめ
getrandbits(k)は0以上 2k 未満のランダムな整数を返す- 2進数で見ると0と1がk個並んだもの
getrandbits(1)はコイントスに使えるrandint()より高速だが、範囲は2のべき乗に限られるrandint()やrandrange()の内部でも使われている土台の関数- 負の数は
ValueError、小数はTypeError、0は常に0を返す - トークンや鍵には
secrets.randbits()を使う
普段はrandint()で十分ですが、「〇ビット」という単位でデータを扱うときや大量生成で速度が欲しいときに、選択肢として思い出せると役立ちます。



