HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2022
0709
NOiA総数を1通りずつ確認するプログラムを書きたい8解決


NOiA

リンク

2022/7/9(Sat) 23:07:28|NO.96771

前にサイコロ10個を振った場合において、
特定の組合わせが何通り出るかチェックしようとした時に、

1.ダイス10コから各出目検出 2.求めている組合せと合うかチェック(ここが長いので文章のみにしてます) 3.ダイス1つの出目を1つ進めて、   全部確認するまで(1111111111→6666666666)1.に戻る
というhspプログラムを書いて検証したんですが、
その時の組合わせ総数が 60,466,176通り で、
実行してかかった処理時間が約12時間でした。

そして今度は、
トランプ1組52枚から5枚引いた時の
各ポーカー役(役なし含む)が揃うのがそれぞれ何通りか検証したいんですが、
5枚引くとなると
52×51×50×49×48= 311,875,200通り、
カード重複を弾くにしても、プログラム上全通り検証する事になるので、
     52の5乗= 380,204,032通り。
サイコロ10個の時と同じような順番で
プログラムを書くとすると・・・予想処理時間が約75.5時間。
何か処理時間を短縮出来るようなプログラムの書き方ってありますか?



この記事に返信する


zrs90(5さい)

リンク

2022/7/10(Sun) 00:19:27|NO.96772

hsp3 計算処理速度 で検索
関連するスレッドが何例か
サイト内検索で引っかかってきます。
一度スレッドを見てみると良いかと。

このプラグインを使えばかなり
高速化出来るはず...
ただし、普通のhsp3の文法等と
違うはず?なので、それをプログラムする
技量は必要と思います。

http://hsp.tv/play/pforum.php?mode=pastwch&num=91896


後、動作保証が出来ない、互換性の問題が出るので
反則技になりますが、openhsp から
hsp3自体の再コンパイル
(※vc++のバージョンを上げる、高速なコードを
吐くcコンパイラに変える等)と言う手段もあります。



kinokawa

リンク

2022/7/10(Sun) 01:36:29|NO.96773

サイコロについては10進数と6進数で相互変換をすれば
高速化できるような気がします



zrs90(5さい)

リンク

2022/7/10(Sun) 02:22:22|NO.96774

#96772で紹介した、youdai さんの hpページですが
最近、下記に移動していました。
お手数ですが、こちらから探して下さい。

http://youdaizone.starfree.jp/


追記。
cとかのソース探せば、この手のアルゴリズムは
ありそうな気がするんですが...私、頭が良くないので。

何処で見たか忘れましたが、このプラグイン
を使ったプログラムは条件が合えば
cよりも速いそうです。

※動作にはOpenCLに対応した
グラフィックボードがないといけません。



沢渡

リンク

2022/7/10(Sun) 10:38:52|NO.96775

1番目のサイコロの件ですが、これはたとえば
「1が4つ、2が3つ、3が2つ、6が1つ出れば当たり」というように最初に決めておき、
「その当たりになるパターンの数は60466176パターン中いくつか?」
というのを求めたいということでしょうか?
「当たりの組み合わせ」のデータを一つの変数にまとめ、
それを元に当たったか否かの判定を行うというやり方でやってみました。
(私の環境だと1分ちょっとで処理が終わります)

#include "winmm.as" TimeGetTime : s=stat //「『当たり』の組み合わせ」データの作成 dim cor0,6 //各要素はそれぞれの目がいくつずつ出れば当たりになるかを表す。合計で10でなければならない。 //cor0の内容をランダムで決める場合。 //randomize : repeat 10 : cor0(rnd(6))++ : loop //cor0の内容を任意のものにする場合。 cor0=4,3,2,0,0,1 //「当たりの組み合わせ」データを1つの変数にまとめる cor=0 //それぞれの目について4bitずつ使用。合計24bit repeat 6 cor|=cor0(5-cnt) if cnt=5 : break cor<<=4 loop //本編 num=0 repeat 60466176 x=cnt : y=0 repeat 10 y+=1<<(x\6*4) x/=6 loop num+=(y=cor) loop //処理終了 TimeGetTime : s=stat-s repeat 6 mes str(cnt+1)+"が"+str(cor0(cnt))+"回" loop mes "出る組み合わせは、60466176種類中"+str(num)+"種類です。" mes "処理時間:"+str(s)+"ミリ秒"



