巴哈姆特

哈啦區 爐石戰記
查看全文

【討論】前兩回合時,手上同時抽到兩張龍鰻、一張學徒、和一張鏡像的機率?

綜合心得
彼時楓憶 (daniel580804) 2018-10-18 05:21:52
#1
之前看到版上有人在聊這個問題,自己剛考完的機率論考試所以忍不住就想要解解看。然而,實際動手算之後發現因為有換牌的關係,比想像中難算很多。

於是乎,這就是學校教的C++能派上用場的時候啦!因為寫程式能力有點生疏,所以花了一整個晚上才寫好。

但是!!!非常令人驚訝,機率竟然不到1%

以下是執行結果:
舉例來說:
0 1 1 0
0.015873  1201

是指:“沒換牌前,龍鰻0張學徒至少1鏡像至少1張”的情況— 在總共的27405個組合中,一共出現了1201次。
而在這種特定的情況下,前兩回合就有抽齊兩張龍鰻、一張學徒、和一張鏡像的條件機率是0.015873


那麼,就只好把程式碼貼上來,和大家討論實際的機率啦~
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <array>
#include <vector>
using namespace std;

//returns all (-1) if having no other ways to choose
void selection_changeToNextState(int numOfThingsToChooseFrom, int numOfThingsChosen, int nowState[]);

int main(int argc, const char * argv[]) {
    const int MaxCards = 30;
    int rounds = 2;
    
    array<int,MaxCards> cards={};
    cards[0]=2;
    cards[1]=2;
    cards[2]=2;
    cards[3]=24;
    
    
    array<int,MaxCards> goal={};
    goal[0]=2;
    goal[1]=1;
    goal[2]=1;
    goal[3]=0;
    
    array<int,MaxCards> deck={};
    int nowCard=0;
    int importantTypes=0;
    for(int nowType=0; nowType<MaxCards; ++nowType){
        if(cards[nowType]>0){
            importantTypes = nowType+1;
            
            for(int j=nowCard; j< nowCard+cards[nowType]; ++j){
                deck[j]=nowType;
            }
            
            nowCard+=cards[nowType];
        }
    }
    int totalCards = nowCard;
    
    cout<<"totalCards:\t"<<totalCards<<"\n";
    cout<<"importantTypes:\t"<<importantTypes<<"\n";
    
     //for testing: show the entire deck
     /*for(int i=0;i<totalCards;++i){
        cout<<i<<":\t"<<deck[i]<<"\n";
    }*/
    
    bool isFirst = false;
    
    int firstDrawNum;
    
    if(isFirst)
        firstDrawNum=3;
    else
        firstDrawNum=4;
    
    
    //hand starts from "choosing the first <firstDrawNum> number of cards"
    array<int,MaxCards> hand = {};
    for(int i=0;i<firstDrawNum;++i){
        hand[i]=i;
    }
    
    bool testShow_cardTypeDrawn = false;
    bool testShow_possible_hands = false;
    bool testShow_possible_afterHands = false;
    bool testShow_unfulfilledGoal = false;
    bool testShow_finalGoal = false;
    bool testShow_cardsLaterDrawn = false;
    bool testShow_count = true;
    bool testShow_finishProbability = false;
    
    int count=1;
    
    struct cardTypesKept_to_probability{
        array<int,MaxCards> cardTypesKept;
        double probability;
        int count;
    };
    
    vector<cardTypesKept_to_probability> record;
    
    while(true){
        int cardsKept = 0;
        int cardsToRedraw = 0;
        array<int,MaxCards> unfulfilledGoal = goal;
        array<int,MaxCards> cardTypesKept = {};
        array<bool,MaxCards> cardIsKept = {};
        
        for(int j=0;j<firstDrawNum;++j){
            int cardDrawn = deck[hand[j]];
            
            // for testing: show card type drawn
            if(testShow_cardTypeDrawn)
                cout<<cardDrawn<<"\n";
            
            if(unfulfilledGoal[ cardDrawn ]>0){
                unfulfilledGoal[ cardDrawn ]--;
                cardTypesKept[ cardDrawn ]++;
                cardIsKept[ hand[j] ] = true;
                ++cardsKept;
            }
            else{
                ++cardsToRedraw;
            }
        }
        
        
        // for testing: show card type drawn
        if(testShow_cardTypeDrawn){
            cout<<"\n";
            for(int j=0;j<importantTypes;++j){
                cout<<cardTypesKept[j]<<"\t";
            }
            cout<<"\n";
        }
        
        bool inRecord = false;
        
        for(int j=0;j<record.size();++j){
            if(cardTypesKept == record[j].cardTypesKept){
                ++record[j].count;
                inRecord = true;
            }
        }
        if(!inRecord){
            int cardsLaterDrawn = cardsToRedraw + (rounds-1) +1;
            
            array<int,MaxCards> calcHandsAfter = {};
            for(int i=0;i<cardsLaterDrawn;++i){
                calcHandsAfter[i]=i;
            }
            
            int afterCount = 1;
            int finishCount = 0;
            while(true){
                // for testing: show all the possible hands after drawing again
                if(testShow_possible_afterHands){
                    cout<<"\t\t";
                    cout<<afterCount<<"~  ";
                    for(int j=0;j<cardsLaterDrawn;++j){
                        cout<<calcHandsAfter[j]<<" ";
                    }
                    cout<<"\n";
                }
                
                array<int,MaxCards> finalGoal = unfulfilledGoal;
                bool goalFinished = true;
                
                int nowPlace=0;
                int nowAfterCode=0;
                for(int stepsMoved=0; nowPlace<totalCards;++nowPlace){
                    if(cardIsKept[nowPlace]){
                        if(testShow_possible_afterHands)
                            cout<<nowPlace<<" ";
                    }
                    else{
                        if(stepsMoved == calcHandsAfter[nowAfterCode]){
                            if(testShow_possible_afterHands)
                                cout<<nowPlace<<" ";
                            
                            int cardDrawnAfter = deck[nowPlace];
                            if(finalGoal[ cardDrawnAfter ]>0){
                                finalGoal[ cardDrawnAfter ]--;
                            }
                            
                            ++nowAfterCode;
                        }
                        ++stepsMoved;
                    }
                }
                if(testShow_possible_afterHands)
                    cout<<"\n";
                
                for(int j=0;j<importantTypes;++j){
                    if(finalGoal[j]>0){
                        goalFinished=false;
                    }
                }
                if(goalFinished)
                    ++finishCount;
                
                // for testing: show finalGoal
                if(testShow_finalGoal){
                    for(int j=0;j<importantTypes;++j){
                        cout<<finalGoal[j]<<" ";
                    }
                    cout<<"\n";
                }
                
                selection_changeToNextState(totalCards-cardsKept, cardsLaterDrawn, calcHandsAfter.data());
                
                //if having no other ways to choose
                if(calcHandsAfter[0]<0)
                    break;
                
                ++afterCount;
            }
            
            double finishProbability = (finishCount * 1.0)/afterCount;
            struct cardTypesKept_to_probability c_p={cardTypesKept,finishProbability,1};
            record.push_back(c_p);
            //cout<<finishProbability<<"\n";
        }
        
        // for testing: show all the possible hands
        if(testShow_possible_hands){
            cout<<count<<":  ";
            for(int j=0;j<firstDrawNum;++j){
                cout<<hand[j]<<" ";
            }
            cout<<"\n";
        }
        
        // for testing: show unfulfilledGoal
        if(testShow_unfulfilledGoal){
            cout<<count<<"–  ";
            for(int j=0;j<importantTypes;++j){
                cout<<unfulfilledGoal[j]<<" ";
            }
        }
        
        
        selection_changeToNextState(totalCards, firstDrawNum, hand.data());
        
        //if having no other ways to choose
        if(hand[0]<0)
            break;
        
        ++count;
    }
    
    //for testing
    if(testShow_count)
        cout<<count<<"\n";
    
    
    double sumOfProbability = 0.0;
    
    for(int i=0;i<record.size();++i){
        sumOfProbability += (record[i].probability)*(record[i].count);
        for(int j=0;j<importantTypes;++j){
            cout<<record[i].cardTypesKept[j]<<"\t";
        }
        cout<<"\n";
        cout<<record[i].probability<<"\t"<< record[i].count <<"\n";
        cout<<"\n";
    }
    
    cout<<"\n"<< sumOfProbability/count <<"\n";
    return 0;
}


