关于唯一值生成的建议

时间:2016-12-22 11:49:08

标签: c random

我想生成一系列“随机”唯一数字,用于纸牌游戏!这些数字应该在0到81之间。 在这个阶段我不关心安全性或速度,我只想要一些简单的工作。

在我的下面的代码中,我设法在数组中创建了2个唯一的随机数,但其余的10个数字不会改变,但保留-1作为初始值。 我已经找到了更准确的随机数生成方法,但我会在稍后阶段检查它们!

ImageView mImageViewFilling = (ImageView) findViewById(R.id.animated_imageview);
((AnimationDrawable) mImageViewFilling.getBackground()).start();

2 个答案:

答案 0 :(得分:1)

您的代码有我见过的一种奇怪的缩进和支撑布局方案:

#include <stdio.h>
#include <stdlib.h>

int getRandomNumber(int Min, int Max)
{
    double rnd= (double)rand()/((double)RAND_MAX+1);
    return   (int)(rnd*(Max-Min+1))+Min;
}

int main()
{
    int j,counter,temp,deck[13];
    srand(time(NULL));
    int i;
    counter=1;
    for (i=0;i<12;i++)
        {deck[i]=-1;
        temp = getRandomNumber(0,81);

        for (j=0;j<=i;j++)
            {if (temp==deck[j])
                {counter=0;}
            if (counter!=0)
                deck[i]=temp;
            }
        }
    for(i=0;i<12;i++)
        printf("%d ",deck[i]);

}

转换为更正统的风格(奥尔曼,或多或少 - 见Indent style上的维基百科),你得到:

#include <stdio.h>
#include <stdlib.h>

static int getRandomNumber(int Min, int Max)
{
    double rnd = (double)rand() / ((double)RAND_MAX + 1);
    return (int)(rnd * (Max - Min + 1)) + Min;
}

int main(void)
{
    int j, counter, temp, deck[13];
    srand(time(NULL));
    int i;
    counter = 1;
    for (i = 0; i < 12; i++)
    {
        deck[i] = -1;
        temp = getRandomNumber(0, 81);

        for (j = 0; j <= i; j++)
        {
            if (temp == deck[j])
            {
                counter = 0;
            }
            if (counter != 0)
                deck[i] = temp;
        }
    }
    for (i = 0; i < 12; i++)
        printf("%d ", deck[i]);
}

需要staticint main(void)才能使代码超出我的默认编译选项;否则,他们会化妆。

现在我们可以看到一些问题。在外循环之外,counter设置为1一次;它有时在循环内设置为0,但一旦发生,它永远不会重置为1,因此deck不再添加其他数字。你需要重做内循环,可能是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static int getRandomNumber(int Min, int Max)
{
    double rnd = (double)rand() / ((double)RAND_MAX + 1);
    return (int)(rnd * (Max - Min + 1)) + Min;
}

int main(void)
{
    int deck[13];
    srand(time(NULL));
    for (int i = 0; i < 12; i++)
    {
        int temp = getRandomNumber(0, 81);
        deck[i] = -1;

        int counter = 1;
        for (int j = 0; j <= i; j++)
        {
            if (temp == deck[j])
            {
                counter = 0;
                break;
            }
        }
        if (counter != 0)
            deck[i] = temp;
        else
            i--;  // Try again with a new random choice for the same i
    }
    const char *pad = "";
    for (int i = 0; i < 12; i++)
    {
        printf("%s%d", pad, deck[i]);
        pad = " ";
    }
    putchar('\n');
    return 0;
}

我不喜欢拖尾空白,因此打印循环需要注意确保没有任何空白。

使用getRandomNumber()的示例输出 - 在macOS Sierra 10.12.2上使用GCC 6.3.0:

