【PHP】テニスの確率論

突然ですがこの前ネットでおもろい話を見つけました

テニスの確率論:http://hamakazuchan.web.infoseek.co.jp/tennis/tennis3.html

ここで言ってることは、例えばAさんがポイントを取る確率が55%、Bさんが45%で結構実力的には張り合っていても、最終的にBさんが勝てる確率は0.043%にまで落ち込んでしまうという話です。

自分はテニスをサークルでやってたんですが、確かにみんなが思う「アイツが勝つだろうな」っていう予想は結構外れない感じはありました。
要はテニスって結構番狂わせが起こりづらいスポーツなんですね。

面白そうだったのでPHPでこのアルゴリズムどんな感じになるのか書いてみました。

<?php
class Tennis
{
  function step_cul($a,$b,$c){//階乗
    $escape_zero = array($a,$b,$c);
    foreach ($escape_zero as $key){
      if ($key == 0){$key = 1;}
    }
    $a_num = $b_num = $c_num = 1;
    for($i = $a;$i == 1;$i--){
      $a_num = $a_num * $i; 
    }
    for($i = $b;$i == 1;$i--){
     $b_num = $b_num * $i; 
    }
    for($i = $c;$i == 1;$i--){
     $c_num = $c_num * $i; 
    }
    return $a_num/$b_num/$c_num;
  }
  function multiple ($point,$duce) {//乗算
    $escape_zero = array($point,$duce);
    foreach ($escape_zero as $key){
      if ($key == 0){$key = 1;}
    }
    $num_mlt = $point;
    for ($i=0;$i<=$duce-1;$i++) {
      $num_mlt =  $num_mlt * $point;
    }
    return $num_mlt;
  } 
  function probability ($duce,$b_point) {
    $a_point = 1 - $b_point;
    $ary = array();
    for ($i=0;$i<=$duce;$i++) {
      if($i == $duce){
        $ary[] = $this->multiple($b_point,$duce+2)
                 *$this->multiple($a_point,$i)
                 *$this->step_cul($duce+$i,$duce,$i);
      } else {
        $ary[] = $this->multiple($b_point,$duce+1)
                 *$this->multiple($a_point,$i)
                 *$this->step_cul($duce+$i,$duce,$i);
      }
    }
    $prob = '0';
    foreach ($ary as $key){
      $prob = $prob + $key;
    }
    return $prob;
  }
  static public function instantiate ($game) {
    $class = 'Tennis_'.$game;
    return new $class;
  }
}

class Tennis_Game extends Tennis {
  function cul(){
    return $this->probability(3,0.49);
  }
}
class Tennis_Set extends Tennis {
  function cul(){
    $game  = new Tennis_Game;
    return $this->probability(5,$game->cul());
  }
}
class Tennis_Match extends Tennis {
  function cul(){
    $set = new Tennis_Set; 
    return $this->probability(3,$set->cul());
  }
}
$tennis = new Tennis;
$game = $tennis->instantiate('Game');
$set = $tennis->instantiate('Set');
$match = $tennis->instantiate('Match');
$cul = array($game,$set,$match);
foreach ($cul as $key) {
  echo $key->cul();
}



書いといてなんなんですがこのコード不完全な点が幾つかありますw
一つはデュースになった際の扱いです。
僕はBさんが2ポイント連取してゲームを取る確率しか考えられていません。
しかし実際AさんとBさんが交互にポイントをとり続ければデュースというのは無限に続く確率があるわけで、
本当はその確率も考えなければならないはずです。
それを上記リンクのページでは無限級数の和で導いています。
もちろん僕もそれでやろうとしたのですが、純正文系バカなので途中で諦めたというw数Ⅲとかしらねーしw
プログラムをかくのが目的だったのでいっかなーみたいな言い訳もしつつ

まぁ今度ゆっくり時間をかけて再チャレンジしたいと思います。

あとセットを取るときにタイブレークを考えられていないのも不完全な点ですね・・・。
あと階乗はともかく乗算に関しては普通にPHPの関数でなんかあるような気がしてきた。


ポイントとしてはゲームを取る確率、セットを取る確率、勝利する確率を同じ計算方法でやっているという点と、その計算方法を単なる継承ではなくポリモーフィズムでやってることですかね。計算方法に関してはゲームだろうとセットだろうと何ポイント目に「デュース現象」がおこるかという話なので。あとポリモーフィズムは前から気になってたんで無理やり使った感ありますが、なんというかこういう設計のやり方にプログラマの性格でるんだろうなと思いました。
実を言うと後半の部分は多重継承で書くべきだなと思ってました。ゲーム、セット、マッチとそれぞれ前のクラスを呼んでから計算しているので。でもPHPは基本的に多重継承しちゃいけないし、まぁ形としてわかりやすいのでポリモーフィズムでいくか、みたいな。あとそもそも無理やりクラスに分けなくてもfunctionだけ作ってその都度呼べばいいんじゃねーの的な話もありますね。オブジェクト指向ガン無視で邪道中の邪道ですがw 計算の量自体は実はクラスを設定してやるよりも減る・・・かなたぶん。
あとはabstractで書くとか・・・あ。タイブレークはこれでかくといいかも・・・。

そんな感じで試行錯誤してるのがちょっと楽しかったです。

あともはやはてな記法で綺麗に見せようというやる気がない。

よし寝るか