找出具有可选不等式的一组范围中的间隙

时间:2014-04-14 02:32:15

标签: c#

在SO上有很多类似的问题,但似乎都没有回答我的问题。

我有一个类似的课程:

public partial class PercentileWeight
{
    public virtual Guid VotingWeightId { get; set; }

    public virtual decimal LowerBoundPercentageRanking { get; set; }

    public virtual bool LowerBoundInclusive { get; set; }

    public virtual decimal UpperBoundPercentageRanking { get; set; }

    public virtual bool UpperBoundInclusive { get; set; }

    public virtual decimal PercentageWeight { get; set; }
}

......这里的概念是,如果数据源在某个百分位数内排名,那么他们的数据值可能在消耗该数据的决策树中或多或少地计数。例如,如果数据源排在前10%,我可能希望将数据源的值权重加倍。这种PercentileWeight的对象看起来像这样:

var pw = new PercentileWeight
    {
        UpperBoundPercentageRanking = 100M,
        UpperBoundInclusive = true,
        LowerBoundPercentageRanking = 90M,
        LowerBoundInclusive = false,
        PercentageWeight = 200M
    };

请注意UpperBoundInclusiveLowerBoundInclusive值。在此模型中,正好90%的排名符合条件,但正好100%的值符合条件。还有一些逻辑可以确保没有任何范围重叠。

我想要做的是以编程方式识别这些对象集合中的“间隙”,因此我可以向用户显示“未覆盖的范围”,以便为它们创建PercentileWeight个对象。

我想向用户展示一个覆盖第一个间隙的“预制”PercentileWeight对象;例如,如果上述对象已经在系统中,则会向用户显示类似于的潜在对象:

var pw = new PercentileWeight
    {
        UpperBoundPercentageRanking = 90M,
        UpperBoundInclusive = true,
        LowerBoundPercentageRanking = 0M,
        LowerBoundInclusive = true,
        PercentageWeight = 100M
    };

问题在于:看起来这应该相对简单,但我不知道如何做到这一点。有人可以建议一种相对高效的方法来识别这些范围的集合中的差距吗?

1 个答案:

答案 0 :(得分:1)

这是一个看似简单的问题,但在实践中实施起来有点棘手。这是一种扩展方法,可以创建新的PercentileWeight来填补范围之间的所有空白。

public static class PercentileWeightExtension
{
    public const decimal Delta = 0.00000000000000000000000001M;

    public static IEnumerable<PercentileWeight> CoveringRange(this IEnumerable<PercentileWeight> inputs, PercentileWeight coveringRange)
    {
        //todo: following code expects no overlaps check that none exist

        //create lower and upper weights from coveringRange
        var lower = new PercentileWeight(decimal.MinValue, true, coveringRange.LowerBoundPercentageRanking, !coveringRange.LowerBoundInclusive);
        var upper = new PercentileWeight(coveringRange.UpperBoundPercentageRanking, !coveringRange.UpperBoundInclusive, decimal.MaxValue, true);

        //union new lower and upper weights with incoming list and order to process
        var orderedInputs = inputs.Union(new [] { lower, upper })
            .OrderBy(item => item.LowerBoundPercentageRanking)
            .ToList();

        //process list in order filling in the gaps
        for (var i = 1; i < orderedInputs.Count; i++)
        {
            var gap = GetPercentileWeightBetweenLowerAndUpper(orderedInputs[i - 1], orderedInputs[i]);
            if (gap != null)
            {
                yield return gap;
            }
            //dont want to output last input this represents the fake upper made above and wasnt in the original input
            if (i < (orderedInputs.Count - 1))
            {
                yield return orderedInputs[i];    
            }
        }
    }

    private static PercentileWeight GetPercentileWeightBetweenLowerAndUpper(PercentileWeight lowerWeight, PercentileWeight upperWeight)
    {
        var lower = lowerWeight.UpperBoundPercentageRanking;
        var lowerInclusive = lowerWeight.UpperBoundInclusive;
        var upper = upperWeight.LowerBoundPercentageRanking;
        var upperInclusive = upperWeight.LowerBoundInclusive;
        //see if there is a gap between lower and upper (offset by a small delta for non inclusive ranges)
        var diff = (upper + (upperInclusive ? 0 : Delta)) - (lower - (lowerInclusive ? 0 : Delta));
        if (diff > Delta)
        {
            //there was a gap so return a new weight to fill it
            return new PercentileWeight
            {
                LowerBoundPercentageRanking = lower,
                LowerBoundInclusive = !lowerInclusive,
                UpperBoundPercentageRanking = upper,
                UpperBoundInclusive = !upperInclusive
            };
        }
        return null;
    }
}

这可以很容易地使用

public class Program
{
    public static void Main(string[] args)
    {
        //existing weights
        var existingWeights = new[] {
            new PercentileWeight(90, false, 95, true) { VotingWeightId = Guid.NewGuid() },
            new PercentileWeight(50, true, 60, false) { VotingWeightId = Guid.NewGuid() }
        };
        //get entire range with gaps filled in from 0 (non inclusive) to 100 (inclusive)
        var entireRange = existingWeights.CoveringRange(new PercentileWeight(0, false, 100, true)).ToList();
    }
}

哪个输出包含这些项目的新列表(所有新项目都有VotingWeightId Guid.Empty

  • 0(不包括)至50(不包括)(新)
  • 50(含)至60(不包括)
  • 60(含)至90(含)(新)
  • 90(不包括)至95(含)
  • 95(含)至100(含)(新)