2014-01-09 77 views
5

我开始自己学习C语言,并决定构建计算扑克股票的程序。我试图用蒙特卡罗计算公平,但我得到错误的结果。用于计算扑克股票的蒙特卡罗方法

所以这里是一个例子:我拿着JsTs(黑桃杰克和黑桃10)。我有两个对手分配特定的手牌。第一个对手只能使用AA(任何Ace-Ace口袋,总共6种不同组合),第二个对手使用KK +(任何Ace-Ace或King-King口袋)。所以计算过程从随机选择一个范围开始(对于对手1这总是AA)。然后我决定在该范围内的不同组合(Ah Ac,Ah Ad,Ah As ...等)并随机选择范围内的其中一个组合。所以我这样做是为了对手。然后我随机选择五张棋盘牌并评估玩家手牌和对手牌。然后我看看我是否赢或平局并保持结果。

所以我这样做了1000万次,我的股本达到了20.5%,但应该是19.1%。这里是我的代码的main()函数:

int main() 
{ 
randctx rctx; 
Deck[4][13]; 
randinit(&rctx, TRUE); 
numberOfOpponents = 2; 

//opponent ranges selected 
rangeIsSelected[0][0]= 1; 
rangeIsSelected[1][0]= 1; 
rangeIsSelected[1][14]= 1; 

//player cards 
Player_Card_Flush[0] = 0; 
Player_Card_Rank[0] = 8; 
Player_Card_Flush[1] = 0; 
Player_Card_Rank[1] = 9; 

//insert player cards to dealt cards 
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 1; 
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 1; 

checkForErrors(0); 

if (impossibleError==1) { 
    printf("Impossible to calculate equity"); 
    return; 
} 

gamesSimulated = 0; 
totalTies = 0; 
totalWins = 0; 
int opponentToBeDealt = 0; 

//let's see what ranges are selected by opponents 
for (int i=0; i<numberOfOpponents; i++) { 
    findSelectedRanges(i); 
} 

//beginning of Monte Carlo method 
while (gamesSimulated<maxTrials) { 
    int b = 0; 
    int opponentsDealt = 0; 

    //randomly select hand for opponents 
    while (opponentsDealt<numberOfOpponents) { 

     opponentCardsForMonteCarlo(opponentToBeDealt); 

     opponentsDealt += 1; 

     if (opponentsDealt==numberOfOpponents) break; 
     if (opponentToBeDealt==numberOfOpponents-1) { 
      opponentToBeDealt = 0; 
     } 
     else { 
      opponentToBeDealt += 1; 
     } 
    } 

    //randomly choose 5 board cards 
    while (b<5) { 

     int randomCardTag = rand(&rctx) % 52; 

     randomCardFlush[b] = randomCardTag/13; 
     randomCardRank[b] = randomCardTag % 13; 

     //if this card hasn't been dealt then add to dealt cards 
     if (Deck[randomCardFlush[b]][randomCardRank[b]]==0) { 
      Deck[randomCardFlush[b]][randomCardRank[b]] = 1; 
      b++; 
     } 
    } 

    //evaluate hands (this function also removes random opponent hands when it's done) 
    calculateMonteCarloEquity(); 

    //remove random board cards from dealt cards 
    for (int x=0; x<b; x++){ 
     Deck[randomCardFlush[x]][randomCardRank[x]]=0; 
    } 
} 

因为我已经写我自己的评价代码,我怀疑是第一次,但是当我做穷举完全相同的代码中,我得到正确的结果(证实PokerStove )。然后我开始考虑是否有对我的对手处理卡牌的方式有任何偏差。这是我的输出:

对手1手 作为AC:1665806次 作为阿:1667998次 作为广告:1666631次 交流啊:1665767次 交流广告:1666595次 阿广告: 1667203次

对手2手 作为AC:833847次 作为阿:833392次 作为广告:8323 96倍 AC阿:833406倍 AC广告:834542倍 阿广告:833703倍 Ks的KC:832585倍 了Ks的Kh:835641倍 了Ks的Kd:832483倍 的Kc的Kh:833013倍 的Kc的Kd:831558倍 Kh Kd:833434次

这对我来说看起来很随意。我也看了一下板卡,似乎也没有任何偏见,除了As-271 812倍,Ac-272 856倍,Ah-271 898倍,Ad以外,基本上所有的卡都处理约1088×××万次 - 272 062次,Ks - 815 113,Kc - 816 871,Kh - 814 955次,Kd - 814 866次,当然还有Js - 0次和Ts - 0次。我也尝试创建一个undealt卡阵列,所以我的随机板卡不会是rand(& rctx)%52,但根据情况rand(& rctx)%46,rand(& rctx)%45 etc(基本上我只选择低价牌)。然而,这并不会改变结果。

我使用ISAAC随机数发生器,虽然我几乎得到与内置rand()函数相同的结果。我试着用randinit(&rctx, time(NULL))这样的时间播种,但最终结果没有太大差异。是的,我知道时间对于密码学来说是一个糟糕的种子,但对于这样的模拟应该是可以的。

所以我用尽了想法,也许有人可以看到我失踪的东西?

编辑 这里是我的股权计算代码

void calculateMonteCarloEquity() { 
opponentsBeaten = 0; 
opponentsTied = 0; 
opponentsLost = 0; 

//remove all opponent cards from dealt cards, because we need 7 dealt cards to evaluate hand 
for (int x=0; x<numberOfOpponents; x++) { 
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 0; 
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 0; 
} 

//at this point we have 5 board cards and 2 player cards left in dealt cards 
//so let's evaluate that hand 
Evaluate_Hand(); 
playerHandScore = Hand_Score; 

//now remove player hand form dealt cards 
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 0; 
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 0; 

//let's evaluate opponent hands and save their scores 
for (int x=0; x<numberOfOpponents; x++) { 

    //insert opponent x hand to dealt cards 
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 1; 
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 1; 

    Evaluate_Hand(); 
    opponentHandScore[x] = Hand_Score; 

    //remove opponent x hand from dealt cards when evaluated 
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 0; 
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 0; 
} 

//compare opponent hand scores with player hand score 
for (int x=0; x<numberOfOpponents; x++) { 

    if (playerHandScore > opponentHandScore[x]) { 
     opponentsBeaten += 1; 
     continue; 
    } 
    else if (playerHandScore == opponentHandScore[x]) { 
     opponentsTied += 1; 
     continue; 
    } 
    else if (playerHandScore < opponentHandScore[x]) { 
     opponentsLost += 1; 
     continue; 
    } 
} 

//if player beats all opponents he wins the hand 
if (opponentsBeaten==numberOfOpponents) { 
    totalWins += 1; 
} 
//if player doesn't beat all the opponents, but neither loses to anyone, there must be a tie 
if (opponentsLost==0 && opponentsBeaten!=numberOfOpponents) { 
    totalTies += 1/(opponentsTied+1); 
} 

//one game has been evaluated 
gamesSimulated += 1; 
playerEquity = (totalWins+totalTies)/gamesSimulated; 

//let's insert player cards back to dealt cards 
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 1; 
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 1; 

if (gamesSimulated>=maxTrials) return; 
} 
+0

你能显示你的计算19.1%。你有没有考虑到对手会一直有国王和王牌?这意味着在桌子上获得千斤顶和十几张牌的概率是基于46张卡而不是50张卡。 –

+0

你对结果做过统计分析吗?如果正确的值是19.1%,那么得到20.5%的概率是多少? –

+0

我仍然认为理论值(19.1%)可能是错误的。你是如何达到19.1%的? –

回答

4

您分配了AA和KK概率相等的对手2.

在现实生活中,AA KK比AA AA更有可能。 KK有6个组合,只有一个有AA。

0

我不知道这是否是你为这个特殊的应用问题,但是这是使用RNG与蒙特卡罗方法一个糟糕的方式:

int randomCardTag = rand(&rctx) % 52; 

是因为两个原因:

  1. 低位不是非常随机的 - 模系运算符会丢弃所有高位。随机性主要由大多数生成器生成(肯定由LCG's生成,在大多数(所有?)实现中为rand())。

  2. 不同的randomCardTag的状态数是不同的:RNG有RAND_MAX + 1个状态,除了RAND_MAX + 1是52的倍数,一些randomCardTag有1个状态。这个差别很小,大约为1 /(RAND_MAX/52),但对于这个发生器来说,RAND_MAX通常不是很大。

使用它应该是有分工的正确方法:

int randomCardTag = rand(&rctx)/(RAND_MAX/52); 

注意,您必须使用这种方式使RNG的状态数是每个randomCardTag相同,尽管存在提醒。但也有一些国家导致randomCardTag = 52,所以:

int randomCardTag; 
do { 
    randomCardTag = rand(&rctx)/(RAND_MAX/52); 
} while (randomCardTag > 51); 

我会使用,无论梅森,倍捻机RNG,只是要确定你是不是遇到与兰特一定的相关性()。

如果您在修复后仍然存在问题,那么在别处还有其他问题。