2016-09-28 64 views
-3

例如,如果我想生成0之间的随机浮 - 100,但不包括值1.097 - 3 346,7.0001 - 8.9996,14.5 - 38.6 50 - 50.389,75.648 - 88.8975等?我认为这将是一个简单的问题,但似乎在c#中似乎没有Range对象,并且没有RandWithExclusion()方法。如何在两个值之间随机生成一个浮点数并排除可变数量的范围?

我已经看到了所有这些问题,
How can I generate a random number within a range but exclude some?
https://gamedev.stackexchange.com/questions/124059/how-can-i-exclude-a-range-of-values-when-generating-random-numbers
How to get a random number from a range, excluding some values
他们都不是,甚至远程有用。我在做什么真的是这样一个罕见的问题?

如何将我甚至去这样做? (请不要蛮力。)

+2

如何是那些问题没有用?第一个似乎令人难以置信的转移。 – Shadetheartist

+1

可能的重复[如何生成一个范围内的随机数,但排除一些?](http://stackoverflow.com/questions/6443176/how-can-i-generate-a-random-number-within-a -range-but-exclude-some) – Shadetheartist

+0

我会采取第一种解决方案,将其转换为C#并将其扩展为支持_multiple_排除范围。 –

回答

0

绘制的数字,获取可能的名单之前,我会做一些预处理范围。因此,让我们假设我们有一个Range结构,如下所示:

/// <summary> A possible range of values. </summary> 
public struct Range 
{ 
    /// <summary> Min value, inclusive. </summary> 
    public readonly double Min; 
    /// <summary> Max value, inclusive. </summary> 
    public readonly double Max; 
    public Range(double min, double max) { Min = min; Max = max; } 
    /// <summary> Range length, distance between Min and Max. </summary> 
    public double Length { get { return Max - Min; } } 
} 

另一个结构RangeList将多个范围保存在一起。范围列表还包含你的范围的连续长度总和的累计长度的数组,像这样:

/// <summary> All possible ranges grouped together. </summary> 
public struct RangeList 
{ 
    /// <summary> Possible range. </summary> 
    public readonly Range[] Ranges; 
    /// <summary> Sum of each range length. </summary> 
    public readonly double Length; 
    /// <summary> Cumulative lengths values of each ranges. </summary> 
    public readonly double[] CumulLengths; 
    public RangeList(Range[] ranges) 
    { 
     Ranges = ranges; 
     Length = 0; 
     CumulLengths = new double[ranges.Length]; 
     for (var i = 0; i < ranges.Length; ++i) 
     { 
      Length += ranges[i].Length; 
      CumulLengths[i] = Length; 
     } 
    } 
} 

然后,我们可以写很容易地从排除范围的给定列表中创建RangeList功能:

/// <summary> Get possible ranges to draw from, considering exclusions. </summary> 
    public static RangeList GetRangeList(Range range, params Range[] exclusions) 
    { 
     var ranges = new List<Range>(); 
     ranges.Add(range); 
     if (exclusions != null) 
     { 
      foreach (var exclusion in exclusions) 
      { // progressively eat latest range added to the list, cutting exclusions. 
       var lastRange = ranges[ranges.Count - 1]; 
       if (exclusion.Min < lastRange.Max) 
       { 
        ranges[ranges.Count - 1] = new Range(lastRange.Min, exclusion.Min); 
        if (exclusion.Max < lastRange.Max) 
        { 
         ranges.Add(new Range(exclusion.Max, lastRange.Max)); 
        } 
       } 
      } 
     } 
     return new RangeList(ranges.ToArray()); 
    } 

此方法依赖于几个假设,包括并非排除所有空间,排除不重叠,排除按升序排列。 它是那么直着借鉴的可能范围内的号码:

/// <summary> Assume exclusions are also given in ranges. </summary> 
    public static double RangeWithExclusions(this Random random, Range range, params Range[] exclusions) 
    { 
     var rangeList = GetRangeList(range, exclusions); 
     var rnd = random.NextDouble() * rangeList.Length; 
     var rangeIndex = Array.BinarySearch(rangeList.CumulLengths, rnd); 
     if (rangeIndex < 0) 
     { // 'unlucky', we didn't hit a length exactly 
      rangeIndex = ~rangeIndex; 
     } 
     var previousLength = rangeIndex > 0 ? rangeList.CumulLengths[rangeIndex - 1] : 0; 
     var rndRange = rangeList.Ranges[rangeIndex]; // result range of our random draw 
     return rndRange.Min + (rnd - previousLength); // scale rnd back into range space 
    } 

以下NUnit测试演示如何使用该解决方案:

[TestFixture] 
public class TestRandom 
{ 
    [Test] 
    public void Tests() 
    { 
     var random = new Random(); 
     double rnd; 
     rnd = random.RangeWithExclusions(new Range(0, 1)); 
     Assert.IsTrue(rnd >= 0 && rnd <= 1); 
     rnd = random.RangeWithExclusions(new Range(-100, 1)); 
     Assert.IsTrue(rnd >= -100 && rnd <= 1); 
     rnd = random.RangeWithExclusions(new Range(0, 1), new Range(0.1, 0.9)); 
     Assert.IsTrue(rnd >= 0 && rnd <= 1 && (rnd <= 0.1 || rnd >= 0.9)); 
     rnd = random.RangeWithExclusions(new Range(0, 1), new Range(0, 0.9)); 
     Assert.IsTrue(rnd >= 0 && rnd <= 1 && (rnd >= 0.9)); 
     rnd = random.RangeWithExclusions(new Range(0, 1), new Range(0.2, 0.4), new Range(0.6, 0.8)); 
     Assert.IsTrue(rnd >= 0 && rnd <= 1 && (rnd <= 0.2 || rnd >= 0.4) && (rnd <= 0.6 || rnd >= 0.8)); 
    } 
} 

希望这有助于

-2

这很简单...你可能应该坐下来自己想出来学习,特别是因为你自己说这是一个简单的问题) 。

