C#递归问题

时间:2011-07-21 16:36:33

标签: c# recursion

我有一个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));
        }
    }

很抱歉,如果我没有意义,但我很难解释。

2 个答案:

答案 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;
        }