【问题标题】:Need to find a logic error in a card shuffling method需要找出洗牌方法中的逻辑错误
【发布时间】:2011-03-04 14:31:30
【问题描述】:

我正在尝试编写一个方法,该方法采用整数数组(0-51,按该顺序),使用我知道的 cut 方法将其切割成两个单独的数组(以下函数中的 A 和 B肯定有效),然后通过从 A 或 B 的底部随机选择 0、1 或 2 张牌,然后将它们添加到牌组中,将两个阵列重新融合在一起。

(ps-“数组”是指链表,我只是说数组是因为我认为它在概念上更容易)

到目前为止,这是我的代码,它有效,但在卡牌的位置方面存在一定的偏差。谁能发现我的逻辑错误?

[code]
void Deck::shuffle(){
    IntList *A = new IntList();
    IntList *B = new IntList();
    cut(A, B);
    IntListNode *aMarker = new IntListNode;
    aMarker = A->getSentinel()->next;
    //cout<< A->getSentinel()->prev->prev->data <<'\n'<<'\n';
    IntListNode *bMarker = new IntListNode;
    bMarker = B->getSentinel()->next;
    //cout<< B->getSentinel()->prev->data;
    deckList.clear();

    srand(time(NULL));
    int randNum = 0, numCards = 0, totalNumCards = 0; 
    bool selector = true, aisDone = false, bisDone = false;

    while(totalNumCards < 52){
        randNum = rand() % 3;
        if(randNum == 0){
            selector = !selector;
            continue;
        }
        numCards = randNum;

        if(!aisDone && !bisDone){
            if(selector){
                for(int i = 0; i < numCards; i++){
                    deckList.push_back(aMarker->data);
                    aMarker = (aMarker->next);
                    if(aMarker == A->getSentinel()){
                        aisDone = true;
                        break;
                    }
                }
                selector = false;
            }else{
                for(int i = 0; i < numCards; i++){
                    deckList.push_back(bMarker->data);
                    bMarker = (bMarker->next);
                    if(bMarker == B->getSentinel()){
                        bisDone = true;
                        break;
                    }
                }
                selector = true;
            }
        }
        if(aisDone && !bisDone){
            for(int i = 0; i < (52 - totalNumCards); i++){
                deckList.push_back(bMarker->data);
                bMarker = (bMarker->next);
                if(bMarker == B->getSentinel()){
                    bisDone = true;
                    break;
                }
            }
            //return;
        }
        if(bisDone && !aisDone){
            for(int i = 0; i < (52 - totalNumCards); i++){
                deckList.push_back(aMarker->data);
                aMarker = (aMarker->next);
                if(aMarker == A->getSentinel()){
                    aisDone = true;
                    break;
                }
            }
            //return;
        }
        totalNumCards += numCards;
    }

    int tempSum = 0;
    IntListNode *tempNode = deckList.head();
    for(int j = 0; j < 52; j++){
        //cout<< (tempNode->data) << '\n';
        tempSum += (tempNode->data);
        tempNode = (tempNode ->next);
    }
    if(tempSum != 1326)
        system("PAUSE");

    return;
}
[/code]

【问题讨论】:

    标签: c++ linked-list shuffle


    【解决方案1】:

    只使用std::random_shuffle 怎么样?是的,它不适用于链表,但您可以将其更改为 vector :)

    【讨论】:

      【解决方案2】:

      如果你的导师有道德教你编程,那么他们会鼓励你像这样用四行代码解决问题:

      #include<algorithm>
      #include<vector>
      
      // ...
      std::vector<int> cards;  // fill it in ...
      std::random_shuffle(cards.begin(), cards.end());
      

      使用标准库是正确的做事方式。当您可以使用标准库解决问题时自己编写代码是错误的做事方式。你的导师没有教你正确。如果他们想表达观点(例如,您是否练习过使用指针),那么他们应该更加注意选择他们给您的练习。

      给出的演讲,这是一个比上述更糟糕但比你的导师更好的解决方案:

      1. 52 次执行以下操作:

        1. 在 [0,52) 范围内随机选择两个不相等的整数。
        2. 交换与这些位置对应的数组中的值。

      【讨论】:

      • 对不起-我应该提到,我不允许使用它:(
      • 谁不允许你使用 C++ 标准库?这是作业吗?
      • 我更新了我的答案。我希望你不要坚持“削减甲板”。
      • 取决于这是什么类。如果它是一门软件工程课程,那么一定要使用标准库函数并将可读性放在首位(除了正确性)。但如果是计算机科学课,直接实现算法肯定是正确的做事方式。计算机科学是关于理解什么使随机算法足够随机,而不是关于如何洗牌。
      【解决方案3】:
      1. 对于大多数随机数生成器,低位是最少个随机数。所以你的线

        randNum = rand() % 3;
        

        应修改以使其值更多地来自rand 的高位到中位。

      2. 您的期望可能会偏离。我注意到如果您的随机值为 0,您会交换选择器。再加上 randNum 的相对非随机性,这可能是您的问题。也许您需要减少随机性以使它们看起来更随机,例如每次循环都交换选择器,并且总是从选定的牌组中取出 1 张或更多张牌。

      【讨论】:

        【解决方案4】:

        评论:

        srand(time(NULL));
        

        这只能在应用程序运行期间调用一次。通常最好在开始时在 main() 中调用它。

        int randNum = 0, numCards = 0, totalNumCards = 0; 
        bool selector = true, aisDone = false, bisDone = false;
        

        每行一个标识符。每个编写的编码标准都有这个规则。它还可以防止使用指针时可能出现的一些细微错误。习惯就好。

        randNum = rand() % 3;
        

        rand 的低位随机性最小。

        rand Num = rand() / (MAX_RAND / 3.0);
        

        问题:

            if(!aisDone && !bisDone)
            {
                 This can execute
                 and set one of the above to isDone
        
                 Example:
                   Exit state aisDone == false bsiDone == false   // OK
                   Exit state aisDone == true  bsiDone == false   // Will run below
                   Exit state aisDone == false bsiDone == ture    // Will run below
            }
            if(aisDone && !bisDone)
            {
                 Is this allowed to run if the first block above is run?
            }
            if(bisDone && !aisDone)
            {
                 Is this allowed to run if the first block above is run?
            }
        

        其他的太复杂了,看不懂。

        我可以想出一些更简单的技巧来很好地洗牌:

        for(loop = 0 .. 51)
        {
            rand = rand(51 - loop);
            swap(loop, loop+rand);
        }
        

        上面模拟了从牌组 A 中随机挑选一张牌并将其放在牌组 B 的顶部(牌组 B 最初是空的)。当循环完成时,B 现在是 A(就地完成)。

        因此,每张卡(来自 A)有相同的概率被放置在 B 中的任何位置。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-28
          • 1970-01-01
          • 2021-08-20
          • 2011-08-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多