这是不是最好的方式,也不是最有效的方式通过任何方式做到这一点,但它是一个天真的做法,与要求的小算盘的作品。缺点是,如果你多次运行(例如一百万次),你会产生相当多的重复。

public class Range 
{ 
    public float MinValue { get; set; } 
    public float MaxValue { get; set; } 
} 

public static class FloatGenerator 
{ 
    public static float GenerateFloatWithExclusionsInANaiveWay(int minValue, int maxValue, params Range[] rangeExclusions) 
    { 
     // We don't care about ranges outside of the min and max values allowed 
     var allowedRanges = rangeExclusions.Where(r => r.MinValue >= minValue && r.MaxValue <= maxValue); 

     // We use a guid to generate a random seed that random will use (reduces chance of duplicates) 
     var random = new Random(Guid.NewGuid().GetHashCode()); 

     // We will use this to keep a pool of random values that fit within our expected ranges 
     var randomPool = new List<float>(); 

     // Loop through each of the ranges and get a value that fits the range 
     foreach (var range in allowedRanges) 
     { 
      var randomValue = float.MaxValue; 
      while (randomValue < range.MinValue || randomValue > range.MaxValue) 
      { 
       randomValue = (random.Next((int)range.MinValue, (int)range.MaxValue) + (float)random.NextDouble()); 
      } 

      randomPool.Add(randomValue); 
     } 

     // Return one of the acceptable random numbers randomly 
     return randomPool.ElementAt(random.Next(0, randomPool.Count - 1)); 
    } 
} 

如果你想更大胆的尝试,你可以看看这把更多的心思到它可能比你想要的答案。

+2

“你应该坐下来自己学习的好东西”,这意味着你不应该这样做hw对于他 – Steve

+3

这实际上并不排除范围,它仅排除特定值。该代码也很有可能导致堆栈溢出异常。 – Servy

+0

@Steve Yea,你是对的,但OP显然花费了更多的时间搜索答案,并写了一篇文章而不是去思考它。我倾向于发现这样做的人会花费十倍于每个编码论坛的时间,并一遍又一遍地发布相同的问题,直到有人为他们回答。不妨在这里停止论坛垃圾邮件。 –

4
  1. 考虑你想要包括R1,R2,......的范围。假设它们是非重叠的并按顺序排列。

  2. 加入了他们的总跨度(最终开始)。你现在有一个连续的范围为你的随机数(零到总和(跨度))。

  3. 产生该范围内的号码。

  4. 现在其映射回数到非连续范围:

    1. 如果是小于第一范围的跨度,将其添加到启动的第一范围和返回。
    2. 否则,减去第一个范围的跨度从它,比较第二跨度等

enter image description here

+0

请注意,对于检查二进制搜索的范围有点大,将会更好的选项 - O(log n_ranges))与建议的线性搜索O(n_ranges)。 –

+0

非常有用,谢谢,只是一件事:“将它添加到第一个范围的**开始**并返回它” - 你是否意味着**结束?**我问,因为我测试了两个,并添加了开始第一个范围导致显着更多的被排除的数字被输出。 (尽管我应该提到这两种方式都会导致被排除的数字被输出)。 – user982566171

+0

不,将随机偏移量添加到范围的开始位置,请参阅我添加的图表。 –

相关问题