php poker 生成的随机结果与预期不符

Posted

技术标签:

【中文标题】php poker 生成的随机结果与预期不符【英文标题】:php poker generated random results do not match what is expected 【发布时间】:2014-03-25 19:22:52 【问题描述】:

我对扑克股票进行了研究。我所做的是使用 pokerstove 程序并选择了一些。并通过该程序使用枚举所有方法计算股票。所以pokerstove结果:

我还制作了一个存储随机结果的表格:

CREATE TABLE poker_results
(
id int NOT NULL AUTO_INCREMENT,
matches_id int NOT NULL,  -- just for filtering in case I will want another starting hands.
result varchar(255) NOT NULL,
winner_hands varchar(255) NOT NULL,
PRIMARY KEY (id)
) 

结果列数据如下所示:Jd,9d,Qh,5c,Kc - 这表示板上的牌。

Winner_hands 列数据如下所示:Ks-Ac,6c-Ad,3c-Ah(可以是 1 手赢,也可以是全部 8 手赢)。

这是将结果生成到数据库的代码。它在codeigniter框架上。而且为了不复制整个 poker.php,我只是复制了几个从中使用的函数:

protected function get_probabilities($hands, $board_cards = array()) 
        $players = count($hands);

        $board = implode('', $board_cards);
        $board = trim($board);
        if (empty($board)) 
            $board = '-';
        
        $hx = '';
        $hand_counter = 1;

        foreach ($hands as $hand) 
            $hx .= '&h' . $hand_counter++ . '=' . $hand[0] . $hand[1];
        
        
        $url = 'http://' . $this->poker_server . '/?board=' . $board . $hx . '&auth=' . $this->auth;
        
        

        //Output exm. string '0.1342, 0.2042, 0.13525, 0.52635'
        //WAR!! check if alive
        $result = $this->parse_url_link($url);
        
        if (substr($result, 0, 3) == 'Err') 
            $this->utils->logging('ERROR', 'Poker server authorization failed!');
        
        
        //echo $result;

        return array(
            'hands' => $hands,
            'prob' => explode(', ', $result),
            'board' => $board_cards
        );
    


// protected because in child class needed
    protected function get_poker_winner($table_winners) 
        $to_return = array();
        foreach($table_winners['prob'] as $key => $val) 
            if ($val > 0) 
                $to_return[] = $table_winners['hands'][$key][0] . '-' . $table_winners['hands'][$key][1];
            
        
        
        return $to_return;
    

poker_tests.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');


include_once(APPPATH . 'controllers/poker.php');    
require_once APPPATH . "libraries/downloaded/Mersenne_twister.php";
use mersenne_twister\twister; 

