【问题标题】:Calculate number of distinct values between two numbers at a given precision以给定精度计算两个数字之间不同值的数量
【发布时间】:2019-11-27 20:34:39
【问题描述】:

上下文:我正在构建一个随机数生成用户界面,用户可以在其中输入以下值:

  • lowerLimit:每个随机生成数的下限
  • upperLimit:每个随机生成数字的上限
  • maxPrecision:每个随机生成数的最大精度
  • 数量:要生成的随机数值的最大数量

问题是:如何确保在给定的下限/上限范围和给定精度下,用户不会请求比可能更大的数量?

例子:

下限:1 上限:1.01 最大精度:3 数量:50

在此精度级别 (3) 上,1 和 1.01 之间有 11 个可能的值:1.000、1.001、1.002、1.003、1.004、1.005、1.006、1.007、1.008、1.009、1.100,但用户要求前 50 名。

在一个只返回符合用户条件的不同值的函数版本中,我使用字典对象来存储已经生成的值,如果该值已经存在,请尝试另一个随机数,直到找到 X 个不同的随机数其中 X 是用户所需数量的值。问题是,如果可能值的数量少于用户输入的数量,我的逻辑允许一个永无止境的循环。

虽然我可能会使用逻辑来检测失控情况,但我认为提前以某种方式计算可能返回值的数量以确保它是可能的会是一种更好的方法。但这种逻辑让我难以理解。 (什么都没试过,因为我想不出怎么做)。

请注意:我确实看到了问题 Generating random, unique values C#,但没有解决我的问题的具体细节,即在给定精度和随后的失控条件下可能值的数量。

private Random RandomSeed = new Random();
public double GetRandomDouble(double lowerBounds, double upperBounds, int maxPrecision)
{
    //Return a randomly-generated double between lowerBounds and upperBounds 
    //with maximum precision of maxPrecision
    double x = (RandomSeed.NextDouble() * ((upperBounds - lowerBounds))) + lowerBounds;
    return Math.Round(x, maxPrecision);
}
public double[] GetRandomDoublesUnique(double lowerBounds, double upperBounds, int maxPrecision, int quantity)
{
    //This method returns an array of doubles containing randomly-generated numbers
    //between user-entered lowerBounds and upperBounds with a maximum precision of
    //maxPrecision.  The array size is capped at user-entered quantity.

    //Create Dictionary to store number values already generated so we can ensure
    //we don't have duplicates
    Dictionary<double, int> myDoubles = new Dictionary<double, int>();
    double[] returnValues = new double[quantity];
    double nextValue;
    for (int i = 0; i < quantity; i++)
    {
        nextValue = GetRandomDouble(lowerBounds, upperBounds, maxPrecision);
        if (!myDoubles.ContainsKey(nextValue))
        {
            myDoubles.Add(nextValue, i);
            returnValues[i] = nextValue;
        }
        else
        {
            i -= 1;
        }
    }
    return returnValues;
}

【问题讨论】:

  • 任意数量的随机数在任意范围内和任意精度都是可能的,因为根据定义,随机数是允许重复出现的。如果您说您永远不想重复出现相同的数字,那么您正在寻找shuffle,而不是随机数。

标签: c# random distinct precision


【解决方案1】:

可以通过从最后减去第一个的“位置”来计算项目数(下面的伪代码,使用Math.Pow计算10^x):

(int)(last * 10 ^ precision) - (int)(first * 10 ^ precision)

这可能需要根据您是否需要边界以及您是否将decimal(精确)或float/double 作为输入进行调整 - 可能需要添加一些 +/-1 和 Math.Round in 以获得所有预期值的预期结果。

在你得到项目数量后,基本上有两种情况

  • 想要得到的结果有很多选择(即 1 到 100,取 5 个随机数) - 使用必须过滤掉重复项的代码。
  • 那里的选择数接近或少于所需的结果数(即 1 到 10,返回 11 个随机数) - 预先生成所有值的列表并随机播放。

实验“显着更多”和“接近”之间的界限 - 我会使用 25% 作为界限(即 1 到 100,取 76 - 使用改组)以避免接近尾声的过度退休(这正是原因基本方法的缓慢/无限重试)。

shuffle 的正确实现在 Randomize a List<T> 中(查看类似的帖子,如 Generating random, unique values C# 以获得更多讨论)。

【讨论】:

  • 谢谢——很高兴知道随机播放。这在将来会有用。顺便说一句:看来 ^ 符号不适用于“power”。不过,Math.Pow 成功了。
  • @AndrewBanjo1968 抱歉,我原本不想让示例看起来像代码...因为确实^ 不是力量(也是常识)...
【解决方案2】:

最简单的方法可能是通过将值乘以 10 ^ 精度然后减去来将值转换为整数

int lowerInt = (int)(lower * (decimal)Math.Pow(10, precision));
int higherInt = (int)(higher * (decimal)Math.Pow(10, precision));
int possibleValues = higherInt - lowerInt + 1

我觉得这会违背您项目的目的,即要求用户提前知道有多少可能的值,因为这似乎就是他们首先使用此功能的目的。我假设该要求只是为了缓解您遇到的技术问题。您现在可以将循环更改为此

for (int i = 0; i < possibleValues; i++)

【讨论】:

    【解决方案3】:

    这是根据 Josh Williard 的回答起作用的。

    public double[] GetRandomDoublesUnique(double lowerBounds, double upperBounds, int maxPrecision, int quantity)
        {
            if (lowerBounds >= upperBounds)
            {
                throw new Exception("Error in GetRandomDoublesUnique is: LowerBounds is greater than UpperBounds!");
            }
            //These next few lines are for the purpose of determining the maximum possible number of return values
            //possibleValues is populated to prevent a runaway condition that could occurs if the 
            //max possible values--at the given precision level--is less than the user-selected quantity.
            //i.e. if user selects 1 to 1.01, precision of 3, and quantity of 50, there would be a problem
            // if we didn't limit loop to the 11 possible values at precision of 3:  
            //1.000, 1.001, 1.002, 1.003, 1.004, 1.005, 1.006, 1.007, 1.008, 1.009, 1.010
    
            int lowerInt = (int)(lowerBounds * (double)Math.Pow(10, maxPrecision));
            int higherInt = (int)(upperBounds * (double)Math.Pow(10, maxPrecision));
            int possibleValues = higherInt - lowerInt + 1;
    
            //Create Dictionary to store number values already generated so we can ensure
            //we don't have duplicates
            Dictionary<double, int> myDoubles = new Dictionary<double, int>();
            double[] returnValues = new double[(quantity>possibleValues?possibleValues:quantity)];
            double NextValue;
            //Iterate through and generate values--limiting to both the user-selected quantity and # of possible values
            for (int i = 0; (i < quantity)&&(i<possibleValues); i++)
            {
                NextValue = GetRandomDouble(lowerBounds, upperBounds, maxPrecision);
                if (!myDoubles.ContainsKey(NextValue))
                {
                    myDoubles.Add(NextValue, i);
                    returnValues[i] = NextValue;
                }
                else
                {
                    i -= 1;
                }
            }
            return returnValues;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      • 2020-07-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多