【问题标题】:Random number with Probabilities in C#C#中带有概率的随机数
【发布时间】:2020-07-13 07:49:00
【问题描述】:

我已将this Java program 转换为 C# 程序。

using System;
using System.Collections.Generic;

namespace RandomNumberWith_Distribution__Test
{
    public class DistributedRandomNumberGenerator
    {

        private Dictionary<Int32, Double> distribution;
        private double distSum;

        public DistributedRandomNumberGenerator()
        {
            distribution = new Dictionary<Int32, Double>();
        }

        public void addNumber(int val, double dist)
        {
            distribution.Add(val, dist);// are these two
            distSum += dist;            // lines correctly translated?
        }

        public int getDistributedRandomNumber()
        {
            double rand = new Random().NextDouble();//generate a double random number
            double ratio = 1.0f / distSum;//why is ratio needed?
            double tempDist = 0;

            foreach (Int32 i in distribution.Keys)
            {
                tempDist += distribution[i];

                if (rand / ratio <= tempDist)//what does "rand/ratio" signify? What does this comparison achieve?
                {
                    return i;
                }
            }
            return 0;
        }
    }

    public class MainClass
    {
        public static void Main(String[] args)
        {
            DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
            drng.addNumber(1, 0.2d);
            drng.addNumber(2, 0.3d);
            drng.addNumber(3, 0.5d);

            //================= 
            // start simulation
            int testCount = 1000000;
            Dictionary<Int32, Double> test = new Dictionary<Int32, Double>();

            for (int i = 0; i < testCount; i++)
            {
                int random = drng.getDistributedRandomNumber(); 

                if (test.ContainsKey(random)) 
                {
                    double prob = test[random];   // are these
                    prob = prob + 1.0 / testCount;// three lines
                    test[random] = prob;          // correctly translated?
                }
                else
                {
                    test.Add(random, 1.0 / testCount);// is this line correctly translated?
                }
            }

            foreach (var item in test.Keys)
            {
                Console.WriteLine($"{item}, {test[item]}");
            }

            Console.ReadLine();
        }
    }
}

我有几个问题:

  1. 您能否检查注释标记的行是否正确或需要解释?
  2. 为什么getDistributedRandomNumber() 在进行进一步计算之前不检查1 分布的总和?

【问题讨论】:

    标签: c# random probability-distribution


    【解决方案1】:

    方法

    public void addNumber(int val, double dist)
    

    翻译不正确,因为您缺少以下几行:

    if (this.distribution.get(value) != null) {
        distSum -= this.distribution.get(value);
    }
    

    当您调用以下代码时,这些行应该涵盖这种情况(基于您的示例代码):

    DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
    drng.addNumber(1, 0.2d);
    drng.addNumber(1, 0.5d);
    

    因此,使用相同的第一个参数调用方法addNumber 两次,缺少的代码部分会查看第一个参数是否已存在于字典中,如果存在,它将从字典中删除“旧”值以插入新的价值。

    你的方法应该是这样的:

    public void addNumber(int val, double dist)
    {
        if (distribution.TryGetValue(val, out var oldDist)) //get the old "dist" value, based on the "val"
        {
            distribution.Remove(val); //remove the old entry
            distSum -= oldDist; //substract "distSum" with the old "dist" value
        }
    
        distribution.Add(val, dist); //add the "val" with the current "dist" value to the dictionary
        distSum += dist; //add the current "dist" value to "distSum"
    }
    

    现在开始你的第二种方法

    public int getDistributedRandomNumber()
    

    不要在每次调用此方法时都调用初始化Random 的新实例,而应该只初始化一次,所以换行

    double rand = new Random().NextDouble();
    

    到这里

    double rand = _random.NextDouble();
    

    并像这样在类声明内的方法之外初始化字段_random

    public class DistributedRandomNumberGenerator
    {
        private Dictionary<Int32, Double> distribution;
        private double distSum;
        private Random _random = new Random();        
    
    
        ... rest of your code
    }
    

    如果在循环中调用,这将防止new Random().NextDouble() 一遍又一遍地产生相同的数字。 您可以在此处阅读有关此问题的信息:Random number generator only generating one random number

    正如我的旁注,c# 中的字段以前缀下划线命名。您应该考虑将distribution 重命名为_distribution,同样适用于distSum


    下一步:

    double ratio = 1.0f / distSum;//why is ratio needed?
    

    需要比率是因为该方法会尽力使用您提供的信息来完成其工作,假设您只调用它:

    DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
    drng.addNumber(1, 0.2d);
    int random = drng.getDistributedRandomNumber(); 
    

    通过这些行,您告诉全班您希望在20% 的案例中拥有1 的数量,但是其他80% 呢?

    这就是比率变量出现的地方,它根据您给出的概率总和计算一个可比较的值。 例如。

    double ratio = 1.0f / distSum;
    

    与最新示例一样,drng.addNumber(1, 0.2d);distSum 将是0.2,这转换为20% 的概率。

    double ratio = 1.0f / 0.2;
    

    比率为5.020% 的概率为 5,因为100% / 5 = 20%

    现在让我们看看当比率为5时代码是如何反应的

    double tempDist = 0;
    foreach (Int32 i in distribution.Keys)
    {
        tempDist += distribution[i];
    
        if (rand / ratio <= tempDist)
        {
            return i;
        }
    }
    

    rand 在任何给定时间都将是一个大于或等于 0.0 且小于 1.0 的值。这就是 NextDouble 的工作原理,因此我们假设以下 0.254557522132321rand

    现在让我们一步一步来看看会发生什么

    double tempDist = 0; //initialize with 0 
    foreach (Int32 i in distribution.Keys) //step through the added probabilities
    {
        tempDist += distribution[i]; //get the probabilities and add it to a temporary probability sum
    
        //as a reminder
        //rand = 0.254557522132321
        //ratio = 5
        //rand / ratio = 0,0509115044264642
        //tempDist = 0,2
        // if will result in true
        if (rand / ratio <= tempDist)
        {
            return i;
        }
    }
    

    如果我们不应用比率,if 将是错误的,但这是错误的,因为我们的字典中只有一个值,所以无论rand 值是什么,if 语句都应该返回是的,这就是rand / ratio 的本质。

    根据我们添加的概率总和来“修复”随机生成的数字。 rand / ratio 只有在您没有提供完美总结为1 = 100% 的概率时才有用。

    例如。如果你的例子是这样的

    DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
    drng.addNumber(1, 0.2d);
    drng.addNumber(2, 0.3d);
    drng.addNumber(3, 0.5d);
    

    您可以看到提供的概率等于1 => 0.2 + 0.3 + 0.5,在本例中为行

    if (rand / ratio <= tempDist)
    

    看起来像这样

    if (rand / 1 <= tempDist)
    

    除以 1 永远不会改变值和 rand / 1 = rand,所以这个设计的唯一用例是你没有提供完美的 100% 概率的情况,可能或多或少。

    作为旁注,我建议将您的代码更改为此

    //call the dictionary distributions (notice the plural)
    //dont use .Keys
    //var distribution will be a KeyValuePair
    foreach (var distribution in distributions)
    {
        //access the .Value member of the KeyValuePair
        tempDist += distribution.Value;
    
        if (rand / ratio <= tempDist)
        {
            return i;
        }
    }
    

    您的测试例程似乎翻译正确。

    【讨论】:

      猜你喜欢
      • 2013-12-18
      • 1970-01-01
      • 2017-05-16
      • 2011-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-26
      • 1970-01-01
      相关资源
      最近更新 更多