沢渡

リンク

2022/7/10(Sun) 11:46:24|NO.96776

次にポーカーの件ですが、まず最初に、
>カード重複を弾くにしても、プログラム上全通り検証する事になるので、
これがちょっと気になるのですが、これは
「最初に、カードが重複しているかどうかをチェックし、
 重複していたら『ありえないケース』としてスキップするつもり」ということですか?
それとも「重複するケースもあり得る」ということですか?
後者であればかなり話は厄介になりますが、以下、
「カードの重複はありえない」ことを前提に話を進めます。

52枚のカードから5枚を選んだ時の順列の数は311,875,200ですが、
1つの組み合わせにつき5×4×3×2×1=120通りの順列が存在するので、
組み合わせの数は311,875,200÷120=2,598,960となり、
このおよそ260万種類についてだけ検証すれば良いということになります。

以下、やってみました。
私の環境だと7秒くらいでした。

#define repeat2(%1,%2) repeat (%2)-(%1)+1,%1 //cntの初期値が%1、最終値が%2になるrepeat //以下、次のようにルールを決める。 //・52枚のカードには0〜51の通し番号を振る。 //・通し番号を13で割った余りがカードの数字(0なら"A"、1なら"2"…12なら"K") //・通し番号を13で割った結果がカードのスート(0がスペード、1がクラブ、2がハート、3がダイヤ) #module #define ctype rank(%1) ((%1)\13) #define ctype suit(%1) ((%1)/13) //por_hand(p1) //p1は要素数5の整数変数で、それぞれに0〜51のカード番号を入れる。 //ポーカーの手役の判定を行い、その結果を以下のように返す // 0:ノーペア 1:ワンペア 2:ツーペア 3:スリーカード 4:ストレート // 5:フラッシュ 6:フルハウス 7:フォーカード 8:ストレートフラッシュ 9:ロイヤルフラッシュ #defcfunc por_hand array hand //フラッシュ判定 f_flag=1 //フラッシュフラグ x=suit(hand(0)) repeat 4,1 if suit(hand(cnt))!=x : f_flag=0 : break loop //以下、スートは判定に関係なくなるので、数字だけを基準に処理を行う dim ranks,5 repeat 5 : ranks(cnt)=rank(hand(cnt)) : loop sortval ranks,0 //ranksを小さい順にソート。この結果、同じ数字は隣接することになる。 //ストレート判定 s_flag=1 //ストレートフラグ x=ranks(0) repeat 4,1 if ranks(cnt)!=x+cnt : s_flag=0 : break loop if s_flag { //ストレートが成立した場合 if f_flag : return 8 //フラッシュが同時成立した場合はストレートフラッシュ return 4 //そうでない場合はストレート } //ロイヤルストレート(10・J・Q・K・Aの特殊ストレート)判定 if (ranks(0)=0) & (ranks(1)=9) & (ranks(2)=10) & (ranks(3)=11) & (ranks(4)=12) { if f_flag : return 9 //フラッシュが同時成立した場合はロイヤルフラッシュ return 4 //そうでない場合はストレート } if f_flag : return 5 //フラッシュ //この時点で残る可能性は、ノーペア、ワンペア、ツーペア、スリーカード、フルハウス、フォーカード dim pairs,4 //隣接しているカード同士が同じ数字かどうか pair_num=0 //その数 repeat 4 if ranks(cnt)=ranks(cnt+1) { pairs(cnt)=1 pair_num++ } loop switch pair_num case 1 return 1 //ペアが1つだけならワンペア swbreak case 2 //隣接カード同士のペアが2つの場合、スリーカードかツーペア if (pairs(0)&pairs(1)) | (pairs(1)&pairs(2)) | (pairs(2)&pairs(3)) : return 3 return 2 swbreak case 3 //隣接カード同士のペアが3つの場合、フォーカードかフルハウス if (pairs(0)=0) | (pairs(3)=0) : return 7 //ペアが左か右に寄っていたらフォーカード return 6 //そうでなければフルハウス swbreak swend return 0 #global #include "winmm.as" TimeGetTime : s=stat dim res,10 dim hand,5 x=0 repeat2 0,47 hand(0)=cnt repeat2 hand(0)+1,48 hand(1)=cnt repeat2 hand(1)+1,49 hand(2)=cnt repeat2 hand(2)+1,50 hand(3)=cnt repeat2 hand(3)+1,51 hand(4)=cnt res(por_hand(hand))++ x++ loop loop loop loop loop TimeGetTime : s=stat-s //結果表示 sdim text,64,10 text="ノーペア","ワンペア","ツーペア","スリーカード","ストレート","フラッシュ","フルハウス","フォーカード" text(8)="ストレートフラッシュ","ロイヤルフラッシュ" mes "組み合わせ総数:"+str(x) repeat 10 mes text(cnt)+":"+str(res(cnt)) loop mes "\n順列総数:"+str(x*120) repeat 10 mes text(cnt)+":"+str(res(cnt)*120) loop mes "\n処理にかかった時間:"+str(s)+"ミリ秒"
念のためWikipediaの「ポーカー・ハンドの一覧」を確認してみましたが、
正しく計算できたことが確認できました。
https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%BB%E3%83%8F%E3%83%B3%E3%83%89%E3%81%AE%E4%B8%80%E8%A6%A7