//returns all (-1) if having no other ways to choose
void selection_changeToNextState(int numOfThingsToChooseFrom, int numOfThingsChosen, int nowState[]){
    int lastDigit = numOfThingsChosen-1;
    int lastNum = numOfThingsToChooseFrom-1;
    int carryDigits = 0;
    while(nowState[lastDigit - carryDigits] >= lastNum-carryDigits){
        ++carryDigits;
        if(lastDigit - carryDigits<0)
            break;
    }
    //return all (-1) if having no other ways to choose
    if(lastDigit - carryDigits<0){
        for(int j= 0; j<=lastDigit; ++j){
            nowState[j]= -1;
        }
        return;
    }
    
    nowState[lastDigit - carryDigits]++;
    for(int j= lastDigit - carryDigits+1; j<=lastDigit; ++j){
        nowState[j]=nowState[j-1]+1;
    }
    
}

跑出來的結果比我預期中小很多。大家覺得,這一種情況的機率是多少呢?

看較舊的 24 則留言

透明エレジー: B6 2018-10-18 08:09

在我對面天胡的機率高達90%

臺灣國之神: B26 2018-10-22 08:10

偷偷告訴你,遊戲類的運算程式碼跟你寫的不同,用HDT紀錄一下就懂了

可撥韭菜: B27 2018-10-23 08:51

我是覺得 1-2費的某幾張職業卡 機率有刻意調高 感覺太明顯了

可撥韭菜: B28 2018-10-23 08:53

其他職業也有 某幾張1-2費特別容易抽到的問題

憤怒熊熊: B29 2018-10-25 01:21

這就是莫非定律厲害的地方

延伸閱讀

情報活動任務 歡樂術師杰佩托 解法

2024-03-27 14:40:58

情報平衡調整,聖騎士88

2024-03-27 11:57:10

問題卡包開到這張傳說但是無法分解或合成?

2022-12-17 15:41:27

問題請問有中了冰凍陷阱增加消耗(2),下一回合水晶多過消耗仍打不出同一張牌的嗎?

2020-09-11 03:29:14

攻略死亡輪盤!【輪盤术教程与实战讲解

2024-03-27 16:25:39

心得抽不到哨斗龍.. 只好玩戰吼童話森林

2024-03-25 20:17:46

攻略窮人版快攻混節奏DH上傳說 只需要一張傳說

2020-04-19 13:07:30

討論試算卡拉贊改版五張傳送門之實質效益(8/15更新薩2費法術大漩渦傳送門)

2016-08-15 21:45:50

不同視角有不一樣的感受

看更多

RPG 新作《英雄傳說 閃之軌跡:北方戰役》於日本推出 以動畫版角色視角深入體驗北方戰役

人物

《黎明死線》新殺手「恰吉」本日正式登場 以第三人稱視角展現神出鬼沒的矮小夢魘

多平台

第一人稱視角心理恐怖遊戲《Luto》試玩版登陸 Steam 平台

多平台

開啟 APP

face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】