C++ C+中PRNG的默认随机引擎+;为类的每个实例生成相同的输出-正确的种子?

C++ C+中PRNG的默认随机引擎+;为类的每个实例生成相同的输出-正确的种子?,c++,class,random,chrono,random-seed,C++,Class,Random,Chrono,Random Seed,我对伪随机数生成(PRNG)没有经验,但最近我一直在考虑它,因为我想测试一些东西,手动生成数据很困难,坦率地说,很容易出错 我有以下课程: #include <QObject> #include <QList> #include <QVector3D> #include <random> #include <functional> // TaskCommData is part of a Task instance (a QRunna

我对伪随机数生成(PRNG)没有经验,但最近我一直在考虑它,因为我想测试一些东西,手动生成数据很困难,坦率地说,很容易出错

我有以下课程:

#include <QObject>
#include <QList>
#include <QVector3D>
#include <random>
#include <functional>

// TaskCommData is part of a Task instance (a QRunnable).
// It contains all the data required for partially controlling the runnable
// and what it processes inside its run() method
class TaskCommData : public QObject
{
    friend class Task;
    Q_OBJECT
    // Property is used to abort the run() of the Task and also signal the TaskManager that the Task has changed its running status
    Q_PROPERTY(bool running
               READ isRunning
               WRITE setRunningStatus
               NOTIFY signalRunningStatusChanged)
public:
    QString getId() const;  // Task ID
    bool isRunning() const;
signals:
    void signalRunningStatusChanged(QString id, bool running);
public slots:
    void slotAbort();
private:
    bool running;
    QList<QVector3D> data; // Some data in the form of a list of 3D vectors
    QString id;

    // PRNG related members
    std::default_random_engine* engine;
    std::uniform_int_distribution<>* distribution;
    std::function<int()> dice;

    // Private constructor (don't allow creation of TaskCommData outside the Task class which instantiates the class as its class member
    explicit TaskCommData(QString id, QObject *parent = 0);

    void setRunningStatus(bool running);
    QList<QVector3D>* getData();
    void generateData();
};
初始化后,我从
qDebug()
获得以下输出(我创建了10个
Task
实例,其中实例化了
TaskCommData
,每个任务一个实例):

正如您可能从查看输出中猜到的那样,我希望有更多的多样性(显然,由于单个数据块(a
QVector3D
)包含3个二进制值,所以不可能有那么多的多样性),这里显然出了问题

您可能还注意到输出中的
(sleep:…)
。它是来自我的
TaskManager
类的输出,该类创建了一组
Task
s及其各自的
TaskCommData
s:

void TaskManager::initData()
{
    // Setup PRNG
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution(0,10000); // Between 0 and 10000ms
    auto dice = std::bind(distribution, generator);

    this->tasks.reserve(this->taskCount);
    qDebug() << "Adding" << this->taskCount << "tasks...";
    int msPauseBetweenChunks = 0;

    for(int taskIdx = 0; taskIdx < this->taskCount; ++taskIdx) {
        msPauseBetweenChunks = dice();
        Task* task = new Task("task_" + QString::number(taskIdx), msPauseBetweenChunks);
        task->setAutoDelete(false);
        const TaskCommData *taskCommData = task->getCommData();

        // Manage connections
        connect(taskCommData, SIGNAL(signalRunningStatusChanged(QString, bool)),
                this, SLOT(slotRunningStatusChanged(QString, bool)));
        connect(this, SIGNAL(signalAbort()),
                taskCommData, SLOT(slotAbort()));
        this->tasks.insert(task->getCommData()->getId(), task);
        qDebug() << "Added task " << task->getCommData()->getId() << " (sleep: " << msPauseBetweenChunks << ")";
    }

    emit signalCurrentlyRunningTasks(this->tasksRunning, this->taskCount);
}
我得到了一个稍微好一点的结果:

