将列表分区为子集

时间:2011-12-21 17:47:07

标签: c# .net ienumerable yield-return

我有一个项目列表,我想将其划分为子集。为了便于讨论,我们可以说它们是文件。我希望每个子集最多包含5个文件,如果可能的话,子集中文件的总大小应小于1 MB。如果单个文件超过1MB,则它本身应该是一个子集。

我用稍微更通用的形式编写了这个,使用通用的“项目指标”而不是文件大小。但我怀疑有更简单和/或更好的方法来做到这一点。有什么建议?

这就是我所拥有的:

public static IEnumerable<IEnumerable<T>> InSetsOf<T>(this IEnumerable<T> source, int maxItemsPerSet, int maxMetricPerSet, Func<T, int> getMetric)
{
    int currentMetricSum = 0;
    List<T> currentSet = new List<T>();

    foreach (T listItem in source)
    {
        int itemMetric = getMetric(listItem);

        if (currentSet.Count > 0 && 
            (currentSet.Count >= maxItemsPerSet || (currentMetricSum + itemMetric) > maxMetricPerSet))
        {
            yield return currentSet;

            //Start a new subset
            currentSet = new List<T>();
            currentMetricSum = 0;
        }

        currentSet.Add(listItem);
        currentMetricSum += itemMetric;
    }

    //Return the last set
    yield return currentSet;
}

2 个答案:

答案 0 :(得分:2)

Bin包装是NP难问题。获得最佳解决方案的唯一方法是测试所有组合。如果存在固定数量的不同大小,则可以使用动态编程系统地完成(对于这种情况,存在answer on SO示例代码),但这种算法的运行时间很糟糕。

这意味着您应该寻找一种启发式方法,让您在合理的时间内接近最佳解决方案。你的算法(first-fit)是一个很好的起点。只需稍加努力,就可以通过减小尺寸来预先计算物品来略微改善。然而,还有一些其他或多或少复杂的启发式方法可以提高速度和结果。

Google search将其作为结果之一返回:Basic analysis of bin-packing heuristics(有paper分析结果)。显然,具有bin查找表最佳拟合算法在合理的运行时间内提供了良好的结果。

答案 1 :(得分:0)

缺少1MB测试,但是否则您的代码看起来不错。我认为没有一种明显更好的方法。