class Poker_tests extends Poker    
    
    public function __construct() 
        parent::__construct();
    

    /**
     * Generates data in database with such structure:
     * CREATE TABLE matches
     * (
     * id int NOT NULL AUTO_INCREMENT,
     * player_cards varchar(255) NOT NULL,
     * 
     * PRIMARY KEY (ID)
     * )
     * 
     * CREATE TABLE poker_results
     * (
     * id int NOT NULL AUTO_INCREMENT,
     * result varchar(255) NOT NULL,
     * PRIMARY KEY (id)
     * ) 
     * 
     * 
     * Here 1 match will have many results, because we use same preflop cards.
     * 
     * Text results appended to pokerstove.txt
     * 
     * 376,992 games     0.013 secs  28,999,384 games/sec
     * 
     * Board: 
     * Dead:  
     * 
     *          equity      win     tie             pots won    pots tied   
     * Hand 0:  20.925%     20.53%  00.40%           77382       1504.50    KhQs 
     * Hand 1:  06.215%     03.50%  02.72%           13190      10239.00    9c4d 
     * Hand 2:  06.396%     04.08%  02.32%           15379       8734.00    8d4c 
     * Hand 3:  18.906%     18.15%  00.76%           68426       2847.50    AcKs 
     * Hand 4:  08.767%     06.91%  01.86%           26032       7019.50    9h2c 
     * Hand 5:  10.204%     09.83%  00.38%           37044       1424.00    Ad6c 
     * Hand 6:  09.046%     08.67%  00.38%           32678       1424.00    Ah3c 
     * Hand 7:  19.541%     18.08%  01.46%           68154       5514.50    8c7c 
     * 
     * 
     * ---
     * 
     * 
     */
    public function run() 

        $this->benchmark->mark('start');

        $this->output->enable_profiler(TRUE);

        //close the current connection, because its not needed
        $this->db->close();

        $db_poker = $this->load->database('poker_test', TRUE);

        $sql = "INSERT INTO poker_results (result, winner_hands, matches_id) VALUES (?, ?, ?)";

        // matches_id = 1. Insert new match
        $table8_hands = 'Kh,Qs,4d,9c,4c,8d,Ks,Ac,2c,9h,6c,Ad,3c,Ah,7c,8c';  // do test with this. Do those win the right amount of time?

        for ($i=0; $i < 400000; $i++)  // pradejus id 100194

            $flop = $this->poker_flop($table8_hands);
            $turn = $this->poker_turn($flop, $table8_hands);
            $river = $this->poker_stop($turn, $table8_hands);
            //echo json_encode($river) . '<br>';

            $db_poker->query($sql, array($river['river_board'],  implode(',', $river['winner_hands8']), 2));
        
        
        $db_poker->close();

        $this->benchmark->mark('end');
        echo $this->benchmark->elapsed_time('start', 'end') . '<br>';
    



    

    /**
     * 
     * Override - remove unneeded things for test from that function in poker.php
     * Generates 3 flop cards
     */
    public function poker_flop($table8_hands) 
            
    
        $table8_cards = explode(',', $table8_hands);
        
        $table8_results = $this->random_result($table8_cards, 3);
        
        return $table8_results;

    

    /**
     * Generates 1 turn card
     * @param $table8_hands - same as match score in database. But here we have hardcoded in run function
     * 
     */
    public function poker_turn($table8_flop, $table8_hands) 
            
            $table8_cards = explode(',', $table8_hands);
            
            //Join players cards and opened board cards         
            $table8_reserved_cards = array_merge($table8_cards, $table8_flop);

            //Pass all opened cards and get one new board card          
            $table8_results = $this->random_result($table8_reserved_cards, 1);
            
            //Merge all opened board cards
            $table8_results = array_merge($table8_flop, $table8_results);

            // this is flop and turn both           
            return $table8_results;
            
    

    /**
     * 
     * Generates 1 river card
     */
    public function poker_stop($table8_flop_turn, $table8_hands) 
            
        $table8_cards = explode(',', $table8_hands);
        
        $table8_reserved_cards = array_merge($table8_cards, $table8_flop_turn);
        
        $table8_results = $this->random_result($table8_reserved_cards, 1);
                    
        $table8_results = array_merge($table8_flop_turn, $table8_results);

        
        $table8_hands = $this->array_to_hands_array($table8_cards);
    
        $flop_turn_results = implode(',', $table8_results);

        // $this->benchmark->mark('code_start');
        //Get new probabilities - they will be needed to determine if hand has won or not. When prob. > 0 - then won
        $table8_prob = $this->get_probabilities($table8_hands, $table8_results);

        // $this->benchmark->mark('code_end');
        // echo $this->benchmark->elapsed_time('code_start', 'code_end');
        
        return array(
            'winner_hands8' => $this->get_poker_winner($table8_prob),
            'river_board' => $flop_turn_results
        );
            
    


    /**
     * for second generation - new random function
     * @param  array  $reserved_cards 
     * @param  integer $cards_amount   
     * @return array
     */
    protected function random_result($reserved_cards, $cards_amount = 5) 

        $card_types = array('s', 'c', 'h', 'd');
        $card_values = array('A', 2, 3, 4, 5, 6, 7, 8, 9, 'T', 'J', 'Q', 'K');

        $deck = array();
        foreach ($card_values as $value) 
            foreach ($card_types as $type) 
                $deck[] = $value . $type;
            
        

        $remaining_deck = array_diff($deck, $reserved_cards);
        // make keys sequence:
        $remaining_deck = array_values($remaining_deck);

        $results = array();

        while (count($results) != $cards_amount)           

            $rand_card_key = $this->random(0, (count($remaining_deck) - 1));
            
            $results[] = $remaining_deck[$rand_card_key];

            // remove from deck         
            unset($remaining_deck[$rand_card_key]);
            // make keys sequence:
            $remaining_deck = array_values($remaining_deck);
        
        
        return $results;
    


    /**
     * Picks random element from range
     * @param  integer $from        
     * @param  integer $to          
     * @return integer              
     */
    private function random($from, $to) 

        if (file_exists('/dev/urandom')) 
            $twister4 = new twister;
            $twister4->init_with_file("/dev/urandom", twister::N); 

            return $twister4->rangeint($from, $to);
        
        else 
            return mt_rand($from, $to); 
        
        

正如我们在随机函数中看到的 - 我在 linux 上进行测试时使用带有 linux dev/urnadom 的 twister 库,而在 Windows 上进行测试时使用本机 mt_rand。没有注意到差异。

所以要选择结果,我使用这样的查询:

获取结果总数

select count(*) from poker_results where matches_id = 2 and id < 296351 

要获得手牌的总赢(赢 + 平):

select count(*) from poker_results where matches_id = 2 and id < 296351 and winner_hands like '%Kh-Qs%' 

要获得多少手牌:

select count(*) from poker_results where matches_id = 2 and id < 296351 and winner_hands like '%Kh-Qs%' and winner_hands != 'Kh-Qs'

