[英]Weighted-random probability in Java
我有一個適用性值(百分比)的列表,這些值以降序排列:
List<Double> fitnesses = new ArrayList<Double>();
我想從這些Doubles中選擇一個,以極高的可能性將其作為第一個,然后降低每個項目的可能性,直到最終的可能性接近0%的可能性成為列表中的最后一個項目。
我該如何實現這一目標?
感謝您的任何建議。
如果要選擇“這些雙打中的一個,則極有可能是第一個,然后降低每個項目的可能性,直到最后一個接近列表中最后一個項目的可能性接近0%”,然后似乎您想要一個指數概率函數。 ( p = x 2 )。
但是,只有在對解決方案進行編碼並進行了嘗試之后,您才知道是否選擇了正確的函數;如果該解決方案不符合您的需求,那么您將需要選擇其他一些概率函數,例如正弦( p = sin( x * PI / 2) )或反比( p = 1 / x )。
因此,重要的是編寫一種基於概率函數來選擇項目的算法,以便您可以嘗試任何喜歡的概率函數。
因此,這是一種方法。
請注意以下幾點:
我將10隨機數生成器植入種子,以便始終產生相同的結果。 刪除種子,以在每次運行時獲得不同的結果。
我正在使用Integer
列表作為您的“百分比”,以免造成混淆。 了解事物的工作原理后,請隨時用Double
列表替換。
我提供了一些樣本概率函數。 嘗試他們看看他們產生什么分布。
玩得開心!
import java.util.*;
public final class Scratch3
{
private Scratch3()
{
}
interface ProbabilityFunction<T>
{
double getProbability( double x );
}
private static double exponential2( double x )
{
assert x >= 0.0 && x <= 1.0;
return StrictMath.pow( x, 2 );
}
private static double exponential3( double x )
{
assert x >= 0.0 && x <= 1.0;
return StrictMath.pow( x, 3 );
}
private static double inverse( double x )
{
assert x >= 0.0 && x <= 1.0;
return 1/x;
}
private static double identity( double x )
{
assert x >= 0.0 && x <= 1.0;
return x;
}
@SuppressWarnings( { "UnsecureRandomNumberGeneration", "ConstantNamingConvention" } )
private static final Random randomNumberGenerator = new Random( 10 );
private static <T> T select( List<T> values, ProbabilityFunction<T> probabilityFunction )
{
double x = randomNumberGenerator.nextDouble();
double p = probabilityFunction.getProbability( x );
int i = (int)( p * values.size() );
return values.get( i );
}
public static void main( String[] args )
{
List<Integer> values = Arrays.asList( 10, 11, 12, 13, 14, 15 );
Map<Integer,Integer> counts = new HashMap<>();
for( int i = 0; i < 10000; i++ )
{
int value = select( values, Scratch3::exponential3 );
counts.merge( value, 1, ( a, b ) -> a + b );
}
for( int value : values )
System.out.println( value + ": " + counts.get( value ) );
}
}
這是另一種實現方法,使您能夠近似任意權重分布。
傳遞給WeightedIndexPicker的數組指示應分配給每個索引的“存儲桶”(> 0)的數量。 在您的情況下,這些值將下降,但不一定如此。 當您需要索引時,請選擇一個介於0和存儲桶總數之間的隨機數,然后返回與該存儲桶關聯的索引。
我使用了一個整數權重數組,因為它更易於可視化,並且避免了與浮點數相關的舍入誤差。
import java.util.Random;
public class WeightedIndexPicker
{
private int total;
private int[] counts;
private Random rand;
public WeightedIndexPicker(int[] weights)
{
rand = new Random();
counts = weights.clone();
for(int i=1; i<counts.length; i++)
{
counts[i] += counts[i-1];
}
total = counts[counts.length-1];
}
public int nextIndex()
{
int idx = 0;
int pick = rand.nextInt(total);
while(pick >= counts[idx]) idx++;
return idx;
}
public static void main(String[] args)
{
int[] dist = {1000, 100, 10, 1};
WeightedIndexPicker wip = new WeightedIndexPicker(dist);
int idx = wip.nextIndex();
System.out.println(idx);
}
}
我認為您不需要所有這些代碼來回答您的問題,因為您的問題似乎更多地是關於數學而不是代碼。 例如,使用apache commons數學庫獲取分布很容易:
ExponentialDistribution dist = new ExponentialDistribution(1);
// getting a sample (aka index into the list) is easy
dist.sample();
// lot's of extra code to display the distribution.
int NUM_BUCKETS = 100;
int NUM_SAMPLES = 1000000;
DoubleStream.of(dist.sample(NUM_SAMPLES))
.map(s->((long)s*NUM_BUCKETS)/NUM_BUCKETS)
.boxed()
.collect(groupingBy(identity(), TreeMap::new, counting()))
.forEach((k,v)->System.out.println(k.longValue() + " -> " + v));
但是,正如您所說,數學庫中有很多可能的分布。 如果您是出於特定目的編寫代碼,則最終用戶可能會希望您解釋為什么選擇特定的發行版以及為什么要以這種方式設置該發行版的參數。 這是一個數學問題,應該在數學論壇中提出。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.