我有一个Range类,我希望能够从中减去一组范围。
我可以做一件事。
E.g。
Range range1 = new Range(0,100);
Range range2 = new Range(40,60);
List<Range> differences = range1.Difference(range2);
differences[0]; // 0 to 40
differences[1]; // 60 to 100
我遇到的问题是找到范围和一组范围之间的差异:
List<Range> rangeSet = new List<Range>();
rangeSet.Add(new Range(10, 30);
rangeSet.Add(new Range(25, 70);
rangeSet.Add(new Range(90, 120);
List<Range> results = range1.Difference(rangeSet);
结果应该是:
results[0]; // 0 to 10
results[1]; // 70 to 90
算法应该从range和rangeSet [0]之间的差异得到结果,然后使用该结果作为下一次迭代等的输入,最后返回作为结果的范围集。
我猜这需要一个递归方法,但我不能理解它????
'澄清'。问题比我给出的范围示例更复杂。这是我试图通过的测试。
[Test]
public void BandCanReturnDifferenceWithASetOfOtherBands()
{
var bandA = Band.Create<ChargeBand>("Band A");
bandA.AddMonth(EMonth.January);
bandA.AddMonth(EMonth.February);
bandA.AddDayOfWeek(DayOfWeek.Monday);
bandA.AddDayOfWeek(DayOfWeek.Tuesday);
bandA.AddTimeRange(5, 0, 11, 0);
var bandA2 = Band.Create<ChargeBand>("Band A2");
bandA2.AddMonth(EMonth.January);
bandA2.AddMonth(EMonth.December);
bandA2.AddDayOfWeek(DayOfWeek.Wednesday);
bandA2.AddDayOfWeek(DayOfWeek.Friday);
bandA2.AddTimeRange(1, 0, 10, 0);
bandA2.AddTimeRange(12, 0, 24, 0);
IList<IBand> bands = new List<IBand>();
bands.Add(bandA);
bands.Add(bandA2);
var bandB = Band.CreateAllTimesBand<ChargeBand>("Band B");
// this should result in
var bandR1 = Band.Create<ChargeBand>("R1");
var bandR2 = Band.Create<ChargeBand>("R2");
var bandR3 = Band.Create<ChargeBand>("R3");
bandR1.AddMonth(EMonth.January);
bandR1.AddMonth(EMonth.February);
bandR1.AddDayOfWeek(DayOfWeek.Monday);
bandR1.AddDayOfWeek(DayOfWeek.Tuesday);
bandR1.AddTimeRange(0, 0, 5, 0);
bandR1.AddTimeRange(11, 0, 24, 0);
bandR2.AddMonth(EMonth.January);
bandR2.AddMonth(EMonth.December);
bandR2.AddDayOfWeek(DayOfWeek.Wednesday);
bandR2.AddDayOfWeek(DayOfWeek.Thursday);
bandR2.AddTimeRange(0, 0, 1, 0);
bandR2.AddTimeRange(10, 0, 12, 0);
bandR3.AddMonth(EMonth.March);
bandR3.AddMonth(EMonth.April);
bandR3.AddMonth(EMonth.May);
bandR3.AddMonth(EMonth.June);
bandR3.AddMonth(EMonth.July);
bandR3.AddMonth(EMonth.August);
bandR3.AddMonth(EMonth.September);
bandR3.AddMonth(EMonth.October);
bandR3.AddMonth(EMonth.November);
bandR3.SetAllDays();
bandR3.AddTimeRange(0, 0, 24, 0);
// J,F,M,A,M,J,J,A,S,O,N,D - M,T,W,T,F,S,S - (00:00,24:00)
// J,F - M,T - (05:00,11:00)
// J, D - W F - (01:00,10:00),(12:00,24:00)
IList<IBand> expectedResults = new List<IBand>();
expectedResults.Add(bandR1); // J,F - M,T - (00:00,05:00),(11:00,24:00)
expectedResults.Add(bandR2); // J,D - W,F - (00:00,01:00),(10:00,12:00)
expectedResults.Add(bandR3); // M,A,M,J,J,A,S,O,N - (00:00,24:00)
var actualResults = bandB.Difference(bands);
Assert.AreEqual(expectedResults.Count, actualResults.Count);
foreach (var result in actualResults)
{
Assert.IsTrue(expectedResults.Contains(result));
}
}
很抱歉,如果我没有意义,但我很难解释。
答案 0 :(得分:0)
同意递归算法是不必要的。您只需要一些原始的集合操作即可,这可以很容易地完成。其中最难的是减法操作,从单个范围中移除一个或多个范围。一旦你有了这个,你需要一个可以再次规范化范围的联盟。
这是一个有效的例子:
[System.Diagnostics.DebuggerDisplay("Range({Start} - {End})")]
public class Range : IEnumerable<int>
{
public int Start, End;
public Range(int start, int end)
{
Start = start;
End = end;
}
public IEnumerator<int> GetEnumerator()
{
for (int i = Start; i <= End; i++)
yield return i;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
public IEnumerable<Range> Subtract(IEnumerable<int> removed)
{
IEnumerator<int> add = this.GetEnumerator();
IEnumerator<int> rem = removed.GetEnumerator();
bool hasmore = rem.MoveNext();
while (add.MoveNext())
{
int start = add.Current;
int end = start;
while (hasmore && rem.Current < start)
hasmore = rem.MoveNext();
if(!hasmore)
{
while (add.MoveNext())
end = add.Current;
yield return new Range(start, end);
yield break;
}
if(rem.Current == start)
{
hasmore = rem.MoveNext();
continue;
}
while (add.MoveNext())
{
if (add.Current == rem.Current)
{
hasmore = rem.MoveNext();
break;
}
end = add.Current;
}
if (end >= start)
yield return new Range(start, end);
}
}
}
public static IEnumerable<int> UnionRanges(this IEnumerable<Range> ranges)
{
int pos = int.MinValue;
foreach(Range r in ranges.OrderBy(x => x.Start))
{
pos = Math.Max(pos, r.Start);
for (; pos <= r.End; pos++)
yield return pos;
}
}
public static IEnumerable<Range> CreateRanges(this IEnumerable<int> values)
{
Range r = null;
foreach (int val in values)
{
if (r == null)
r = new Range(val, val);
else if (val == r.End + 1)
r.End++;
else
{
yield return r;
r = new Range(val, val);
}
}
if (r != null)
yield return r;
}
public static void Main()
{
Range range1 = new Range(0, 100);
Range range2 = new Range(40, 60);
IEnumerable<Range> diff1 = range1.Subtract(range2);
Console.WriteLine("Subtract 40-60 from 0-100:");
foreach (Range r in diff1)
Console.WriteLine("{0} ~ {1}", r.Start, r.End);
List<Range> rangeSet = new List<Range>();
rangeSet.Add(new Range(10, 30));
rangeSet.Add(new Range(25, 70));
rangeSet.Add(new Range(90, 120));
Console.WriteLine("Normalized ranges to remove:");
foreach (Range r in rangeSet.UnionRanges().CreateRanges())
Console.WriteLine("{0} ~ {1}", r.Start, r.End);
IEnumerable<Range> diff2 = range1.Subtract(rangeSet.UnionRanges());
Console.WriteLine("Results:");
foreach (Range r in diff2)
Console.WriteLine("{0} ~ {1}", r.Start, r.End);
}
前面的程序产生以下输出:
Subtract 40-60 from 0-100:
0 ~ 39
61 ~ 100
Normalized ranges to remove:
10 ~ 70
90 ~ 120
Results:
0 ~ 9
71 ~ 89
Press any key to continue . . .
剩下的主要问题是示例中的fence post错误。您需要确定范围是否包含结束,然后相应地进行调整。
我会注意到前面的程序并不打算成为“生产”......它只是一个如何解决所涉问题的例子。更好的实现可以联合并减去范围,而无需迭代范围中的项。这个概念仍然是相同的,构建集合操作并从那里开始。
顺便说一下,如果你只有几百个项目,我会使用HashSet或Dictionary来继续生活;)答案 1 :(得分:-1)
你可以试试这个:
List<Range> rangeSet = new List<Range>();
rangeSet.Add(new Range(10, 30));
rangeSet.Add(new Range(25, 70));
rangeSet.Add(new Range(90, 120));
List<Range> results = new List<Range>();
Range currentRange = rangeSet.First();
foreach (var x in rangeSet.Skip(1))
{
results.Add(x.Difference(currentRange));
currentRange = x;
}