7 73 38 61 11 13 41 66 29 39 72 20
7 2 18 17 54 31 45 40 34 22 63 16
7 13 80 54 16 49 14 58 28 53 23 26
7 24 60 10 67 53 69 32 23 2 66 12
7 34 40 48 21 3 57 43 6 18 27 80
7 45 20 3 65 21 61 17 12 69 66 27
7 67 62 78 70 57 68 46 9 2 72 39
7 77 41 34 32 75 72 20 64 78 33 25
7 6 21 72 76 11 75 38 73 27 64 33
7 17 1 27 37 28 80 49 12 67 59 36

第一个数字并非随机 - 在测试序列之后不久,它从7变为8,但同样是非随机的。替代 产生随机数的方法是:

static int getRandomNumber(int Min, int Max)
{
    int rnd;
    int range = (Max - Min + 1);

    while ((rnd = rand()) > RAND_MAX - (RAND_MAX % range))
        ;

    return (rnd % range) + Min;
}

这避免了82不能精确划分RAND_MAX的事实的偏差,这会使较低数字的分布加权略高于0..81范围内的较高数字。它还避免了意外一致的第一个数字,尽管当测试以1秒的间隔运行时,新的第一个数字也是半可预测的。

示例结果:

48 33 28 78 14 2 81 13 23 75 38 40
45 42 74 1 11 68 17 33 78 49 23 80
42 51 38 3 5 52 35 56 54 23 59 41
39 60 2 8 36 53 79 30 72 75 62 37
36 69 45 10 78 20 71 17 6 53 54 30
33 78 9 15 75 7 40 61 27 36 70 68
30 5 55 17 69 73 25 63 37 1 21 71
27 14 19 66 57 43 1 13 3 65 71 21
24 26 62 63 41 61 68 28 67 20 74 17
21 35 26 57 28 79 47 44 2 52 60 77

请注意,第一个数字减少3;第二个似乎增加了9;呃 - 随机性并不是那么好。众所周知,rand()通常不是高质量的PRNG(伪随机数发生器),但我对这种明显的系统行为感到惊讶,种子每次相差1。

在我的Mac上,当我将srand()更改为srandom()rand()更改为random()时,我的结果变得更好(因为更不可预测):

29 1 7 11 25 52 63 15 26 55 75 64
40 4 64 18 8 57 73 27 38 15 60 28
43 3 27 17 1 58 26 72 73 18 20 7
76 16 27 43 64 20 63 30 35 17 33 57
79 47 32 33 6 30 35 7 38 55 25 61
69 57 79 75 15 54 5 35 21 46 65 61
30 79 66 14 56 39 19 8 50 47 76 33
62 65 81 44 52 39 25 30 54 12 24 68
27 49 60 72 53 35 14 41 63 46 45 65
67 39 9 11 60 19 64 73 43 17 21 26

random()的Mac手册页仍建议使用arc4random(),但这比普通rand()好很多。你在其他系统上找到的将取决于系统提供的设施 - rand()可能不像在Mac上看起来那么糟糕。基本上,要谨慎选择PRNG - 特别是如果您要使用系统生成的种子(例如当前时间)。

答案 1 :(得分:0)

出于建议的目的(生成0到81之间的随机数字序列,其中每个项目与其他项目不同),您将需要一种特定类型的随机数生成器(RNG),一种可以生成所有可能的随机数生成器82个项目的排列(表示为82!或82个阶乘)。但是,只有有限的RNG才能做到这一点。特别是C rand()函数的实现是未指定的,因此不能保证生成那么多的排列。

伪随机数生成器(PRNG,这里使用的一种RNG)不能生成比其周期更多的随机数序列。对于82!排列,没有周期小于82!的PRNG可以做到这一点(2的最高幂2是2 408 ,这意味着生成器需要在至少408位,或51字节,很可能这样做 - 而51字节比srand通常要大得多。或者,为此目的,建议您使用生成“不可预测”数字的RNG,C语言和C库都不包含标准。有关详细信息,请参阅my article on randomness中的“随机播放”和“不可预知的RNG”。