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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

1回答

852閲覧

Pythonを用いたガチャシミュレーションの速度向上について

Avent

総合スコア47

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2023/01/02 21:42

前提

Pythonを使ってソーシャルゲームのガチャのシミュレーションを行いたいのですが、単純に random.random() をfor文で繰り返し行うのでは1億回の計算で47秒かかってしまいました。
更に精度を上げたいですが、これ以上は10億回で10分、100億回で100分と、指数関数的に計算時間が増えてしまうため、なにか良い案は無いかと思い質問させていただきました。

実現したいこと

random.random() の繰り返しによるソーシャルゲームのガチャのシミュレーションプログラムの計算速度向上。

何かライブラリを使用して高速化する案、プログラムの無駄を指摘して高速化する案などをお答えいただきたいです。

せめて1000億回程度を数分以内に行えるようにと考えていますが、早ければ早いほど良いです。

シミュレーションのコード

Python

1from random import random 2 3from tqdm import tqdm 4 5 6RATE4 = 0.051 7RATE4UP = 0.511 8RATE5 = 0.006 9RATE5UP = 0.324 10 11 12class Wish(): 13 def __init__(self) -> None: 14 self.count4 = 0 15 self.count5 = 0 16 17 def wish(self): 18 return self.get_rarity() 19 20 def get_rarity(self): 21 self.set_th() 22 23 th = random() 24 if th < self.th5: 25 self.count4 += 1 26 self.count5 = 0 27 return 5 28 elif th < self.th4: 29 self.count4 = 0 30 self.count5 += 1 31 return 4 32 else: 33 self.count4 += 1 34 self.count5 += 1 35 return 3 36 37 def set_th(self): 38 if self.count5 < 75: 39 self.th5 = RATE5 40 elif self.count5 < 89: 41 self.th5 = RATE5UP 42 else: 43 self.th5 = 1 44 45 if self.count4 < 8: 46 self.th4 = self.th5 + RATE4 47 elif self.count4 < 9: 48 self.th4 = self.th5 + RATE4UP 49 else: 50 self.th4 = 1 51 52 53wish = Wish() 54 55count5 = 0 56count4 = 0 57 58COUNT = 100000000 59for _ in tqdm(range(COUNT)): 60 result = wish.wish() 61 if result == 5: 62 count5 += 1 63 elif result == 4: 64 count4 += 1 65 66print('★4: ' + str(count4 * 100 / COUNT) + '%') 67print('★5: ' + str(count5 * 100 / COUNT) + '%') 68

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

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

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

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

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

Demerara

2023/01/03 08:29 編集

出力される値は毎回ほぼ同じですがどういった意図がありますか?このコードにどんな挙動、及び結果を期待しますか? 場合によっては、random.choices() や random.sample() あたりが使えそうです。それを非同期 (asyncio) で回せばそれなりに速くなるような気がします。 追記:よくよく考えたら非同期にするとタスクを一時的に保存しておくメモリが足りなくなりますね。numpy 使っても条件は変わらないし、下のお二方の言う通りコンパイル言語を使うのが現実的な気がします。
melian

2023/01/03 04:49

他のプログラミング言語(C, C++, Rust などのコンパイラ)で実装してみてはどうでしょうか。例えば、質問にある Python コードでは80秒、Golang version 1.20pre では 2.4秒でした。
meg_

2023/01/03 04:51

速くしたいのであれば、コンパイル言語を使ってみるのはどうでしょうか?
guest

回答1

0

面白い質問だったので、Pythonでどんなものか試しました。
結果、概算3%の時間(x34くらい)で処理できました。

条件秒数
Numbaなし452秒
Numbaあり13秒ちょい

Pythonだけで高速化するポイントは、

  • Numbaを使えば雑なやり方でも早くできる
  • Numbaで処理するには、Numbaで捌きたい関数(do_it())の中身(Wish)もNumbaで捌ける形でなければならない。そうしないとコケる。
  • Numpyの普段使いうる関数はNumbaでサポートしているが、tqdmの様なモジュールには対応していない。tqdmのようなモジュールはやむを得ずコメントアウトするのが望ましい。

やっていないのに生意気なことを言うなら、

  • ちゃんと並列化するともっとはやいはず(例えばThreading.threadで4つThreadを使って、全量の1/4ずつしょりなど)

Python3

1from numba import jit, f4, f8, i8 2from numba import jitclass 3 4from random import random 5 6from tqdm import tqdm 7import time 8 9spec = [ 10 ("count4", i8), 11 ("count5", i8), 12 ("RATE4", f4), 13 ("RATE4UP", f4), 14 ("RATE5", f4), 15 ("RATE5UP", f4), 16 ("th4", f8), 17 ("th5", f8), 18 19 20 ] 21 22@jitclass(spec) 23class Wish(): 24 25 def __init__(self) -> None: 26 self.count4 = 0 27 self.count5 = 0 28 29 ############################## 30 self.RATE4 = 0.051 31 self.RATE4UP = 0.511 32 self.RATE5 = 0.006 33 self.RATE5UP = 0.324 34 35 def wish(self): 36 return self.get_rarity() 37 38 def get_rarity(self): 39 self.set_th() 40 41 th = random() 42 if th < self.th5: 43 self.count4 += 1 44 self.count5 = 0 45 return 5 46 elif th < self.th4: 47 self.count4 = 0 48 self.count5 += 1 49 return 4 50 else: 51 self.count4 += 1 52 self.count5 += 1 53 return 3 54 55 def set_th(self): 56 if self.count5 < 75: 57 self.th5 = self.RATE5 58 elif self.count5 < 89: 59 self.th5 = self.RATE5UP 60 else: 61 self.th5 = 1 62 63 if self.count4 < 8: 64 self.th4 = self.th5 + self.RATE4 65 elif self.count4 < 9: 66 self.th4 = self.th5 + self.RATE4UP 67 else: 68 self.th4 = 1 69 70@jit() 71def do_it(): 72 wish = Wish() 73 74 count5 = 0 75 count4 = 0 76 77 COUNT = 100000000 78 # for _ in tqdm(range(COUNT)): 79 for _ in range(COUNT): 80 result = wish.wish() 81 if result == 5: 82 count5 += 1 83 elif result == 4: 84 count4 += 1 85 return count4, count5,COUNT 86 87time_start = time.time() 88count4 ,count5,COUNT = do_it() 89print("ELAPSED:",time.time()-time_start ,"[sec]") 90print('★4: ' + str(count4 * 100 / COUNT) + '%') 91print('★5: ' + str(count5 * 100 / COUNT) + '%') 92

投稿2023/01/04 01:01

編集2023/01/04 01:03
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問