按“等额”分组

时间:2014-09-01 19:43:30

标签: c# linq grouping

我正在尝试获取一个数字列表,并将它们放入> = N个组,使得每个组的总和大约(但不一定完全)相等,并且“异常值”可以在一组中他们自己的。

因此对于3组的目标和类似的输入

[3, 2, 1, 4, 2, 5]

输出可能是:

[[5,1], [4,2], [3,2]]

各组的各自总和为

6, 6, 5

我认为我的方法已经失效,因为伪代码看起来像这样:

let target = Ceil(Sum(Series) / NumberOfTargetGroups) //The ideal size of each group

while (count(UnpickedNumbers) > 0)
    let CurrentGroup = new group
    while (sum(CurrentGroup) < target)
        for each Unpicked in sortDesc(UnpickedNumbers)
            if (sum(CurrentGroup) + Unpicked)
                Add Unpicked to current group
                Remove unpicked from available numbers

我无法弄清楚如何将该逻辑转换为GroupBy(n => ...) - 想要这样做的原因是数字列表实际上来自一系列对象的属性我是想以这种方式分组。

1 个答案:

答案 0 :(得分:3)

分区是NP完全问题。 我已经预先准备好了片段:

public IEnumerable<IEnumerable<TObject>> Algo<TObject>(IEnumerable<TObject> source, int groups,
                                                       Func<TObject, int> intSelector)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }

    source = source.OrderByDescending(intSelector);
    var evaluated = source as IList<TObject> ?? source.ToList();
    if (groups > evaluated.Count())
    {
        throw new ArgumentException("Invalid group count.");
    }

    var result = new List<List<TObject>>();
    for (var i = 0; i < groups; i++)
    {
        result.Add(new List<TObject> { evaluated[i] });
    }

    for (var i = groups; i < evaluated.Count(); i++)
    {
        var bestIndex = 0;
        var bestSum = result[bestIndex].Sum(intSelector);
        for (var j = 1; j < result.Count; j++)
        {
            var sum = result[j].Sum(intSelector);
            if (sum < bestSum)
            {
                bestSum = sum;
                bestIndex = j;
            }
        }

        result[bestIndex].Add(evaluated[i]);
    }

    return result;
}

效率不高(有很多方法可以优化它),结果并不总是很优秀。但希望它能成为你的算法的基础(也许大约对你来说足够了 - 测试它!)。

编辑: 我已经为您修改了代码段 - 您不必使用GroupBy。用法:

var widgets = new List<Widget>  { W1, W2, etc. };
var result = Algo(widgets, groups: 3, intSelector: widget => widget.Height);