簡體   English   中英

嘗試每次迭代生成唯一的隨機數序列

[英]Trying to produce a unique sequence of random numbers per iteration

如標題所示,每次運行這個小程序時,我都試圖創建一個唯一的隨機數序列。

但是,有時我會得到如下結果:

102
201
102

編碼

#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;

int main() {
        for (int i = 0; i < 3; i++) {
                srand (time(NULL)+i);
                cout << rand() % 3;
                cout << rand() % 3;
                cout << rand() % 3 << '\n' << endl;
        }
}

很顯然,srand沒有我想要的神奇功能。 我希望對此有一個合乎邏輯的破解方法?

Edit1:澄清一下,這只是一個簡單的測試程序,可以大規模實施。 因此,我可以運行1000或更多rand%50,而不是rand%3的3次迭代。 如果我在操作的某個時刻看到102,那么我希望它不再顯示102。

首先,如果要使用srand / rand ,則希望在程序每次執行的開始將其種子一次(並且只能播一次):

int main() {
    srand(time(NULL));
    for (int i = 0; i < 3; i++) {
    cout << rand() % 3;
    cout << rand() % 3;
    cout << rand() % 3 << '\n' << endl;
}

其次, time通常只會產生分辨率為一秒的結果,因此即使進行了此更正,如果您在同一秒內運行兩次程序,則可以預期它在兩次運行中會產生相同的結果。

第三,您還是不想使用srand / rand <random>中的隨機數生成器通常要好得多(也許更重要的是,定義得足夠好,它們代表了眾所周知的數量)。

#include <random>
#include <iostream>

int main() { 
    std::mt19937_64 gen { std::random_device()() };
    std::uniform_int_distribution<int> d(0, 2);

    for (int i = 0; i < 3; i++) {
        for (int j=0; j<3; j++)
            std::cout << d(gen);
        std::cout << "\n";
    }
}

但是,基於編輯,這仍然不夠。 您真正想要的是一個無重復的隨機樣本。 為此,您需要做的不僅僅是生成數字。 隨機生成的數字不僅可以重復,而且如果您生成足夠多的數字,則不可避免地重復(但即使不是不可避免的,重復的可能性也很高)。

只要您生成的結果數量比可能的結果數量少,就可以很容易地在生成結果時將結果存儲在一組中,並且僅將結果視為以前的實際結果即可出現在集合中:

#include <random>
#include <iostream>
#include <set>
#include <iomanip>

int main() {
    std::mt19937_64 gen { std::random_device()() };
    std::uniform_int_distribution<int> d(0, 999);
    std::set<int> results;

    for (int i = 0; i < 50;) {
        int result = d(gen);
        if (results.insert(result).second) {
            std::cout << std::setw(5) << result;
            ++i;
            if (i % 10 == 0)
                std::cout << "\n";
        }
    }
}

如果結果數量接近可能的結果數量,這將變得效率很低。 例如,假設您的生產數字從1到1000(所以可能有1000個結果)。 考慮如果您決定產生1000個結果(即所有可能的結果)會發生什么。 在這種情況下,當您生成最后一個結果時,實際上只剩下一個可能性,而不僅僅是產生一個可能性,而是一個接一個地產生另一個隨機數,直到偶然發現剩下的一個可能性為止。

對於這種情況,有更好的方法來完成這項工作。 例如,您可以從一個包含所有可能數字的容器開始。 要生成輸出,請在該容器中生成一個隨機索引。 您輸出該數字,然后從容器中刪除該數字,然后重復(但是這次,該容器小了一個,因此您將隨機索引的范圍減小了一個)。 這樣,您產生的每個隨機數都會給出一個輸出。

只需對數字數組進行混排, 可以做到這一點 但是,這有兩個缺點。 首先,您需要正確地對其進行洗牌-Fischer-Yates洗牌效果很好,但否則很容易產生偏差。 其次,除非您確實確實使用了數組中的所有(或非常接近所有)數字,否則效率很低。

在極端情況下,請考慮要幾個(例如10個)64位數字。 在此,您首先用2 64 -1的數字填充數組。 然后,您進行2 64 -2交換。 因此,您大約要進行2 65次運算才能產生10個數字。 在這種極端情況下,問題應該很明顯。 雖然如果每個產生(例如)1000個32位數字不太明顯,但是仍然存在相同的基本問題,只是程度較小。 因此,盡管這是在某些特定情況下做事的有效方法,但其適用性相當狹窄。

生成一個包含27個三位數的數字的數組,其數字小於3。將其隨機播放 根據需要遍歷改組后的數組,值將是唯一的,直到用盡所有值為止。

正如其他人指出的那樣,不要繼續使用隨機數生成器。 而且, rand是一個可怕的生成器,您應該使用C ++標准庫中提供的更好的選擇之一。

您正在有效地生成一個三位數的基數3。 使用您選擇的RNG生成一個以10為底的數字,范圍為0 .. 26,並將其轉換為以3為底。得到000 .. 222。

如果您絕對必須避免重復,那么請按照pjs的建議重新排列數組。 這將導致后面的數字比前面的數字“隨機性較低”,因為它們是從較小的池中提取的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM