洗牌 Array 的元素:基于堆栈的缓冲区溢出错误

Shuffling elements of Array : stack-based buffer overrun error

我给出的代码是原程序的问题部分。它在 T 次循环中随机交换 myArray 的两个元素 N 次。该程序执行了它应该执行的操作,但在点击 "return 0" 后,它显示 "program.exe has stopped working" 的错误信息。调试输出显示

Stack cookie instrumentation code detected a stack-based buffer overrun

为什么程序完成后显示错误? 我该如何解决这个问题?

#include <iostream>
#include <ctime>    
#include <cstdlib>

using namespace std;

int main()
{
    const int N = 10000;
    const int T = 100; 

    srand((unsigned)time(0));   

    bool myArray[N] ;
    bool temp = true;
    int save1 = 0;
    int save2 = 0;

    //initializing myArray
    for (int index = 0; index < N/2; index++) {
        myArray[index] = false;
    }
    for (int index = N/2; index < N; index++) {
        myArray[index] = true;
    }

    for (int index = 0; index < T; index++) {

        for (int index1 = 0; index1 < N; index1++) {    
            save1 = int( N*rand()/RAND_MAX );
            save2 = int( N*rand()/RAND_MAX );


            temp = myArray[save1];
            myArray[save1] = myArray[save2] ;
            myArray[save2] = temp; 
        }
    }

    cout<<" Press any key to exit...";
    cin.get();

    return 0;
}

编辑: 我必须生成从 0 到 (N-1) 的随机整数。调用 myArray 中的第 N 个位置会产生问题。

但是下面两种方法都不能统一生成随机整数。

    save1 = int( (N-1)*rand()/RAND_MAX );

也不

    save1 = int( N*rand()/(RAND_MAX+1) );

这个方法的问题有一个很好的video。还有Mic和Bob__指出的(N-1)*rand()导致的溢出问题。

这种取模方法对于大范围的随机整数也是非常低效的(查看article了解详情)。所以,我生成均匀随机数的最佳机会是以下方法(从文章中借用)。

while(true)
{
  int value = rand();
  if (value < RAND_MAX - RAND_MAX % range)
    return value % range;
}

另外,对于改组数组元素,最好使用 random_shuffle 函数或 Fisher–Yates shuffle 以获得最佳性能。

至少要解决一件事:

rand() returns 介于 0 和 RAND_MAX 之间的随机整数,因此您必须替换

  N*rand()/RAND_MAX 

来自

  N*rand()/(1+RAND_MAX)

您应该将 N 替换为 (N-1)。可能这就是你想要做的。

    save1 = int( (N-1)*rand()/RAND_MAX );
    save2 = int( (N-1)*rand()/RAND_MAX );

只是想知道您是否打算在语句中使用“Index1”来计算 save1 和 save2。这也将解决问题。

让我们考虑这一行(编辑后的问题):

save1 = int( (N-1)*rand()/RAND_MAX );

其中save1int类型的变量,N是相同类型的常量,rand()returns是int 范围 [0, RAND_MAX].

在 C++ 中,此表达式是从左到右求值的,因此首先是乘法,然后是除法。 如果 rand() returns 大于 INT_MAX / (N - 1) 的值,此操作会溢出导致未定义的行为。在大多数实现中,由于整数值的二进制补码表示,结果可能是负值。

之后,执行 整数 除以 RAND_MAX,因此对于满足 -RAND_MAX < x < [=41 的任何值 x =] 结果为 0.

你可以看到here你的程序(我只加了一行来证明我的观点)编译并执行了。请注意索引不为零的次数。

在C语言中,使用rand()生成一个0到N(不包括)之间的随机数的常用方法是:

int number = rand() % N;

还要考虑一个更好的算法来打乱数组,例如 Fisher Yates,您可以在 C 中将其实现为:

void my_c_shuffle(bool *arr, size_t n)
{
    while ( n > 1 )
    {
        size_t choice = rand() % n;
        --n;
        bool temp = arr[n];
        arr[n] = arr[choice];
        arr[choice] = temp;
    }
}

在 C++ 中,您应该使用标准库而不是重写这些算法:

#include <iostream>
#include <random>
#include <array>
#include <algorithm>

int main()
{
    std::random_device rd;
    std::mt19937 g(rd());

    std::array<bool, 10000> my_array;
    auto middle = my_array.begin() + my_array.size() / 2;
    std::fill(my_array.begin(), middle, false);
    std::fill(middle, my_array.end(), true);

    std::shuffle(my_array.begin(), my_array.end(), g);  
}