2011年9月10日

麻雀の確率 ( vol.1 一色麻雀で天和の確率を )

麻雀を覚えた 1986年、当時出ていた関連の書籍を新刊・古本あわせて45冊集めて読んだ。

その中に、コンピュータで14枚の牌姿をすべてのケースを示すと 何通り。そのうちに和了の形がいくつ。よって天和の確率はいくつ、と書いてある本があった。

署名は忘れた。既に手放したから、私が読んだときに理解を間違えて、そのまま20年以上を過ごしてきたのかもしれない。


最近、清一にしようとやみくもに 13枚同じ色を集めたときに、聴牌である確率はどれくらいかと考え出した。頭の体操にほどよい問題と思った。

9種類4枚ずつの牌から13枚抜き出して、その形から、聴牌のものを抜き出してケースを数えたら良いのかと思い、数えるプログラムを二、三日考えていた。


ちょっと待て。違うではないか。すべての牌姿は、同じ確率では出現しない。

一色36枚から 4枚を抜き出したときに、たとえば 1111 である確率は、1234 である確率の 256 分の 1 だ。

詳しく言えば、ある牌姿のなかに同じものが4枚あるとしたら、その部分については 1通りしかない。3枚だとしたら 4通り。2枚だと 6通り、1枚だと 4通り。


上記の本を読んだ18歳のときに、麻雀を考える時には、量子力学のように、ふたつの同種の牌を同じに確率計算するのかと感じて、感じながらなぜかそのまま受け入れていたのよね。

そんなバカなわけはない。一筒四枚に花・鳥・風・月と書いてあれば、2枚が手の中にある場合の数は 花鳥・花風・花月・鳥風・鳥月・風月の6通りで間違いない。

バカだねぇ。> 18歳のわたし


一色9種36枚の麻雀牌から14枚を取ったときの確率を計算するためのすべてのケースは、( 36 × 35 × 34 × ... × 23) ÷ ( 14 × 13 × 12 × ... 1) 通り。だいたい2.66 × 10の11乗 ( 266億 ) として間違いはない。

ここで Perl スクリプトをでっち上げて計算してみた。とりあえず一色手を計算するには十分というだけの、汚いコードならば 90分でできた。実行時間は 0.5秒。

和了の牌姿は、 13277 通り、 445632532 ケース。

2.66 × 10 の11乗 のうちの、4.46×10の9乗だから、一色麻雀だとだいたい 60回に一回の天和になるのか。うん、昔一色手の練習で二人麻雀をしていた時の感覚とだいたいあってる。


同じロジックで、力技で 34種類の牌を使ったふつうの麻雀の天和の確率も計算は可能。力技のコード書くのは面倒くさいし汚いから省略。

もうちょっと綺麗に書いたコードを公開するときがきたら、結果の照合に書いてしまうかもしれないが。

そして、この一色手のすべての和了形を出力したものから、一枚引いて重複を省くことで、清一の聴牌形をすべて抜き出そうと思っている。

参考 : 使用したコード

!/usr/bin/perl
#
#   allcaasecount.pl      2011-09-10 yaemon
#
use strict;
use warnings;
use bignum;
my $all = ( 36 * 35 * 34 * 33 * 32 * 31 * 30 * 29 * 28 * 27 * 26 * 25 * 24 * 23) / ( 14 * 13 * 12 * 11 * 10 * 9 * 8 * 6 * 5 * 4 * 3 * 2 * 1 ) ;
print $all , "\n";
#!/usr/bin/perl
#
#   OneColorMahjongCases.pl      2011-09-10 yaemon
#
use strict;
use warnings;
use bignum;
sub merge( $$ ); sub cases( $ );
my @mentz;
push @mentz , [ 3 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 3 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 3 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 3 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 3 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 0 , 3 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 ]; push @mentz , [ 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 ]; push @mentz , [ 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 ];
my @head; push @head , [ 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @head , [ 0 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @head , [ 0 , 0 , 2 , 0 , 0 , 0 , 0 , 0 , 0 ]; push @head , [ 0 , 0 , 0 , 2 , 0 , 0 , 0 , 0 , 0 ]; push @head , [ 0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 , 0 ]; push @head , [ 0 , 0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 ]; push @head , [ 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 , 0 ]; push @head , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 ]; push @head , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 ];
my %answer;
my $test = 0; for ( my $i = 0 ; $i < @mentz ; $i++ ) { for ( my $j = $i ; $j < @mentz ; $j++ ) { my $twomentz = merge( $mentz[$i] , $mentz[$j] ); if ( $twomentz ) { for ( my $k = $j ; $k < @mentz ; $k++ ) { my $threementz = merge( $twomentz , $mentz[$k] ); if ( $threementz ) { for ( my $l = $k ; $l < @mentz ; $l++ ) { my $fourmentz = merge( $threementz , $mentz[$l] ); if ( $fourmentz ) { for( my $m = 0 ; $m < @head ; $m++ ) { my $hand = merge( $fourmentz , $head[$m] ); if ( $hand ) { my $tmp = join( "" , @$hand ); if( ! exists( $answer{ $tmp } ) ) { $answer{ $tmp } = cases( $hand ); } } } } } } } } } }
# 七対子を忘れていた。ここは手抜きする my $sevenpairscase = 6**7; for ( my $i = 0 ; $i < 9 ; $i++ ) { for ( my $j = $i + 1 ; $j < 9 ; $j++ ) { my @hand; for ( my $k = 0 ; $k < 9 ; $k++ ) { if ( $k == $i || $k == $j ) { push @hand , 0; } else { push @hand , 2; } } my $tmp = join( "" , @hand ); if( ! exists( $answer{ $tmp } ) ) { $answer{ $tmp } = $sevenpairscase; } } }

my $num = keys( %answer ); print "All cases is $num\n"; my $cases = 0; while ( ( my $hand , my $case ) = each( %answer ) ) { $cases += $case; print $hand , "\n"; } print "\nall cases is $cases\n";

sub merge( $$ ) { my $a = shift; my $b = shift; my @ret; for( my $i = 0 ; $i < 9 ; $i++ ) { my $tmp = $$a[$i] + $$b[$i]; if ( $tmp > 4 ) { return undef; } $ret[$i] = $tmp; } return \@ret; }
sub cases( $ ) { my $u = shift; my $case = 1; for ( my $i = 0 ; $i < 9 ; $i++ ) { if ( $$u[$i] == 0 ) { next; } elsif ( $$u[$i] == 1 ) { $case *= 4; } elsif ( $$u[$i] == 2 ) { $case *= 6; } elsif ( $$u[$i] == 3 ) { $case *= 4; } elsif ( $$u[$i] == 4 ) { next; } } return $case; }

0 コメント:

コメントを投稿