【问题标题】:How can I write a random number generator function?如何编写随机数生成器函数?
【发布时间】:2020-07-24 02:25:34
【问题描述】:

故事:在C++中使用rand()函数可以得到随机数,使用%可以设置范围。但是,如果我还想添加偏差,那么我还必须在结果中添加偏差。这是太多的工作要做,所以我决定编写自己的函数来处理这个问题。但是我被困在某一点上。

我知道,每次程序运行时我都必须输入一个新序列(srand(time(NULL))),否则我会一直得到相同的数字。

最明显的方法是在main() 函数中插入srand(time(NULL))。但是,我不想这样做,我希望它以某种方式在我包含 .h 文件时自动完成。

假设myFunctions.h:

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <ctime>

int randint(int start, int end);

myFunctions.cpp:

#include "myFunctions.h"

/* [start,end) */
int randint(int start, int end)
{

    return (rand() % (end - start)) + start;

}

现在,我很困惑应该在哪里添加srand()。如果我在randint() 定义中这样做,我假设,因为时间差太小,time(NULL) 将为循环的每个步骤评估相同的值,并且它会一直提供相同的种子我想在很短的时间内得到随机数,比如:

for(int i = 0; i < 50; i++){
    std::cout << randint(0, 3) << std::endl;
}

输出是相同的数字,50 次。所以这证实了我的猜想。

我在我的 randint() 定义中尝试过类似的东西,

int randint(int start, int end)
{

    #ifndef SEED
        srand(time(NULL));
        #define SEED
    #endif 

    return (rand() % (end - start)) + start;

}

但是,它也不起作用,因为我认为 #ifndef 只在预处理阶段执行一次。

所以,在这些尝试之后,我尝试在我的 .h 文件的开头调用 srand(),但我发现事实上你不能在任何函数之外调用函数(例如main() .)

简而言之,我现在陷入困境,任何帮助将不胜感激。

【问题讨论】:

  • 只需在程序开始时手动调用srand()
  • @michaeldel 是的,它解决了,但所有这些问题都是关于避免它,如果可能的话。
  • 不要使用rand。使用en.cppreference.com/w/cpp/header/randomrand 是 C 时代的老函数,应该避免使用。
  • @Mannoj 如果你检查我的问题,你会发现我理解这些东西没有问题......
  • @ThomasSablik 我会看看,谢谢。

标签: c++ function random


【解决方案1】:

如果我是你,我会实现一个类来包装所有函数。你可以称它为 randomGen 或类似的名称,然后在该类中包含你想要使用的所有函数。然后包含一个调用srand() 的构造函数。然后你可以在 main 中创建一个 randomGen 对象,然后你可以继续生成你的随机数。

【讨论】:

  • 如果在 main 中调用构造函数是可取的,那么我只需将所有内容放在一个 config() 函数中并调用它而不是创建一个类。谢谢你的回答,虽然
  • 我认为没有其他方法可以实现这一点,您也许可以使用预处理器命令做一些事情,但解决方案会很混乱。谢谢!
【解决方案2】:

使用从 C++11 开始提供的 call_once 函数。这需要使用&lt;mutex&gt;。您必须创建一个once_flag 辅助对象。 f 必须是静态的,因此它通过函数调用持续存在。此示例使用 lambda,但您可以轻松地在此之上创建一个名为 seedrand() 或其他任何内容的常规旧函数,并将其作为参数传递。

示例取自 https://en.cppreference.com/w/cpp/thread/call_once 并经过修改以适合您的程序。

#include <iostream>
#include <mutex>

int randint(int start, int end)
{
    static std::once_flag f;
    std::call_once(f, []() {srand(time(NULL)); std::cout << "Random Seeded\n"; });

    return (rand() % (end - start)) + start;
}

int main(int argc, char** argv)
{
    for (int i = 0; i < 10; i++)
    {
        std::cout << i << ": " << randint(1, 100) << "\n";
    }
}

如果您想直接将srand 作为函数传递,则必须将其参数作为附加参数传递。注意srand 后面缺少()。您不想将srand 的RESULT 传递给call_once,您想传递函数本身。 call_once 会为你打电话。

static std::once_flag f;
std::call_once(f, srand, time(NULL)); // notice the lack of () after srand

编辑:std::call_once 太过分了

你需要用pthread 编译这个事实是荒谬的。 std::call_once 只有在你有多个线程调用同一段代码并且只希望它执行一次的情况下才应该用于执行此类操作。

更简单的解决方案是只使用静态布尔值:

int randint(int start, int end)
{
    static bool seeded = false;
    if (!seeded)
    {
        srand(time(NULL));
        seeded = true;
    }

    return (rand() % (end - start)) + start;
}

【讨论】:

  • 优秀的答案!多谢。但是,我无法理解一部分,为什么我不能直接std::call_once(f, srand(time(NULL))) ?我其实不知道[]() {...} 是什么,我应该用谷歌搜索它是什么?
  • 这里是问题信息:no instance of function template "std::call_once" matches the argument list -- argument types are: (std::once_flag, void)。我猜它不喜欢srand()void 返回类型。但我找不到它想要什么类型的回报
  • 这就是所谓的“lambda”。这是一个就地函数。
  • 如果您想调用srand,或任何其他接受参数的函数,您需要将这些参数作为附加参数传递给call_once 函数。我用这种方法编辑了我的答案。
  • 嘿,既然我问你了,我一直在处理它,正如你刚才所说,我发现我必须按照你在回答中写的那样使用它。再次感谢顺便说一句。但是,您是否尝试过使用该格式的代码?因为当我尝试它时,它会抛出一个错误:terminate called after throwing an instance of 'std::system_error' what(): Unknown error -1 Aborted (core dumped)。如果您尝试过,请告诉我它是否适用于您的情况?
猜你喜欢
  • 1970-01-01
  • 2013-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 1970-01-01
  • 2023-01-03
  • 1970-01-01
相关资源
最近更新 更多