Quantcast
Channel: 円周率近似値の日に生まれて理系じゃないわけないだろ! - knifeのblog
Viewing all articles
Browse latest Browse all 5376

52人ババ抜きの確率を考える -その3-

$
0
0

[ 1 2 3 4 ]

その1では数学的に確率を求めてみました。
その2ではプログラミングの大まかな構想を練ってみました。
その3では、その1で求めた確率が正しかったのかを検証するためのプログラムを組んでみます。

そのために組んだプログラムはこれ。

baba52.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define CARD_MAX 53
#define PLAYER_MAX 52
#define JOKER 0
#define LOOP 53*52*51
#define PROBABILITY_MAX 100

int deck[CARD_MAX];
int a1, a2, b, flag=1;
unsigned long long int prob[PROBABILITY_MAX];
unsigned long long int count=0;

void initial()
{
    int i;

    srand((unsigned)time(NULL));
    for (i=0; i<PROBABILITY_MAX; i++) prob[i]=0;
    deck[0] = 0;
    for (i=0; i<CARD_MAX-1; i++) stock[i+1]=i%13+1;
}

void shuffle()
{
    int i, j, k;

    for (i=0; i<CARD_MAX-1; i++) {
        j = rand()%(CARD_MAX-i)+i;
        k = deck[i];
        deck[i] = deck[j];
        deck[j] = k;
    }
}

void next_player()
{
    do {
        b = b%PLAYER_MAX+1;
    } while ( flag*deck[b] < 0 );
    count++;
}

void game()
{
    int i, j, k;

    shuffle();
    a1=1;
    a2=0;
    b=2;
    i=0;
    j=0;
    while ( 1 ) {
        if ( deck[a1]==deck[a2] ) {
            if ( i<PROBABILITY_MAX && j==0 ) prob[i]++;
            deck[a1] *= -1;
            deck[a2] *= -1;
            a2 = b;
            next_player();
            j++;
        }
        a1 = b;
        next_player();
        if ( a1 == b ) {
            count--;
            break;
        }
        if ( rand()%2 ) {
            k = deck[a2];
            deck[a2] = deck[a1];
            deck[a1] = k;
        }
        i++;
    }
    flag *= -1;
}

int main(int argc, char *argv[])
{
    int i,j,k;

    k = 1;
    if ( argc == 2 ) {
        k = atoi(argv[1]);
    }
    initial();
    for (j=1; j<=k; j++) {
        printf("%d/%d", j, k);
        for (i=0; i<LOOP; i++) {
            game();
        }
        printf("\tcount=%llu\n", count);
    }
    printf("-----finish-----\n");
    for (i=0; i<PROBABILITY_MAX; i++) printf("%llu\n", prob[i]);

    return EXIT_SUCCESS;
}


gccのコンパイルオプションは、

gcc baba52.c -O3 -obaba52.exe


実行時のパラメータとして、


第一引数に数値を入れるとその基準値×数値分だけループします。

baba52 1

と実行すると、53*52*51*1=140556回ループします。

統計学的にみて、どれくらいの数をやれば、どれくらいの精度が出るのか、私は知りません。
私のボロパソコンで10000を与えると、丸一日くらいかかりますw。

さて、プログラムを順に追っていきましょう。

initialルーチンでは、乱数のシードの初期化、deck配列の初期化をしています。
前回の構想では、普通に0から52を入れることを想定していましたが、高速化の為にスートの概念を無くし、シーケンスだけにしました。
簡単に説明すると、0が1枚(JOKER)、1~13が4枚ずつです。
このため、カードの比較のif文がかなり簡略化されます。
因みに、乱数のシードを初期化しなければ、毎回同じ結果を出力することになります。

shuffleルーチンは、構想の通り変更点はありません。

next_playerルーチンは、抜けた人を飛ばして次の人を探すルーチンです。

gameルーチンは、ババ抜きを1回行います。

出力された、countを試行回数で割れば、平均何回でババ抜きが終了するかが解ります。

53*52*51*10000回やってみたところ、790.5前後の値を出力しました。

出力の-----finish-----の後に続くテータは、その1で求めた確率で、最初の人からのべ100人分です。
こちらも確率を出すならば、Excelなどに貼り付けて、試行回数で割ってください。

さて、その1で求めた確率が正しかったのかというと、
baba52に100以上の値を与えると、
最初に2枚配られる人を1人目とすると、
1人目は5.66%
2人目は5.43%
と求めた分数が正しいだろうということが判明します。
しかし、その後がそこまで正しい値にならず、私の求めた値よりも、ちょっと多めな値になっています。
プログラム的に問題があるのか、そもそも確率の求め方に間違いがあったのか、
おそらくは前者だと思われます。

ただ、今回確実に解ったことが一つあります。

平均790.5回で52人ババ抜きが終わる。
手札を引くのに5秒掛かったとするならば、790.5×5=3952.5秒
1時間は3600秒、1分は60秒なので、1時間5分52秒5
動画では、1人上がるごとに胴上げなどして時間を掛けているので、それを考慮すれば妥当な値だったと思われます。

というわけで、その3はこのへんで。

その4では確率について、数学的にもう一度考え直ししてみたいと思います。


ではでは


Viewing all articles
Browse latest Browse all 5376

Trending Articles