在C语言中生成“范围内”的随机数

5

我需要生成在区间[0, 10]中的随机数,同时需要满足以下条件:

  • 每个数字只出现一次。
  • 不会重复出现结果。

请问有谁能指导我应该使用哪种算法?


7
生成一个[0, 10)的序列并进行洗牌。 - cnicutar
3
首先,你尝试了什么? 其次,是0-10还是1-10? - Carl Norum
嗨,卡尔,我尝试使用rand()函数生成序列,但没有实现结果。其次,上面提到的范围是0到10。谢谢。 - user731628
@billjoy 作为以后的参考,区间表示法中包括0和10的数字0-10应该写成[0,10]。 - Alex W
@billjoy 你说的“不重复结果”是什么意思? - Eitan T
3个回答

11

Richard J. Ross的答案中的算法是不正确的。它生成的可能排序数量是n^n而不是n!。Jeff Atwood在他的博客上说明了这个问题:http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html

相反,您应该使用Knuth-Fisher-Yates Shuffle算法:

int values[11] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
srand(time(NULL));

for (int i = 10; i > 0; i--)
{
    int n = rand() % (i + 1);

    int temp = values[n];
    values[n] = values[i];
    values[i] = temp;
}

1
这是Knuth洗牌算法还是Fisher-Yates洗牌算法?这里也有相关信息:https://dev59.com/-XM_5IYBdhLWcg3w9oSG - slashmais
在维基百科页面上,它被称为Knuth-shuffle - 我应该阅读介绍,而不仅仅是浏览细节 ;) - slashmais
对于这么小的数据集,这可能并不重要,但请确保您的系统rand函数是一个好的随机数生成器(或者使用标准的生成器,如Mersenne-Twister)。一些C库中的rand调用(希望只有旧版本)存在众所周知的统计问题(http://www.pnas.org/content/61/1/25.full.pdf+html)。 - sfstewman
请注意,因为 rand() % (i + 1) 会产生偏差的数字,所以这个洗牌也会有偏差。 - caf

1

尝试使用这个伪随机数算法:

int values[11] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
srand(time(NULL));

for (int i = 0; i < 11; i++)
{
    int swap1idx = rand() % 11;
    int swap2idx = rand() % 11;

    int tmp = values[swap1idx];
    values[swap1idx] = values[swap2idx];
    values[swap2idx] = tmp;
}

// now you can iterate through the shuffled values array.

请注意,这可能存在模数偏差,但对于您所需的功能应该是可行的。

0
尝试创建一个随机函数,就像这样:

void randomize(int v[], int size, int r_max) {
    int i,j,flag;

    v[0] = 0 + rand() % r_max; // start + rand() % end
    /* the following cycle manages, discarding it,
the case in which a number who has previously been extracted, is re-extracted. */
    for(i = 1; i < size; i++) {
        do {
            v[i]= 0 + rand() % r_max;
            for(j=0; j<i; j++) {
                if(v[j] == v[i]) {
                    flag=1;
                    break;
                }
                flag=0;
            }
        } while(flag == 1);
    }
}

然后,只需调用它并传递一个包含11个元素的数组v[]、它的大小和上限范围:

randomize(v, 11, 11);

由于数组被作为引用参数传递,它将被随机化,没有重复的数,并且数字只出现一次。

在调用randomize之前,请记得调用srand(time(0));,并初始化int v[11]={0,1,2,3,4,5,6,7,8,9,10};


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接