質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

2回答

818閲覧

np.random.seed()の使い方について

chronobcelp

総合スコア9

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/01/22 03:41

前提・実現したいこと

「機械学習のエッセンス」 P.215~216で質問があります。
np.random.seed()の使い方がよくわかりません。

最後の部分で以下のように表示されます。
print(d1.get_sum()) #34
print(d2.get_sum()) #33

本文中には「seed関数はグローバルな状態を変化させる」とありますが、
なぜ異なる値を算出するのでしょうか?
よろしくお願いします。

該当のソースコード

Python

1import numpy as np 2 3# よくない実装の例 4class Dice: 5 def __init__(self): 6 np.random.seed(0) 7 self.sum_ = 0 8 9 def throw(self): 10 self.sum_ += np.random.randint(1, 7) 11 12 def get_sum(self): 13 return self.sum_ 14 15import dice2a 16 17d1=dice2a.Dice() 18for _ in range(10): 19 d1.throw() 20 21print(d1.get_sum()) #39 22 23d2=dice2a.Dice() 24for _ in range(10): 25 d2.throw() 26 27print(d2.get_sum()) #39 28 29d1=dice2a.Dice() 30d2=dice2a.Dice() 31for _ in range(10): 32 d1.throw() 33 d2.throw() 34 35print(d1.get_sum()) #34 36print(d2.get_sum()) #33

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chronobcelp

2022/01/22 05:14

「乱数を発生させることも状態を変化させるからです。」 上記の意味がわかりません。もう少し噛み砕いて説明していただけないでしょうか? よろしくお願いします。
guest

回答2

0

端的に言えば、提示コードではひとつの乱数生成器を共有しているためです。
そのため、後半のループ処理では
d1での呼び出し→d2での呼び出し→d1での呼び出し…
といった順番に乱数が返され、それぞれ別のオブジェクトに足されるため、前半のコードとは異なりd1,d2の合計値も異なる結果になります。
なお、解決策としては各オブジェクト毎に乱数発生器を持てばよいです。
参考:【NumPy】np.randomじゃなくてRandomStateを使いたい理由

Python

1import numpy as np 2 3class Dice_bad: 4 def __init__(self): 5 np.random.seed(0) 6 def throw(self): 7 return np.random.randint(1,100) 8 9class Dice_good: 10 def __init__(self): 11 self.rng = np.random.RandomState(seed=0) 12 def throw(self): 13 return self.rng.randint(1,100) 14 15d1=Dice_bad() 16d2=Dice_bad() 17l1, l2 = [], [] 18for _ in range(3): 19 l1.append(d1.throw()) 20 l2.append(d2.throw()) 21 22print(f'1:{l1}') 23print(f'2:{l2}') 24#1:[45, 65, 68] 25#2:[48, 68, 10] 26 27d1=Dice_good() 28d2=Dice_good() 29l1, l2 = [], [] 30for _ in range(3): 31 l1.append(d1.throw()) 32 l2.append(d2.throw()) 33 34print(f'1:{l1}') 35print(f'2:{l2}') 36#1:[45, 48, 65] 37#2:[45, 48, 65]

投稿2022/01/22 04:17

編集2022/01/22 04:21
can110

総合スコア38230

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chronobcelp

2022/02/06 15:18

共通の乱数機を共用で交互にで使うということですね。 l1, l2の数字の並びも45, 48, 65, etcと使われていますね。
guest

0

  • 本文中には「seed関数はグローバルな状態を変化させる」とありますが、
    なぜ異なる値を算出するのでしょうか?

乱数を発生させることも状態を変化させるからです。

python

1np.random.seed(0) 2print(d1.get_sum()) #34 3np.random.seed(0) 4print(d2.get_sum()) #33

をすれば同じ結果を得ることができます。

  • 「乱数を発生させることも状態を変化させるからです。」上記の意味がわかりません。

多くのコンピュータ言語で提供されている乱数は、本当の乱数ではなく疑似乱数です。
疑似乱数については乱数の仕組みなどをお読みください。
疑似乱数は内部状態を持ち、それを使って乱数を出力しながら内部状態を変えていきます。言い換えれば内部状態が変化するからこそ次に出力乱数は違う値を持つのです。
通常は、疑似乱数の初期値は現在時刻などによって設定するようになっています。

一方、疑似乱数を使うコードは実行するたびに結果が異なるため、デバッグや検証が難しくなる場合があります。
そのため、疑似乱数発生器の内部状態を設定するためにseed(種)という機能を持っている場合が多くあります。

そういう仕組みで動いているものですから、疑似乱数は乱数を発生させる毎に内部状態を変更するのです。

投稿2022/01/22 04:04

編集2022/01/22 11:42
ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chronobcelp

2022/01/22 08:17

「乱数を発生させることも状態を変化させるからです。」 上記の意味がわかりません。もう少し噛み砕いて説明していただけないでしょうか? よろしくお願いします。
ppaul

2022/01/22 11:42

回答に追加しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問