您好,欢迎来到气泡游戏网!

气泡游戏网
手机应用中心 热门攻略 轩辕传奇 气泡问问 疾风之刃 枪神纪 天堂2M 救世者之树 上古世纪 黑色沙漠MOBILE 未来战 冒险岛M(楓之谷 M) 传说对决 瓦尔海姆 鬼谷八荒 怪物猎人系列

当前位置:首页 > 攻略库 > 炉石传说 > 正文

《炉石传说台服》【讨论】前两回合时,手上同时抽到两张龙鳗、一张学徒、和一张镜像的机率?

更新时间:1603438656   |   来源:巴哈姆特

daniel580804 (彼时枫忆) #1 2018-10-18 05:21:52
之前看到版上有人在聊这个问题,自己刚考完的机率论考试所以忍不住就想要解解看。然而,实际动手算之后发现因为有换牌的关係,比想像中难算很多。

于是乎,这就是学校教的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;
    }
    
}

跑出来的结果比我预期中小很多。大家觉得,这一种情况的机率是多少呢?

看较旧的 26 则留言

我他妈射爆: 10-23 08:51

我是觉得 1-2费的某几张职业卡 机率有刻意调高 感觉太明显了

我他妈射爆: 10-23 08:53

其他职业也有 某几张1-2费特别容易抽到的问题

小小捣蛋鬼: 10-25 01:21

这就是莫非定律厉害的地方

daniel580804 (彼时枫忆) #2 2018-10-18 08:40:27
补充,因为只有程式码的话太抽象,所以附上用手算的大概过程

后手 ,1费有龙鳗—

换牌前没有的机率:
p = C(28,4)/C(30,4)=
(28*27*26*25)/(30*29*28*27)=
(26*25)/(30*29)=0.747
全换后也没有的机率:
C(28,4)/C(30,4) = 0.747
后手多抽的那张也没有的机率:
(30-4-2)/(30-4)=24/26=0.923

第一回合没有龙鳗的总机率:以上三个相乘 = 0.747*0.747*0.923 = 0.515
第一回合有龙鳗的总机率:
1 - 0.515 = 0.485

看较旧的 3 则留言

彼时枫忆: 10-18 10:12

[ken811325:小煜]换掉的牌,似乎还是会被重新抽起来?不过我也不是完全确定就是啦~

阿德: 10-18 10:22

起手换掉的牌 起手那三或四张不会在抽到换掉的牌

阿德: 10-18 10:27 编辑

所以其实你起手2费前要全力(全换)抓到成长(以前别人是算2费无耻小斧机率) 先手45%后手55% 但我体感玩德鲁依根本没到这机率 很想找人实测看看机率

daniel580804 (彼时枫忆) #3 2018-10-18 09:26:18
另外,由程式得出,前两回合同时有龙鳗+学徒的机率是0.245,约为1/4。
totalCards:30
importantTypes:3
27405
110
11405

100
0.3201975525

010
0.3201975525

000
0.11877414950


0.245169
Program ended with exit code: 0
game2520741 (凤梨) #4 2018-10-18 10:04:50
我先贴后手起手换牌(共八张)之后有龙鳗加学徒的机率


S=龙鳗
L=学徒

T2有法术的话问题变成10回合内抽出SSLF,F是法术

看较旧的 34 则留言

凤梨: 10-18 13:45

先讲结论,用抽八张的方法算这个问题会高估机率,但简化很多。不同的点在于如果起手都不换,只会是抽六张

凤梨: 10-18 13:45

没办法变成抽十张的情况去算,因为后面那几张有没有中都不知道

Mo: 10-18 14:28

换牌阶段不会重複抽到换掉的牌,但能在T1开始后抽的第一张牌抽到,HearthstoneWiki那可找到BB当时推特关于mulligan的内容

h0911491849 (后来的我) #5 2018-10-18 12:46:24
可以帮我算一下术T1小鬼 硬币 巫医双食尸鬼 的机率吗XD

回到主题
首先是 为什幺要算这个机率呀?

就算让你2张龙鳗+学徒+镜像出了
那也只是你在前两回合就把所有手牌都打玩了
也就是我有东西清场 你就什幺都没了

遇到快攻还怕你铺场比他快吗~
对吧!?


如果只是无聊想算机率的话..
帮我算一下我出生的机率吧XD
2亿的竞争 还不能有缺陷 不然去医院照胎看到缺手缺脚
父母又要是正常夫妻或情侣想生小孩
怀胎10个月还不能有意外

这样看起来 我应该是人生胜利组 对吧XD


看较旧的 3 则留言

番茄炒荷包蛋: 10-23 10:00

呵呵~快攻神起手也能被你说成乾手牌怕aoe的缺点

番茄炒荷包蛋: 10-23 10:12

祝你玩快攻起手没123费,满满手牌不怕对面的aoe

cat521219 (Leonnail) #6 2018-10-23 07:35:21
我怎幺算都是50%呀
有跟没有阿
不然对面怎幺可能动不动就天胡==

看较旧的 1 则留言

空气感奈里( *´◒`*): 10-23 11:31

[tommy014012:ヽ(́◕◞౪◟◕‵)ノ] [tommy014012:ヽ(́◕◞౪◟◕‵)ノ]体感 别人都是神起手100% 我都是高费0%

浮土: 10-24 16:00

[Naile44:小奈里(,,・ω・,,)]平均起来刚好50

Leonnail: 10-26 21:36

起手双龙鳗合情合理

精彩推荐

Wonderful recommendation

更多

关于我们 | 商务合作 | 广告服务 | 法律声明 | 内容导航 | 游戏帮助 | 问题反溃

本站所有软件,来自于互联网或网友上传,版权属原著所有,如有需要请购买正版。如有侵权,敬请来信联系我们,我们立刻删除。

抵制不良游戏 拒绝盗版游戏 注意自我保护 谨防受骗上当 适度游戏益脑 沉迷游戏伤身 合理安排时间 享受健康生活

Copyright 2019-2025 by 鲁ICP备2024066534号-1 成都市互联网举报中心