在Xcode中rand()的奇怪行为

时间:2017-10-22 17:26:13

标签: c xcode random srand

在使用Apple Xcode时,我在下面包含的C程序将始终返回5表示y,但z将返回一个随机数,即使它们具有相同的表达式。你能找出这种行为的原因吗?

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

int main()
{
    int x,y,z;
    srand((unsigned int)time(NULL));
    x = ((unsigned int)time(NULL));
    y=((double)rand()/(double)(RAND_MAX)*10);
    z=((double)rand()/(double)(RAND_MAX)*10);
    printf("Time %d\n", x);
    printf("\n");
    printf("Number y %d\n", y);
    printf("\n");
    printf("Number z %d\n", z);
    printf("\n");
    return 0;
}

4 个答案:

答案 0 :(得分:2)

<强> rand-31

以下是对您的计划的改编,其中显示了正在发生的事情的更多细节:

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

int main(void)
{
    int x = ((unsigned int)time(NULL));
    srand((unsigned int)x);
    int r1 = rand();
    int y  = ((double)r1/(double)(RAND_MAX)*10);
    int r2 = rand();
    int z  = ((double)r2/(double)(RAND_MAX)*10);
    printf("Time %d\n", x);
    printf("Random 1: %d\n", r1);
    printf("Number y: %d\n", y);
    printf("Random 2: %d\n", r2);
    printf("Number z: %d\n", z);
    return 0;
}

它独立于计算捕获两个rand()调用的结果,以便可以打印它们。它还确保传递给time()的值与打印的值相同,尽管它们不同的可能性非常小。

当我跑几次时,我得到了输出:

Time 1508694677
Random 1: 1292016210
Number y: 6
Random 2: 1709286653
Number z: 7

Time 1508694685
Random 1: 1292150666
Number y: 6
Random 2: 1821604998
Number z: 8

Time 1508694701
Random 1: 1292419578
Number y: 6
Random 2: 2046241688
Number z: 9

Time 1508694841
Random 1: 1294772558
Number y: 6
Random 2: 790587255
Number z: 3

如您所见,rand()返回的值不同,但差别不大,因此计算出的值并非完全不同。

<强> rand-23

一个简单的升级选项是使用drand48()功能,如下所示:

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

int main(void)
{
    int x = ((unsigned int)time(NULL));
    srand48((unsigned int)x);
    int r1 = lrand48();
    int y  = ((double)r1/(double)(RAND_MAX)*10);
    int r2 = lrand48();
    int z  = ((double)r2/(double)(RAND_MAX)*10);
    printf("RAND_MAX: %d\n", RAND_MAX);
    printf("Time:     %d\n", x);
    printf("Random 1: %d\n", r1);
    printf("Number y: %d\n", y);
    printf("Random 2: %d\n", r2);
    printf("Number z: %d\n", z);
    return 0;
}

代码打印RAND_MAX主要是为了表明它与lrand48()返回的值范围兼容,记录为返回0..2 31 <范围内的值/ SUP> -1。

这给出了不同的,可以说是更好的结果:

RAND_MAX: 2147483647
Time:     1508695069
Random 1: 705270938
Number y: 3
Random 2: 1758232243
Number z: 8

RAND_MAX: 2147483647
Time:     1508695074
Random 1: 1465504939
Number y: 6
Random 2: 733780153
Number z: 3

RAND_MAX: 2147483647
Time:     1508695080
Random 1: 1948289010
Number y: 9
Random 2: 1222424564
Number z: 5

<强> rand-13

或者,您可以执行macOS文档所说的内容并切换到arc4random()。链接是“遗留的”XCode 5文档,但最近功能没有改变。

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

int main(void)
{
    int x = ((unsigned int)time(NULL));
    int r1 = arc4random_uniform(INT_MAX);
    int y  = ((double)r1/(double)(RAND_MAX)*10);
    int r2 = arc4random_uniform(INT_MAX);
    int z  = ((double)r2/(double)(RAND_MAX)*10);
    printf("INT_MAX:  %d\n", INT_MAX);
    printf("RAND_MAX: %d\n", RAND_MAX);
    printf("Time:     %d\n", x);
    printf("Random 1: %d\n", r1);
    printf("Number y: %d\n", y);
    printf("Random 2: %d\n", r2);
    printf("Number z: %d\n", z);
    return 0;
}

这使用arc4random_uniform()生成0..INT_MAX范围内的值,该范围与生成的其他函数相同。与srand()函数族arc4random()类似。

样品运行:

INT_MAX:  2147483647
RAND_MAX: 2147483647
Time:     1508695894
Random 1: 938614006
Number y: 4
Random 2: 851262647
Number z: 3

INT_MAX:  2147483647
RAND_MAX: 2147483647
Time:     1508695900
Random 1: 1332829945
Number y: 6
Random 2: 386007903
Number z: 1

INT_MAX:  2147483647
RAND_MAX: 2147483647
Time:     1508695913
Random 1: 1953583540
Number y: 9
Random 2: 1273643162
Number z: 5

<强> rand-83

请注意,如果您想要一个0到10之间的随机整数,那么您应该直接使用arc4random_uniform(10)而不进行计算。

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

int main(void)
{
    int x = ((unsigned int)time(NULL));
    int y = arc4random_uniform(10);
    int z = arc4random_uniform(10);
    printf("Time:     %d\n", x);
    printf("Number y: %d\n", y);
    printf("Number z: %d\n", z);
    return 0;
}

示例输出:

Time:     1508696162
Number y: 5
Number z: 3

Time:     1508696163
Number y: 7
Number z: 5

Time:     1508696165
Number y: 3
Number z: 8

当我设法在一秒钟内运行程序3次时,显示arc4random()等的另一个优点。对于其他生成器,由于使用相同的种子,这些生成器会产生相同的结果。

$ rand-83;rand-83;rand-83
Time:     1508696213
Number y: 4
Number z: 0
Time:     1508696213
Number y: 0
Number z: 1
Time:     1508696213
Number y: 9
Number z: 6
$

$ rand-31;rand-31;rand-31
Time 1508696334
Random 1: 1319865409
Number y: 6
Random 2: 1619339200
Number z: 7
Time 1508696334
Random 1: 1319865409
Number y: 6
Random 2: 1619339200
Number z: 7
Time 1508696334
Random 1: 1319865409
Number y: 6
Random 2: 1619339200
Number z: 7
$

JFTR:在运行macOS High Sierra 10.13的MacBook Pro上编译的代码。我使用GCC 7.2.0(而不是XCode 9)来编译,但系统库 - 这是一个关键的库。

答案 1 :(得分:0)

你刚学到了一个重要的教训:

请勿使用time(NULL)为随机数生成器播种。

是的,我知道“每个人都这样做”。这并没有使它正确。 (事实上​​,“每个人”都没有这样做,虽然它在示例代码中非常常见。在代码中实际上需要随机数用于生产目的,这种情况非常罕见 - 甚至在不常见的情况下也是如此。) / p>

事实是,“自纪元以来的秒数”是一个非常随机的数字。前六位需要一整年才能改变;几乎一天,只有低16位改变,并且只有一秒 - 这足以启动数千个进程 - 它根本不会改变。如果您使用相同的种子,保证能够获得相同的随机序列,那么显然您需要更加努力地工作以确保您拥有更随机的种子。

那该怎么办?如今,大多数操作系统都有一些生成真正随机数的机制(或者物理定律允许的随机数)。在许多Unix和类Unix系统上 - 包括Mac OS X和Linux - 你可以通过从/dev/random读取四个字节来获得一个非常好的无符号32位随机数。对于你需要的每个随机数来说,这太贵了,但它作为种子非常好:

uint32_t seed = 0;
FILE* rf = fopen("/dev/random", "r");
if (rf != NULL) {
  if (fread(seed, sizeof seed, 1, rf) != 1) seed = 0;
  fclose(rf);
}
if (!seed) { /* Fallback to some other mechanism */ }
srand(seed);

独立于播种,rand通常不是一个非常好的随机数生成器,特别是在RAND_MAX为32767的实现中。但是没有随机数生成器,无论它有多好,都可以补偿由于缺乏良好的播种策略。

(有些库会自动播种,但是有充分的理由说明为什么手动播种是界面的一部分:您可能需要重新运行相同的随机数序列。例如,如果您使用PRNG生成随机测试数据并且您的测试程序的一些运行会触发错误,您将需要使用相同的随机数序列重新运行程序以验证错误已得到修复。一个简单的策略是使用命令行标志来提供显式随机数种子,并确保测试工具始终打印产生结果的随机数种子。)

答案 2 :(得分:0)

从程序中删除(不需要的)混乱后,请输入以下代码:

  1. 干净地编译
  2. 消除了不需要的局部变量
  3. 执行所需的功能。
  4. 清楚地揭示了time()rand()函数实际发生的情况。
  5. 记录每个头文件包含的原因
  6. 现在建议的代码:

    #include <stdio.h>  // printf()
    #include <time.h>   // time()
    #include <stdlib.h> // srand(), rand()
    
    int main( void )
    {
        srand((unsigned int)time(NULL));
    
        printf("Time %u\n\n", (unsigned int)time(NULL) );
    
        printf("first call to rand:  %d\n\n", rand());
    
        printf("second call to rand: %d\n\n", rand());
    
        return 0;
    }
    

    建议代码的典型运行产生类似于以下的输出:

    Time 1508783846
    
    first call to rand:  1113266715
    
    second call to rand: 810898123
    

答案 3 :(得分:0)

我实际上设法识别问题并且它与使用Xcode 9.1 beta有关,由于某种原因它破坏了srand和rand()。恢复到Xcode 9.0.1之后,一切都恢复正常。