Setting PRNG engine to seed 1473233571281947000
"Task task_0: Generated data [[1,0,0][0,1,1][0,0,0][0,1,1][1,0,0][1,0,0][0,0,1][1,1,1][1,0,0][1,0,0]]"
Added task  "task_0"  (sleep:  0 )
Setting PRNG engine to seed 1473233571282947700
"Task task_1: Generated data [[1,0,1][1,0,0][1,0,1][0,0,1][1,1,0][0,0,1][0,0,1][0,1,0][0,1,0][0,1,0]]"
Added task  "task_1"  (sleep:  1315 )
Setting PRNG engine to seed 1473233571282947700
"Task task_2: Generated data [[1,0,1][1,0,0][1,0,1][0,0,1][1,1,0][0,0,1][0,0,1][0,1,0][0,1,0][0,1,0]]"
Added task  "task_2"  (sleep:  7556 )
Setting PRNG engine to seed 1473233571283948400
"Task task_3: Generated data [[0,0,1][1,0,1][0,1,1][1,1,1][1,0,0][0,0,0][0,0,1][1,1,0][0,1,1][0,0,1]]"
Added task  "task_3"  (sleep:  4586 )
Setting PRNG engine to seed 1473233571283948400
"Task task_4: Generated data [[0,0,1][1,0,1][0,1,1][1,1,1][1,0,0][0,0,0][0,0,1][1,1,0][0,1,1][0,0,1]]"
Added task  "task_4"  (sleep:  5328 )
Setting PRNG engine to seed 1473233571284950700
"Task task_5: Generated data [[0,0,0][1,1,0][0,0,1][0,0,1][0,1,1][1,0,0][1,0,0][1,0,1][0,0,0][0,0,0]]"
Added task  "task_5"  (sleep:  2189 )
Setting PRNG engine to seed 1473233571284950700
"Task task_6: Generated data [[0,0,0][1,1,0][0,0,1][0,0,1][0,1,1][1,0,0][1,0,0][1,0,1][0,0,0][0,0,0]]"
Added task  "task_6"  (sleep:  470 )
Setting PRNG engine to seed 1473233571285950800
"Task task_7: Generated data [[0,0,0][1,0,0][0,1,1][1,0,0][1,0,1][0,1,0][1,0,1][0,1,0][1,1,0][0,0,1]]"
Added task  "task_7"  (sleep:  6789 )
Setting PRNG engine to seed 1473233571285950800
"Task task_8: Generated data [[0,0,0][1,0,0][0,1,1][1,0,0][1,0,1][0,1,0][1,0,1][0,1,0][1,1,0][0,0,1]]"
Added task  "task_8"  (sleep:  6793 )
Setting PRNG engine to seed 1473233571286950900
"Task task_9: Generated data [[1,0,1][1,1,1][1,0,0][1,1,0][0,1,1][0,0,0][1,0,1][1,0,1][0,0,0][1,0,1]]"
Added task  "task_9"  (sleep:  9347 )
尽管仍有太多重复(似乎生成了相同的数据对)。这还有一个巨大的缺点,即它与创建
TaskCommData
对象的速度以及创建该类的两个实例之间的间隔有关。创建速度越快,使用
std::chrono::system\u clock::now()
)测量的差异越小。这似乎不是一个产生种子的好方法(当然我可能错了:D)


你知道怎么解决这个问题吗?即使问题出在种子上,我仍然不明白为什么在
TaskManager::initData()
中,事情运行得很好,而在这里没有太多问题。

因此,是的,第一种情况是正确的:如果您使用相同(默认)种子为所有PRNG种子,它们必须产生相同的数字序列。这就是他们设计的目的

在第二种情况下,如果使用基于时间的种子,您会注意到这也不起作用,因为您实际上只得到三个不同的种子值,而且您也会注意到这并不奇怪,因为不同的种子几乎是在同一时间生成的。因此,这是另一个说明为什么基于时间的种子通常是不好的例子。我真的不知道我们为什么还要教这个。基于时间的播种是一个好主意的情况实际上非常罕见,如果我想一想的话,只要你需要一些实际上无法从外部预测的东西。如果你实际上不需要不可预测,任何静态种子都可以

所以,这里是这样的:简单地用你的任务号作为种子怎么样?这样,您就可以保证拥有与任务一样多的不同PRN序列。如果您需要在不同的运行中使用不同的值,您仍然可以首先获取一个基于时间的随机数(或者,更好的方法是:向您的操作系统请求一个随机数!),然后将任务数添加到该随机数中,再次保证会有不同的序列



基于时间的播种一直是大量未经授权访问背后的安全问题。典型示例:某些连接到internet的过程控制系统具有需要登录的web界面。然后,您会得到一个带有秘密会话ID的cookie。唯一的问题是,该会话ID只是一个随机数,受已知“stringifier”的约束,并且RNG是根据实际用户登录时的时间播种的。由于通常很容易确定设备时间,也很容易猜测可能发生登录的时间范围,因此该会话ID远非秘密,通常只需少量尝试即可强制执行。

使用时间(QTime)如何基于qsrand而不是默认的随机引擎?@Sean83这会解决什么问题?感谢您的优雅解决方案。我刚刚从我的
id
中提取了这个数字,并将其用作种子,它就像一个符咒。另外,感谢您详细解释为什么基于时间的种子不是一个好主意,尽管在我的例子中,安全性不是一个问题。不客气!我希望我发明了这个例子——遗憾的是,它完全是从现实中提取出来的。是的,它听起来很有道理,所以我认为它实际上是真实的D
void TaskManager::initData()
{
    // Setup PRNG
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution(0,10000); // Between 0 and 10000ms
    auto dice = std::bind(distribution, generator);

    this->tasks.reserve(this->taskCount);
    qDebug() << "Adding" << this->taskCount << "tasks...";
    int msPauseBetweenChunks = 0;

    for(int taskIdx = 0; taskIdx < this->taskCount; ++taskIdx) {
        msPauseBetweenChunks = dice();
        Task* task = new Task("task_" + QString::number(taskIdx), msPauseBetweenChunks);
        task->setAutoDelete(false);
        const TaskCommData *taskCommData = task->getCommData();

        // Manage connections
        connect(taskCommData, SIGNAL(signalRunningStatusChanged(QString, bool)),
                this, SLOT(slotRunningStatusChanged(QString, bool)));
        connect(this, SIGNAL(signalAbort()),
                taskCommData, SLOT(slotAbort()));
        this->tasks.insert(task->getCommData()->getId(), task);
        qDebug() << "Added task " << task->getCommData()->getId() << " (sleep: " << msPauseBetweenChunks << ")";
    }

    emit signalCurrentlyRunningTasks(this->tasksRunning, this->taskCount);
}
// ...
std::chrono::nanoseconds nanoseed = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
qDebug() << "Setting PRNG engine to seed" << nanoseed.count();
this->engine = new std::default_random_engine();
this->engine->seed(nanoseed.count());
this->distribution = new std::uniform_int_distribution<int>(0, 1);
this->dice = std::bind(*this->distribution, *this->engine);

generateData();
// ...
Setting PRNG engine to seed 1473233571281947000
"Task task_0: Generated data [[1,0,0][0,1,1][0,0,0][0,1,1][1,0,0][1,0,0][0,0,1][1,1,1][1,0,0][1,0,0]]"
Added task  "task_0"  (sleep:  0 )
Setting PRNG engine to seed 1473233571282947700
"Task task_1: Generated data [[1,0,1][1,0,0][1,0,1][0,0,1][1,1,0][0,0,1][0,0,1][0,1,0][0,1,0][0,1,0]]"
Added task  "task_1"  (sleep:  1315 )
Setting PRNG engine to seed 1473233571282947700
"Task task_2: Generated data [[1,0,1][1,0,0][1,0,1][0,0,1][1,1,0][0,0,1][0,0,1][0,1,0][0,1,0][0,1,0]]"
Added task  "task_2"  (sleep:  7556 )
Setting PRNG engine to seed 1473233571283948400
"Task task_3: Generated data [[0,0,1][1,0,1][0,1,1][1,1,1][1,0,0][0,0,0][0,0,1][1,1,0][0,1,1][0,0,1]]"
Added task  "task_3"  (sleep:  4586 )
Setting PRNG engine to seed 1473233571283948400
"Task task_4: Generated data [[0,0,1][1,0,1][0,1,1][1,1,1][1,0,0][0,0,0][0,0,1][1,1,0][0,1,1][0,0,1]]"
Added task  "task_4"  (sleep:  5328 )
Setting PRNG engine to seed 1473233571284950700
"Task task_5: Generated data [[0,0,0][1,1,0][0,0,1][0,0,1][0,1,1][1,0,0][1,0,0][1,0,1][0,0,0][0,0,0]]"
Added task  "task_5"  (sleep:  2189 )
Setting PRNG engine to seed 1473233571284950700
"Task task_6: Generated data [[0,0,0][1,1,0][0,0,1][0,0,1][0,1,1][1,0,0][1,0,0][1,0,1][0,0,0][0,0,0]]"
Added task  "task_6"  (sleep:  470 )
Setting PRNG engine to seed 1473233571285950800
"Task task_7: Generated data [[0,0,0][1,0,0][0,1,1][1,0,0][1,0,1][0,1,0][1,0,1][0,1,0][1,1,0][0,0,1]]"
Added task  "task_7"  (sleep:  6789 )
Setting PRNG engine to seed 1473233571285950800
"Task task_8: Generated data [[0,0,0][1,0,0][0,1,1][1,0,0][1,0,1][0,1,0][1,0,1][0,1,0][1,1,0][0,0,1]]"
Added task  "task_8"  (sleep:  6793 )
Setting PRNG engine to seed 1473233571286950900
"Task task_9: Generated data [[1,0,1][1,1,1][1,0,0][1,1,0][0,1,1][0,0,0][1,0,1][1,0,1][0,0,0][1,0,1]]"
Added task  "task_9"  (sleep:  9347 )