【问题标题】:Why is my C code only generating every third random number?为什么我的 C 代码只生成每三个随机数?
【发布时间】:2019-08-17 09:51:05
【问题描述】:

我正在尝试模拟蠕虫在由 100,000 台计算机组成的网络中的传播。模拟本身非常简单,我不需要任何帮助,除了出于某种原因,我只得到每三个随机数。

只有索引模1000小于10的计算机才会被感染,所以当1000台计算机被感染时,程序应该被执行。由于某种原因,我的程序只得到 329。当我降低目标编号并检查数组的内容时,只有每三台计算机发生了变化,这是一个一致的模式。例如,在阵列的末尾,只有计算机 98001、98004、98007、99002、99005、99008 被更改,即使中间的计算机(98002、98003 等)也应该更改。该模式一直保持到数组的开头。当我尝试更改所有 1000 时,程序进入无限循环并卡在 329。

编辑:我刚刚发现,如果我将 NETSIZE 降低到 10,000 并将 while 循环中的目标降低到 100,它不会跳过任何内容。这是否意味着问题与舍入误差有关?比我更了解 C 的人一定知道答案。

谢谢。

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

#define NETSIZE 100000

double rand01();
void initNetwork();

unsigned char network[NETSIZE];
int scanrate = 3;
int infectedCount;
int scans;
int ind;
int time;



int main(void) {
    initNetwork();
    time = 0;
    infectedCount = 1;
    while (infectedCount < 1000) { //changing 1000 to 329 stops the infinite loop
        scans = infectedCount * scanrate;
        for (int j = 0; j < scans; j++) {
            ind = (int) (rand01() * NETSIZE);
            if (network[ind] == 0) {
                network[ind] = 1;
                infectedCount++;
            }
        }
        time++;
    }
    for (int k = 0; k < NETSIZE; k++) {
        if (network[k] == 1) printf("%d at %d\n", network[k], k);
    }
}
double rand01() {
    double temp;
    temp = (rand() + 0.1) / (RAND_MAX + 1.0);
    return temp;
}

void initNetwork() {
    for (int i = 0; i < NETSIZE; i++) {
        if (i % 1000 < 10) {
            network[i] = 0;
        } else  {
            network[i] = 2;
        }
    }
    network[1000] = 1;
}

在上面的代码中,我希望代码一直运行到 1000 个易受攻击的索引从 0 变为 1。

【问题讨论】:

  • 请说明您对temp = (rand() + 0.1) / (RAND_MAX + 1.0);这一行的意图和理解。尤其要详细说明可能结果的范围。另请解释为什么您创建一个双随机数,然后将其用作 int。
  • 测试ind是否在if (network[ind] == 0) {之前的范围内
  • 您系统上的RAND_MAX 是什么?如果它是 15 位或 16 位值,则在转换为 double 时可能没有得到足够好的量化。如果它是 31 位或更大的数字,那(可能)不会是问题。您需要研究仅由具有不同种子的 rand01() 函数生成的值,加上乘法并转换为整数 - 只需打印结果和 sort -n | uniq -c 即可查看结果的一致性。 (100,000 / 30,000 大约是 3 倍)。
  • @JonathanLeffler 在我的系统上,RAND_MAX 只有 32767。你认为这可能是我的结果不够精细的原因吗?既然您已经让我考虑过了,那么可能只有 32,767 个可能的值,而我的网络数组是 100,000 个可能的值。这大约相当于我得到的 1/3 结果。
  • 是的,我认为这很可能是问题所在。您想要 100,000 个不同的值,但您的随机数生成器只能生成大约 33,000 个不同的值,这非常接近您的 1:3 指标。你可以试试((rand() &lt;&lt; 15) + rand()) / ((RAND_MAX + 1.0) * (RAND_MAX + 1.0))

标签: c random simulation


【解决方案1】:

将 cmets 转换为答案。

您系统上的 RAND_MAX 是多少?如果它是 15 位或 16 位值,则在转换为双精度时可能没有得到足够好的量化。如果它是 31 位或更大的数字,那(可能)不会是问题。您需要研究仅由具有不同种子的 rand01() 函数生成哪些值,加上乘法并转换为整数 - 只需打印结果和 sort -n | uniq -c 即可查看结果的一致性。

在我的系统上,RAND_MAX 仅为 32767。您认为这可能是我的结果不够精细的原因吗?既然您已经让我考虑过了,那么可能只有 32,767 个可能的值,而我的网络数组是 100,000 个可能的值。这大约相当于我得到的 1/3 结果。

是的,我认为这很可能是问题所在。您需要 100,000 个不同的值,但您的随机数生成器只能生成大约 33,000 个不同的值,这非常接近您的 1:3 指标。它还立即解释了为什么当您将乘数从 100,000 减少到 10,000 时会得到很好的结果。

你可以试试:

double rand01(void)
{
    assert(RAND_MAX == 32767);
    return ((rand() << 15) + rand()) / ((RAND_MAX + 1.0) * (RAND_MAX + 1.0));
}

或者您可以使用替代的随机数生成器——例如,POSIX 定义了drand48() 系列函数和 random(),在需要的地方有相应的种子设置功能。

【讨论】:

  • ((1u*rand()) &lt;&lt; 15) ^ rand() 即使RAND_MAX != 32767 也能很好地工作
【解决方案2】:

是的,我遇到的问题是我系统上的 RAND_MAX 值只有 32767,我正在尝试将其有效地分散到超过 100,000 个值中,结果大约只有三分之一的数字出现。

在我的辩护中,提出 rand01() 函数的人拥有计算机科学博士学位,但我认为他在我们学校的主计算机上运行了此代码,这可能具有更大的 RAND_MAX 值。

@JonathanLeffler 这个解决方案值得称赞。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-25
    • 2023-01-03
    • 2017-09-11
    • 2019-06-04
    相关资源
    最近更新 更多