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()で十分ですが、「〇ビット」という単位でデータを扱うときや大量生成で速度が欲しいときに、選択肢として思い出せると役立ちます。


あわせて読みたい

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら