簡體   English   中英

如何不斷調整隨機數生成器的權重,使結果分布更均勻?

[英]How to constantly adjust the weight for random number generator so the results can be distributed more evenly?

我正在嘗試提出一個解決方案,該解決方案可以生成具有更均勻分布結果的隨機整數。

基本思想是這樣的:

例如,我想生成一個 0 - 9 之間的隨機整數。在生成每個新的隨機數時,我還保留了一個列表,用於計算每個數字生成的次數。 例如,如果第一個結果是 5,則 5 的計數為 1,這將使下一次生成數字 5 的可能性較小,因為其他數字的計數僅為 0。

一開始,我正在考慮這樣做,就像一個常規的加權隨機數生成器一樣,它將所有權重相加並在總和范圍內創建一個隨機數,並查看隨機數落入哪個結果。

但是,權重列表仍然與選擇具有相同的順序。 例如,如果我們有 4 個選擇:0、1、2、3,它們的權重分別為 1、2、3、4。每個選擇的歸一化非加權概率為 25%,歸一化加權概率為分別為 10%、20%、30% 和 40%。 這意味着歸一化的隨機數必須是 0 - 0.1 才能滾動 0,0.1 - 0.3 才能滾動 1,依此類推。

這樣做的問題是,因為從長遠來看,隨機數生成器應該或多或少地具有均勻分布的結果,如果權重仍然以可用結果的相同順序對齊,它可能會產生不太理想的結果。 使用上面的例子。 假設隨機數生成器擲出 0.59,這將導致數字 2 被選中。 現在既然選擇了數字 2,其他選擇的權重應該會增加(所以下次選擇 2 的可能性較小)。 這意味着現在選擇3號的權重范圍將從0.6增加到1.0,並且可能會覆蓋0.59,這在上次操作中2號的范圍內。 根據設計,較大的權重范圍應該會導致選擇表示的結果的機會更大。 但是,由於上次滾動了 0.59,由於可靠的隨機數生成器的性質,這次獲得 0.59 的機會應該小於獲得另一個介於 0 - 1 之間的浮點數,這反過來又會降低數字 3(如果滾動 0.59,現在將被選中)被選中。

我想我目前有一個解決方案,將涉及創建一個非常大的列表,感覺非常不雅,並且可能很容易達到列表的大小限制。 所以我想知道這個問題的更優雅的解決方案。

順便說一下,這里(在 Unity C# 中)是我目前計算權重的方式(這可能並不重要):

// Get the largest weight number
int largestWeight = Mathf.Max(weightList.ToArray()); 

// Get list for weights adjusted based on the weight factor
List<int> adjustedWeights = new List<int>(weightList);
adjustedWeights.ForEach(w => w = Mathf.RoundToInt(Mathf.Pow((largestWeight - w + 1), weightFactor)));

這個 weightList 是每個結果被選擇多少次的計數列表,並被維護,以便如果每個計數大於 0,所有計數將為 - 1,這使最低計數始終為 0。

看來您想模擬我在“ 無替換的加權選擇(多個副本) ”部分中描述的加權選擇 在您的情況下,它可以按如下方式工作:

  1. 為每個項目賦予相同的權重,指定為正整數。 例如,為每個項目賦予 20 的權重。

  2. 使用加權選擇替換算法。 也許最簡單的是拒絕采樣,如下所述。 假設最高權重是max 要使用拒絕采樣在 [1, weights.Count ] 中選擇一個整數:

    1. 在 [1, weights.Count ] 中選擇一個統一的隨機整數i
    2. 使用概率weights[i]/max ,返回i 否則,轉到步驟 1。

    除了拒絕抽樣之外,還有許多其他方法可以進行加權選擇; 請參閱我關於加權選擇算法的說明

  3. 選擇每個項目時,將其權重減 1,以使其不太可能被選中。

  4. 如果所有權重均為 0,則為每個項目分配在步驟 1 中選擇的相同權重(在本例中為 20)。

既然您提到了 Unity,那么您的目標可能是制作一款控制出現哪些隨機數的游戲,讓隨機結果對玩家來說顯得“更公平”。 但是,您還應該考慮做出獨立的統一隨機選擇是否更好,特別是如果您關心玩家是否可以通過預測隨機結果獲得不公平的優勢。

替代 Peter O 想法使用正增長權重,從計數 1 開始並為每個樣本增加它,但使用計數的 INVERSE 作為權重。 因此,具有更多計數的值將不太可能被選擇,類似於反向加權

一些代碼(未經測試!基於Math.Net 數字

import MathNet.Numerics.Distributions;

static void Main() {

    const int N = 4;

    var counts  = new int [N] {1, 1, 1, 1};
    var weights = new double [N] {1.0, 1.0, 1.0, 1.0};

    while (true) {
        int v = Categorical.Sample(weights[k]); // sample one value in [0...N)
        // update counts and weights
        counts[v] += 1;
        weights[v] = 1.0/(double)counts[v];
        
        // use v here for something
        ...
    }
}

您可以發明一種更通用的加權,要記住的一件重要事情是計數歸分母。

Fe 可能效果更好

weights[v] = 1.0/(1.0 + .5*(double)counts[v]);

或者那個

var squared => (x) => x*x;

weights[v] = 1.0/(7.0 + .25*squared((double)counts[v]));

或者那個

weights[v] = 1.0/(3.0 + Math.Sqrt((double)counts[v]));

一旦它是權重公式分母中計數的單調增長函數,它就會以所需的方式改變概率

暫無
暫無

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

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