2016-09-28 58 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

不,將隨機偏移量添加到範圍的開始位置,請參閱我添加的圖表。 –

相關問題