【问题标题】:C++ Array ShuffleC++ 数组随机播放
【发布时间】:2009-05-28 17:44:17
【问题描述】:

我对 C++ 还很陌生,不太了解带有指针和引用的函数参数。我有一系列卡片,我想使用 Fisher-Yates 洗牌来洗牌。甲板被声明为

Card *deck[deckSize];

其中deckSize 已声明为24。然后初始化数组。 然后我调用 shuffle 函数:

void shuffle (Card * deck[]) {
    int deckSize = 24;
    while (deckSize > 1) {
       long int k = lrand48();
       k = k %24;
       deckSize--;
       Card * temp = deck[deckSize];
       deck[deckSize] = deck[k];
       deck[k] = temp;
    }
}

如果我在调用 shuffle 函数后尝试打印卡片的值,则会出现 seg 错误。有关如何正确执行此操作的任何指示?

【问题讨论】:

  • 这一行中的 temp 是什么:deck[k] = temp;?
  • 看起来您在“deckSize--”之后缺少一个“Card *temp = deck[deckSize]”。这是复制+粘贴错误,还是您的代码中实际上缺少该行?
  • 我看不出该代码有什么问题。我认为问题在于打印卡的值或数组的初始化。你应该为那些发布代码。

标签: c++ arrays pointers


【解决方案1】:

只需使用<algorithm> 中的std::random_shuffle,如下所示:

std::random_shuffle(deck, deck + deckSize);

然后你的牌组被洗牌。

