概要
random.sample()とrandom.shuffle()の違いやrandom.choice()の使い方について、サンプルコードを交えて解説します。
サンプルコード
サンプルコードとして、4つの国からなるグループAとグループBを作成し、それぞれのグループからランダムに一つの国を取得して対戦カードを作るコードを実装しました。
import random
class MatchupGenerator:
# クラスのコンストラクタ
def __init__(self, countries):
self.countries = countries
# すべてのデータを返す
def get_all(self):
return self.countries
# データをシャッフルしたリストを返す
def get_shuffle(self):
return random.sample(self.countries, len(self.countries))
# ランダムに1つのデータを返す
def get_random(self):
return random.choice(self.countries)
country_group_a = ['Japan', 'Spain', 'Germany', 'Costa Rica']
country_group_b = ['Morocco', 'Croatia', 'Belgium', 'Canada']
group_a_generator = MatchupGenerator(country_group_a)
group_b_generator = MatchupGenerator(country_group_b)
print("All countries in group A:", group_a_generator.get_all())
print("All countries in group B:", group_b_generator.get_shuffle())
print("Random match:", group_a_generator.get_random(), "VS", group_b_generator.get_random())
# 出力結果
# All countries in group A: ['Japan', 'Spain', 'Germany', 'Costa Rica']
# All countries in group B: ['Belgium', 'Canada', 'Croatia', 'Morocco']
# Random match: Japan VS Croatia
(サンプル用にrandomにあるchoiceとsampleを使うため、GroupBではあえてsampleを使っています)
random.choice()とrandom.randint()
random.choice(self.data)と同じ方法として以下の例もあります。
idx = random.randint(0, len(self.data) - 1)
return self.data[idx]
どちらも同じようにランダムな要素を取得する方法です。choiceという便利な方法を知らずに後者の方ではじめ実装していましたが、choiceの方が読みやすく簡潔ですね。
・random.choice(self.data):リストからランダムな要素を選択する非常に簡潔な方法
・random.randint(0, len(self.data) - 1):インデックスの範囲を指定してランダムなインデックスを生成し、それを使用してリストから要素を取得する方法
random.sample()とrandom.shuffle()
random.sample()と似たものに、random.shuffle()があるのですが、この関数は値を返しません。
そのため、return random.shuffle(self.data)のように直接random.shuffle()をreturn文の中で使用しても、返り値が存在しないためにNoneが返されます。
All countries in group B: None
インプレースで処理されるので、元のデータを新しいものに置き換えたい場合に使います。
ランダムに並び替えられた新しいリストが欲しい場合は、sampleを使う、ということですね。
また、試しにGroupBをタプル(イミュータブルなオブジェクト)に変更すると以下のエラーになります。
x[i], x[j] = x[j], x[i]
TypeError: 'tuple' object does not support item assignment
これは、random.shuffle()関数は、シーケンス型(リストやタプル)を受け入れますが、シャッフルを行う際に要素の入れ替え(x[i], x[j] = x[j], x[i])を使用しているため、対象となるシーケンスが変更可能(ミュータブル)でなければならないため。タプルは変更不可能(イミュータブル)なのでエラーが出ています。
ちなみに、return random.shuffle(list(self.data))としてリストに変換してあげれば概念的には正しいアプローチですが、この関数自体がNoneを返すため、意味がありません。