NOiA

リンク

2022/7/11(Mon) 00:49:26|NO.96782

>zrs90(5さい) さん
URLを先を見たり検索をかけたんですが、
目的に合ってると思われる物が見当たりませんでした。
(もしくは判らなかったかも)
申し訳ない。



NOiA

リンク

2022/7/11(Mon) 00:50:40|NO.96783

>kinokawa さん
サイコロ10個の出目は疑似6進数の様にして回してたので、
配列変数(10ヶ)と、変数1つで10進数→6進数エンコードなら
「配列変数の方が分かりやすくね?」
というのと、元々サイコロ9個以下の検証もできる様にしてたので、
当時相互変換は特に考えてませんでした。
抜き出すとこんな感じ。
-------前略-------
title "サイコロ "+kazu+"個の場合" dim dice,kazu ;そのダイス数だけ枠を確保 dim deme,6 ;出目の分(1〜6の目)も枠確保 ※各出目カウント用 mejirusi=kazu-1 ;整頓時に使用 mawari=0 ;整頓時に使用 --------略-------- foreach dice dice(cnt)=1 loop ;各ダイスに初期値1付与 --------略-------- dice(0)=dice(0)+1 ;1個目のダイスを進める for mawari,0,mejirusi,1 ;最後から1個手前まで処理する if dice(mawari)>6 { dice(mawari+1)=dice(mawari+1)+1 dice(mawari)=1 } next if dice(mejirusi)<7 : goto *kazoe ;最後のダイス数値が6面を超えるまで繰り返す -------後略--------



NOiA

リンク

2022/7/11(Mon) 00:52:19|NO.96784

>沢渡 さん
前半のサイコロ10個組み合わせ検証に関しては既に結果を得ていて、
処理時間のサンプルとして出してみただけなので、
こっちは改良予定はありません。やるとしても気が向いた時に。
半日に抑えられれば万々歳。

そして、ポーカー役検証に関してですが・・・
「最初に、カードが重複しているかどうかをチェックし、
 重複していたら『ありえないケース』としてスキップするつもり」
で合ってます。
カードIDを疑似52進数化して、
末端カード(5枚目/5桁目)のカードIDを1つずつ進め、
5枚目が52に到達したら00にリセットして4枚目(4桁目)を繰り上げ・・・という方式で、
複数のカード枠(複数桁)で同一IDが並んだ場合にスキップ、という組み方です。

プログラムサンプルありがとうございます。
マクロを使う事は全然頭にありませんでしたし、
正直完全には読み解けてはいないんですが、
組み合わせ数から総数に戻す手法を取ればそれだけで
超圧縮になると判ったので、ガッツリ参考にさせて頂きます。



ONION software Copyright 1997-2023(c) All rights reserved.