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

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

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

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python

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

Q&A

解決済

3回答

1043閲覧

Pythonでサイコロの目を比較するゲームの勝利確率の導出

mmm777

総合スコア7

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python

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

0グッド

0クリップ

投稿2017/10/20 03:14

編集2017/10/20 17:39

###前提・実現したいこと
【背景】Python2.7で、サイコロを使ったボードゲームの勝率を計算しようとしています。

【前提】ゲームは、プレイヤー同士が、指定された数のサイコロ(1~4個)を振り、
その最大値を比較して、相手より大きければ勝ち、同値なら相打ち、小さければ負けとなります。

サイコロの出目を操作したり、振り直しをする効果が有るため、
効果による勝率の変化を算出する場合、出目を保持ことが必要です。

ゲーム進め方の例:

  • Player 1(P1): ダイス4個、相手のダイスを選んで1つ振り直すことが出来る。
  • Player 2(P2):ダイス2個、自分のダイスを選んで目を5に変えることが出来る。
  1. ダイス比較

サイコロ(P1): 4, 5, 2, 1、最大値5
サイコロ(P2): 6, 2、最大値6
このままだとplayer 2の勝ち。

  1. Player 1がPlayer 2 の6の目の振り直しを要求

振り直した結果、6の目が3になる。
サイコロ(P1): 4, 5, 2, 1、最大値5
サイコロ(P2): 3, 2、最大値3
このままだと、Player 1の勝ち

  1. Player 2が2の目を5に変える。

サイコロ(P1): 4, 5, 2, 1、最大値5
サイコロ(P2): 3, 5、最大値5
結果、相打ち

###発生している問題
【問題】numpyのitertools.productを使って、プレイヤー同士が振るサイコロの出目の
一覧を作り、勝利確率を計算しました。ただこれだとサイコロ数が多くなったり、
振り直しが必要な場合は、計算時間が膨大になります。

【どうしたいか】効率化したいです。

【現状】サイコロは区別しないので、組み合わせと、その確率が得られれば
効率化できそう。だが、関数や良い方法が思い浮かんでいません(見つけられていない)。

###該当のソースコード
サイコロ4つ振りと、サイコロ1つ振りのデカルト積を得て、
出目の最大値を比較しています。

Python

1import numpy as np 2import itertools 3 4sai=(1,2,3,4,5,6) 5sai5=np.array(list(itertools.product(sai,repeat=5))) 6 7sai5_4=sai5[:,:4].max(axis=1) 8sai5_1=sai5[:,4:].max(axis=1) 9 10win_4 = list(sai5_4>sai5_1).count(True) 11even = list(sai5_4==sai5_1).count(True) 12all = len(sai5) 13 14print win_4*100.0/all 15print even*100.0/all

結果はただしそう。

70.7433127572 16.6666666667

###試したこと
numpyのintertoolsの使い方は一通り読んだ。
順列、組み合わせの数を出す関数は見当たらない。

###補足情報(言語/FW/ツール等のバージョンなど)
上記の環境

  • Python 2.7.13 |Anaconda 4.3.1 (64-bit)| (default, Dec 20 2016, 23:09:15)
  • IPython 5.1.0 -- An enhanced Interactive Python.

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

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

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

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

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

karamarimo

2017/10/20 06:13 編集

「サイコロの出目を操作したり、振り直しをする効果が有るため、 効果による勝率の変化を算出する場合、出目を保持ことが必要」とはどういうことでしょうか。また一覧を作ってカウントするのではなく直接場合の数を計算するのはいけないのでしょうか。
Lhankor_Mhy

2017/10/20 08:39

『サイコロの出目を操作したり、振り直しをする効果』とありますが、これはマイナス効果(1,2,5,6を1個振り直させて1,2,5,3にする等)や、範囲を超える効果(1,2,5,6に+1して2,3,6,7にする等)もあり得るのでしょうか?
mmm777

2017/10/20 17:43

マイナス効果(1,2,5,6を1個振り直させて1,2,5,3にする等)や、範囲を超える効果(1,2,5,6に+1して2,3,6,7にする等)は、共にあります。
mmm777

2017/10/20 17:46

質問がわかりにくくて申し訳ありません。ゲームの進め方を例として追記しました。
karamarimo

2017/10/20 18:31

Player1がPlayer2の最大の目のサイコロを振り直させるのか、ランダムに振り直させるのかによって確率が変わりそうですね。
mmm777

2017/10/20 18:38

確かにかわりますね。うまく負けて総合的に優位に立つなどの戦略もあるかもしれません。ただ、話が複雑になるのでPlayerはシンプルに勝ちに行く(最大値を振り直させる)想定です。
guest

回答3

0

順列や組み合わせの数の計算結果を求める関数は、scipyにあるそうです。
以下は参考記事です。

https://funmatu.wordpress.com/2017/05/24/python%E3%81%A7%E7%9B%B4%E7%A9%8D%EF%BC%8C%E9%A0%86%E5%88%97%EF%BC%8C%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%EF%BC%8C%E9%9A%8E%E4%B9%97%E3%81%AE%E8%A8%88%E7%AE%97/

具体的には、順列:scipy.special.perm()、組み合わせ:scipy.special.comb()/scipy.misc.comb()を使うそうです。私自身は使ったことがないので、使い方や挙動はあらかじめご確認ください。

投稿2017/10/27 13:55

R.Shigemori

総合スコア3376

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

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

0

ベストアンサー

条件がいろいろ気になるところですが、『サイコロは区別しないので、組み合わせと、その確率が得られれば効率化できそう』というところは、itertools.combinations_with_replacementを使うといいのかもしれません。

python

1print(list(itertools.product([1,2,3,4,5,6], repeat=2))) 2#[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6)] 3print(list(itertools.combinations_with_replacement([1,2,3,4,5,6], 2))) 4#[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (3, 3), (3, 4), (3, 5), (3, 6), (4, 4), (4, 5), (4, 6), (5, 5), (5, 6), (6, 6)]

とはいえ、直積の半分程度になるだけですし、計算量のオーダー的には変わらないでしょうねえ……

投稿2017/10/20 11:24

編集2017/10/20 11:24
Lhankor_Mhy

総合スコア35860

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

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

mmm777

2017/10/20 18:17

ありがとうございます。この、itertools.combinations_with_replacementでそれぞれの組み合わせの数が得られるとありがたいのですが、難しそうですね。 例えば、下記の様な結果を得たいです。 (1,1)の組み合わせは、1つ (1,2)の組み合わせは、2つ
karamarimo

2017/10/20 18:27

例えば(1,2,2,2,3,3)の場合、1が1個、2が3個、3が2個あるので順列の数は 6!/(1!3!2!)=60 と計算できます。一般には 6!/((1の個数)!(2の個数)! ... (6の個数)!) で計算できますね。
guest

0

勝負毎にサイコロを振って、結果を保存すればよいでしょう。
サイコロが出る可能性をあらかじめ列挙する必要はありませんし、組み合わせの計算も不要です。

プログラムはMATLABで書いていますが、まあ、わかると思います。Pythonに翻訳してください。
引き分けの場合は再勝負させています。


n=1000;%勝負回数
n_sai_1 = 4;%player1のサイコロ数
n_sai_2 = 1;
player_1_records =[];%player1の出目記録用
player_2_records =[];
winner_records = [];%勝敗:1ならplayer1の勝利、2ならplayer2の勝利
i=1;
while i<=n
player_1_result=randi(6,1,n_sai_1);%1から6までの自然するからランダムにn_sai_1個選ぶ
player_2_result=randi(6,1,n_sai_2);
if max(player_1_result) == max(player_2_result) %引き分けなら再勝負
continue
end
player_1_records=[player_1_records;player_1_result];
player_2_records=[player_2_records;player_2_result];
result = 1+ (max(player_1_result) < max(player_2_result));
winner_records = [winner_records; result];
i = i+1;
end

[sum(winner_records==1)/n , sum(winner_records==2)/n]

追加:
引き分けも勝負回数に含める場合はfor を使います。
n=1000;%勝負回数
n_sai_1 = 4;%player1のサイコロ数
n_sai_2 = 1;
player_1_records =[];%player1の出目記録用
player_2_records =[];
winner_records = [];%勝敗:1ならplayer1の勝利、2ならplayer2の勝利

for i=1:n
player_1_result=randi(6,1,n_sai_1);%1から6までの自然するからランダムにn_sai_1個選ぶ
player_2_result=randi(6,1,n_sai_2);
if max(player_1_result) == max(player_2_result) %引き分けなら再勝負
result = 0
else
result = 1+ (max(player_1_result) < max(player_2_result));
end
player_1_records=[player_1_records;player_1_result];
player_2_records=[player_2_records;player_2_result];
winner_records = [winner_records; result];
end

[sum(winner_records==1)/n , sum(winner_records==2)/n, sum(winner_records==0)/n]

投稿2017/10/20 04:30

編集2017/10/20 05:55
WathMorks

総合スコア1582

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

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

Lhankor_Mhy

2017/10/20 05:06

モンテカルロ法で確率を導出するには試行回数が少なすぎると思いますし、そもそもモンテカルロ法を用いるのは適切ではないと思ったのでマイナス評価しました。
WathMorks

2017/10/20 05:36 編集

試行回数は適当に設定しました。少なければ増やせばいいだけの話です。 「シミュレーションを効率的に実行したい」と理解したので上記のように回答しました。
mmm777

2017/10/20 18:11

回答ありがとうございます。サイコロの目のような離散型の確率工程の場合、モンテカルロ法だと十分な計算精度を出すための施行数が、全組み合わせを求めるよりも圧倒的に多くなってしまうと思われます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問