繪製的數字,獲取可能的名單之前,我會做一些預處理範圍。因此,讓我們假設我們有一個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));
}
}
希望這有助於
如何是那些問題沒有用?第一個似乎令人難以置信的轉移。 – Shadetheartist
可能的重複[如何生成一個範圍內的隨機數,但排除一些?](http://stackoverflow.com/questions/6443176/how-can-i-generate-a-random-number-within-a -range-but-exclude-some) – Shadetheartist
我會採取第一種解決方案,將其轉換爲C#並將其擴展爲支持_multiple_排除範圍。 –