Java中的加权随机概率

时间:2017-07-23 13:13:51

标签: java probability

我有一个适应值列表(百分比),按降序排列:

List<Double> fitnesses = new ArrayList<Double>();

我想选择其中一个双打,其极端可能性是第一个,然后降低每个项目的可能性,直到最后一个接近0%的几率作为列表中的最终项目

我如何实现这一目标?

感谢您的任何建议。

3 个答案:

答案 0 :(得分:2)

如果你想选择“这些双打中的一个,极有可能是第一个,那么每个项目的可能性降低,直到最后一个接近0%的几率成为最后一个项目列表“那么你似乎想要一个指数概率函数。 ( p = x 2 )。

但是,只有在编写了解决方案并尝试过后才会知道是否选择了正确的功能,如果它不符合您的需要,那么您需要选择其他概率函数,如正弦曲线(< strong> p = sin(x * PI / 2))或反比( p = 1 / x )。

所以,重要的是编写一个基于概率函数选择项目的算法,这样你就可以尝试任何你喜欢的概率函数。

所以,这是一种方法。

请注意以下事项:

  1. 我使用10播种随机数生成器,以便始终产生相同的结果。移除种子以在每次运行时获得不同的结果。

  2. 我为您的“百分比”使用Integer列表,以避免混淆。一旦了解了工作原理,请随意用Double列表替换。

  3. 我提供了一些样本概率函数。试试看他们会产生什么样的分布。

  4. 玩得开心!

    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 ) );
        }
    }
    

答案 1 :(得分:1)

这是另一种方法,它使您能够近似任意权重分布。

传递给WeightedIndexPicker的数组表示应分配给每个索引的“桶”(&gt; 0)的数量。在你的情况下,这些将是下降,但它们不一定是。如果需要索引,请选择介于0和桶总数之间的随机数,并返回与该存储桶关联的索引。

我使用了一个int权重数组,因为它更容易可视化,并且它避免了与浮点相关的舍入错误。

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);
    }
}

答案 2 :(得分:0)

我认为您不需要所有这些代码来回答您的问题,因为您的问题似乎更多地是关于数学而不是代码。例如,使用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));

但是,正如您所说,数学库中有这么多可能的分布。如果您是为特定目的编写代码,那么最终用户可能希望您解释选择特定分发的原因以及为何按照您的方式设置该分发的参数。这是一个数学问题,应该在数学论坛上提出。