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

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

ただいまの
回答率

91.02%

  • Python

    5537questions

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

  • Python 2.7

    1098questions

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

  • NumPy

    320questions

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

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

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 507

mmm777

score 1

前提・実現したいこと

【背景】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の勝ち。
  2. Player 1がPlayer 2 の6の目の振り直しを要求
    振り直した結果、6の目が3になる。
    サイコロ(P1): 4, 5, 2, 1、最大値5
    サイコロ(P2): 3, 2、最大値3
    このままだと、Player 1の勝ち
  3. Player 2が2の目を5に変える。
    サイコロ(P1): 4, 5, 2, 1、最大値5
    サイコロ(P2): 3, 5、最大値5
    結果、相打ち

発生している問題

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

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

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

該当のソースコード

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

import numpy as np
import itertools

sai=(1,2,3,4,5,6)
sai5=np.array(list(itertools.product(sai,repeat=5)))

sai5_4=sai5[:,:4].max(axis=1)
sai5_1=sai5[:,4:].max(axis=1)

win_4 = list(sai5_4>sai5_1).count(True)
even = list(sai5_4==sai5_1).count(True)
all = len(sai5)

print win_4*100.0/all
print 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.
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mmm777

    2017/10/21 02:46

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

    キャンセル

  • karamarimo

    2017/10/21 03:31

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

    キャンセル

  • mmm777

    2017/10/21 03:38

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

    キャンセル

回答 3

checkベストアンサー

0

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

print(list(itertools.product([1,2,3,4,5,6], repeat=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)]
print(list(itertools.combinations_with_replacement([1,2,3,4,5,6], 2)))
#[(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/21 03:17

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

    キャンセル

  • 2017/10/21 03:27

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

    キャンセル

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()を使うそうです。私自身は使ったことがないので、使い方や挙動はあらかじめご確認ください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

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

プログラムは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 14:06

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

    キャンセル

  • 2017/10/20 14:36 編集

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

    キャンセル

  • 2017/10/21 03:11

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

    キャンセル

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

  • ただいまの回答率 91.02%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    [python]itertoolsの出力結果を代入

    itertoolsを使い、それをstringにしたものの出力結果すべてを順番に代入したいです。 import itertools str = "abcdefghijklmnop

  • 解決済

    2次元のリストを1次元のリストに展開する方法

    シミュレーションに使うパラメータをpythonでヘッダーありのcsvに吐き出すプログラムを作っています. 具体的には a = [1, 2] b = [3, 4] から

  • 解決済

    pythonのプログラムの高速化

    pythonのプログラムなのですが、処理にかかる時間が長く困っています。改善策があれば教えてください。 m2 = np.empty((0,80)) for x in m:

  • 解決済

    一番初めの要素が大きいもの順に並び替えたい

    一番初めの要素が大きいもの順に並び替えたいです。 total_ans =[ ( sum( [ dict( zip( a, b ) )[v] for i, v in enumer

  • 解決済

    python dataframe 結合で全組み合わせ

    python のデータフレームの結合で、2つのデータフレームの全組み合わせを取得したく、以下のような処理で行ってみました。 一応目的の全組み合わせは取得できたのですが、ダミーの

  • 解決済

    データフレームに記載された情報をもとに時間を計算したい:Python(Pandas)

    前提・実現したいこと お世話になります。 「ある地点からある地点までかかった時間を人ごとに出力したい」ということをしたいです。 具体的にはこんな感じです xさん、yさ

  • 解決済

    Python 内包表記

    jupyter notebookでとある数列を求めるプログラムを作りました。 for文の中にあるfor文(for j in range(compare.size + 1)...)を

  • 解決済

    python 行列式の計算

    pythonのnumpyによる行列式の計算で、 |123|   |234| |456|   |567| |789|   |891| のように、9の9乗通りの計算を自動で出来る簡単な

同じタグがついた質問を見る

  • Python

    5537questions

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

  • Python 2.7

    1098questions

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

  • NumPy

    320questions

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