【问题标题】:Partition a list into subsets将列表划分为子集
【发布时间】:2011-12-21 17:47:06
【问题描述】:

我有一个我想划分为子集的项目列表。为了讨论,让我们说它们是文件。我希望每个子集最多包含 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;
}

【问题讨论】:

  • 看看TakeSkip LINQ 扩展方法。
  • 如果您正在寻找最佳选择,这是经典的bin packing problem。看起来您已经实现了首次拟合(没有预排序)。问题是您是在寻找更简洁的代码还是更好的解决方案?如果是前者,您可能会在Code Review SE 获得更好的运气。
  • @Oded - Take and Skip 不会检查额外的指标(在我的示例中是文件大小)。
  • @RonWarholic - 我认为这个问题必须有一个名称,谢谢!我最感兴趣的是更简洁的代码。在我的实际用例中,最佳分布并不是很重要。
  • @breischl - 公平点。但是TakeWhileSkipWhile 可以。

标签: c# .net ienumerable yield-return


【解决方案1】:

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

这意味着您应该寻找一种启发式方法,它可以让您在合理的时间内接近最佳解决方案。您的算法(首次拟合)是一个很好的起点。不费吹灰之力,可以通过减小大小对项目进行预排序来稍微改善它。然而,还有其他一些或多或少复杂的启发式方法可以提高速度和结果。

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

【讨论】:

    【解决方案2】:

    缺少 1MB 测试,但您的代码在我看来还可以。我认为没有比这更好的方法了。

    【讨论】:

    • 那是为了争论。为此,您可以调用我使用 maxMetricPerSet=1MB 发布的代码,并将 getMetric 函数作为返回文件大小的东西。
    • 抱歉,我认为单个文件的限制与多个文件的限制不同。那么你的代码就好了。随它去吧!
    猜你喜欢
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 2014-08-21
    • 1970-01-01
    相关资源
    最近更新 更多