扑克服务器用于获取手牌赢率。通过河牌的手牌赢率,我确定手牌是否赢 - 当手牌赢率 > 0 时,它就赢了。

这个工具用在另一个服务器上——有运行python程序的php代码,但在这里没关系。

http://pokersleuth.com/programmable-poker-calculator.shtml

该工具类似于 pokerstove,但它具有命令行版本。顺便说一句,不要买这个工具,我们买了他们没有发送许可证密钥,就像他们不在乎一样。

现在结果:

https://docs.google.com/spreadsheet/pub?key=0ArMZCQvNc-oQdEs0a1UyMkFGazVoN09KZmU1Q0FCU0E&output=html&richtext=true

现在,如果我们比较一下,与 pokerstove 或 pokersleuth 相比,赢牌 + 平局更常见。当我看蚂蚁领带时,它比扑克炉显示的要大得多。样本不是那么小。 Pokerstove 使用了近 400K 游戏,虽然少了一点,但趋势保持不变。首先,我尝试了小得多的样本,比如 10K 游戏——同样的趋势。因此,当我将生成 100K 更多时,如果结果保持大致相同,我并不感到惊讶。此示例也是在 linux 上生成的。但是在 Windows 上也生成了大约相同的样本,但获胜次数仍然超过 pokerstove 显示,获胜百分比的总和也是 110-112 %。

所以我们不理解 - 我是在生成错误的东西还是那些程序显示错误?显示错误股票的程序不太可能,因为它们被广泛使用并且可能已经进行了很多测试。

更新:

我想我终于明白了 :) Pokersleuth 计算了 知道棋盘上的 5 张牌,手(两张牌)获胜。你是 然后将这些机会与实际结果进行比较(了解 所有其他玩家)。对吧?

没错。我不得不写在这里,因为它不允许在 cmets 进行扩展讨论。

更新:

生成了更多行 - 目前在 linux 服务器上为 515989,% 仍然与以前大致相同。所以不动

【问题讨论】:

所以您正在生成手牌,然后使用能够生成正确数据的 pokerstove 以及您认为不能生成正确数据的其他工具来计算股票? get_probabilities是使用pokerstove产生数据的方法吗? 我正在根据玩家的手牌生成卡片。我的意思是所有样本玩家都拥有相同的牌。只有板子不同。 pokerstove 与其他工具获得几乎相同的权益。我不记得了,但可能在使用 enumerate 时 - 它应该得到完全相同的 eqwities。问题是我随机生成的数据与那些股票不匹配。它接近但不够接近。如果是因为方差,我应该会起起落落,但不同样本的结果往往会像我展示的那样。 get_probabilities() 方法在河牌圈产生赢率 - 换句话说,当所有公共牌都打开时。它在服务器中使用扑克侦探工具。如果 1 手获胜,该工具提供 100% 的胜率,如果 2 手获胜(平局),则每人 50% 的胜率,依此类推。 get_probabilities() 仅用于为 get_poker_winner() 函数提供输入 所以您基本上是在尝试将 pokersleuth 的准确性与基于最终开放棋盘的实际赢率进行比较? 【参考方案1】:

您有 296350 个生成的结果,但有 332911 个获胜和平局。

这个过程的任何部分是否可以协调这样一个事实,即当出现平局时,它是在两只或多只手之间?似乎关系被多算了。

计算您的总赢率和平局 %,112.3371014%。相乘(生成的结果除以胜率)。您将获得准确的 100%。这说明了什么?

另外,看看 Ad6c 和 Ah3c。他们的平局数完全相同,而且很可能是同一手牌(获胜是一对 A)。

但是对于他们平手的牌,该牌被计算/加权两次(一次用于 Ad6c,一次用于 Ah3c)。这就是为什么您的领带百分比至少是应有的两倍。您必须通过平局的手数来标准化平局计数

【讨论】:

欢迎来到 Stack Overflow。这些都是很好的见解,但您应该编辑您的答案而不是对其发表评论。 我猜你是对的。现在尝试通过对胜利进行分组来选择,当有 2 个获胜者时 - 然后除以 2 平局数,当有 8 个获胜者时 - 除以 8,然后求和,百分比现在看起来更好,总数是 100% 而不是 110 -112 :) 谢谢。所以我猜赏金点会自动添加给你。那么我可以等着看其他人是否会注意到一些错误

以上是关于php poker 生成的随机结果与预期不符的主要内容,如果未能解决你的问题,请参考以下文章

斗地主发牌程序

c语言产生一个随机的验证码(4位的随机字母和数字的组合),要求用户输入,给出长度不符的输入错位提示

在 PHP 中按权重生成随机结果?

PHP---------生成随机数,日期时间函数

PHP的函数-----生成随机数日期时间函数

Java - 比较两个 ZonedDateTime 时的结果与预期不符