【讨论】:

    【解决方案2】:

    我的 C/C++ 生锈了,但我认为您的声明:

    Card *deck[deckSize];
    

    正在声明一个指向卡片的指针数组。你不想要这个吗?

    Card deck[deckSize];
    

    然后声明洗牌:

    void shuffle (Card deck[]) 
    

    请记住,数组是 0 索引的。不确定您是否会访问第 24 个元素,但那将是一个嘘声。

    【讨论】:

    • 我不相信卡片组[deckSize];是有效的 C++。
    • 除非 deckSize 是一个常数。
    • 哎呀——我认为 deckSize 是一个常数。假设:)
    • 还不错;删除了 -1 :D
    • 洗牌指针可能是你想在这里做的。如果卡片对象很大,或者具有复杂的复制构造函数,则复制可能会非常昂贵。
    【解决方案3】:

    你也可以使用

    std::random_shuffle(deck, deck + deckSize)

    Fisher-Yates 为您服务。

    但是,标准随机库中可能没有足够的位来真正从所有可能的随机卡片排列中进行选择。

    【讨论】:

      【解决方案4】:

      看起来你的问题不是来自发布的代码,乍一看还不错,而是来自它周围的代码。

      使用标准的卡片容器怎么样?一定要填,先打印看看好不好,洗牌,再打印。

      #include <vector>
      std::vector<Card> deck; // Empty for now. Must be filled with cards.
      
      
      void shuffle (std::vector<Card> & deck)
      {
          int deckSize = 24;
          while (deckSize > 1) 
          {
             long int k = lrand48();
             k = k %24;
             deckSize--;
             Card temp = deck[deckSize];
             deck[deckSize] = deck[k];
             deck[k] = temp;
          }
      }
      

      【讨论】:

        【解决方案5】:
         Card *deck[deckSize];
        

        我想你想要:

        Card *deck = new Card[deckSize];
        

        【讨论】:

        • deckSize 是一个编译时间常数,所以 `Card * deck[deckSize]' 非常好,比动态分配更可取。
        • 不,你错了。你假设它是一个编译时间常数,但你不能确定。猫是活的还是死的?
        【解决方案6】:

        立即挑剔,您应该始终使用随机数的上半部分,因为大多数随机数的实现在下半部分的随机性较差。因此,如果 long 是 32 位的,您可以使用:k = (k &gt;&gt; 24) % 24 以获得更好的随机性。

        其次,这里的问题是你没有设置温度。您的代码应该有一行:temp = deck[deckSize];

        希望这会有所帮助。

        编辑

        进一步挑剔,无论使用高位还是低位,您的随机数生成器也不足以充分洗牌。它只有 48 位长序列,但要洗牌,您至少需要一个 226 位长的序列(52!,洗牌的方法数,是一个 226 位长的数字)。

        【讨论】:

        • re:随机数。这听起来充其量只是非常边缘的建议。
        • 对不起,如果你和人类对战可能没关系,但如果你在做模拟,你的结果根本不可靠。他没有说明是哪种情况。
        • 关于随机数生成器范围的建议是有效的。如前所述,如果你只是打牌没关系,但如果洗牌被用来确定谁进入更好的学校,谁没有(我在这里根据经验说话),那么你必须问自己: 这公平吗?顺便说一句,你可能不想做 k = (k >> 24) % 24 - 这会让你得到一个非常有限的随机数生成器。 (范围 [0,23]
        • 没错,最好一起跳过Fisher-Yates,并使用226位随机数来生成甲板的第n次排列。如果您只需要使用 lrand48() 调用 5 次来随机播放少量次数以生成更大的数字就足够了。当然,这些该死的随机数并不容易 - 而且,这些都只是挑剔:)。
        【解决方案7】:

        您将 deck 声明为一个指针数组,但没有为它分配任何空间。如果你在不分配空间的情况下取消引用它,你会得到一个段错误。

        【讨论】:

        • 他说它在他的问题中初始化
        • @yz 我不确定那些“不太了解带有指针和引用的函数参数”的人是否意识到为指针分配空间是“初始化”它的一部分。
        • 我循环遍历数组并用“deck[i] = new Card()”初始化每张卡片。这足以为其分配空间吗?
        • @Everett 是的,没关系。但正如 n8wrl 在他的回答中指出的那样:您可能不希望以指针数组开头。可能是指向数组的指针:Card (* deck)[deckSize]
        【解决方案8】:

        我认为查看调用代码可能会有所帮助。

        类卡{ 上市: Card(int number):number_(number){} int getNumber(){返回号码_;} // ... 私人的: 整数_; }; 无效洗牌(卡*甲板[]){ int 甲板大小 = 24; 而(甲板大小> 1){ 长整数 k = lrand48(); k = k %24; 甲板尺寸——; 卡 * 温度 = 甲板[甲板尺寸]; 甲板[甲板尺寸] = 甲板[k]; 甲板[k] = 温度; } } int main(int argc, char* argv[]){ { 常量 int 甲板大小=24; 卡片* 甲板[deckSize]; for(int i = 0 ; i getNumber()

        应该没问题。

        【讨论】:

          【解决方案9】:

          如上所述,基本数组不能使用作为大小传递的变量来定义。

          那里要小心。 的最后一个元素

          typename array[SIZE];

          array[SIZE-1],而不是array[SIZE]。这可能是您遇到段错误的地方。

          您真的应该至少尝试使用 STL 容器。 STL 也有 shuffle 算法(:

          【讨论】:

            【解决方案10】:

            人们抱怨您没有使用容器并且没有声明数组的大小。别担心,这不是问题。有人还说你要越过数组边界,但事实并非如此。没有声明大小的数组是可以的。有一个指向 Card 的指针数组也是可以的。但我不明白为什么它会崩溃。这是我根据您的代码编写的一些示例代码:

            #include <stdio.h>
            #include <stdlib.h>
            #define DECK_SIZE 24
            void shuffle(int deck[]) {
                int n = DECK_SIZE, t;
                while (n > 1) {
                    long k = lrand48() % DECK_SIZE;
                    n--;
                    t = deck[n];
                    deck[n] = deck[k];
                    deck[k] = t;
                }
            }
            int main(int argc, char **argv) {
                int deck[DECK_SIZE], i;
                for (i = 0; i < DECK_SIZE; ++i)
                    deck[i] = i + 1;
                shuffle(deck);
                for (i = 0; i < DECK_SIZE; ++i)
                    printf("%i\n", deck[i]);
                return 0;
            }
            

            运行它,它工作得很好。这意味着还有其他事情发生。在你调用 shuffle 之前尝试打印你牌组中所有牌的值,看看它是否也有段错误,我怀疑它会。

            但是,您的代码中存在错误。您的函数未正确随机播放。正确的洗牌方法不是将每张牌与从整副牌中选出的牌交换,而是将位置 N 的每张牌与从 0..N 范围内选出的牌交换。如果您将每张卡片与随机卡片交换,您将获得 N^N 种可能的结果,如果您将卡片交换回原来的位置,其中一些结果会重叠。使用 3 张牌很明显这是错误的,因为您最终会得到 27 种不同的洗牌,其中一些是相同的,即使 3 张牌有 3!=6 种排列。问题在于,由于 6 不是 27 的因数,因此某些排列比其他排列更有可能。为避免这种情况,请尝试这样做:

            void shuffle_correctly(int deck[]) {
                int i, t, k;
                for (i = 2; i < DECK_SIZE; ++i) {
                    k = lrand48() % i;
                    t = deck[i-1];
                    deck[i-1] = deck[k];
                    deck[k] = t;
                }
            }
            

            【讨论】:

            • Dietrick,我相信你的例程和他的例程是等价的,但从数组的两端开始。请注意,您的“i”从 2 单调增加到 DECK_SIZE。并且他的“n”从 DECK_SIZE 单调减少到 2。两者都是 Fisher-Yates。
            • 哦,我的错,他应该调用 lrand48() % n 正如你正确指出的那样。但是,这确实意味着您的两个例程都完全不同。我确信他(经过上述调整)是 Fisher-Yates (en.wikipedia.org/wiki/Fisher-Yates_shuffle)。但是,我相信你的不是。你用一张随机牌将当前位置洗牌直到当前位置。这意味着一张牌可能会移动不止一次。在 Fisher-Yates 中,它只能移动一次。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-10-07
            • 1970-01-01
            • 1970-01-01
            • 2011-01-27
            • 2023-03-10
            相关资源
            最近更新 更多