将不同大小的项目平衡为大致平衡的集合的算法

发布于 2024-09-16 12:45:13 字数 449 浏览 4 评论 0 原文

我正在寻找一种算法,将不同大小的项目列表拆分为“N”个大小相似的组。

具体来说,我正在使用 C# 开发一个 ASP.NET 站点,其中有一个(数据库检索的)字符串列表。字符串的长度各不相同。我有一组需要显示字符串的列。我需要一种算法来找到最平衡的集合(项目顺序无关),以允许最终的列尽可能平衡。

抽象示例:

创建 3 列。

要分发的项目:

 - Item A - height 5
 - Item B - height 3
 - Item C - height 7
 - Item D - height 2
 - Item E - height 3

期望的输出:

Column 1: Item A, Item D
Column 2: Item C
Column 3: Item B, Item E

I'm seeking an algorithm to split a list of items of varying sizes into "N" number of similarly-sized groups.

Specifically, I'm working on an ASP.NET site in C# where I have a (database-retrieved) list of strings. The strings are of varying lengths. I have a set of columns which need to display the strings. I need an algorithm that will find the most balanced sets (item order is irrelevant) to allow the final columns to be as balanced as possible.

Abstracted Example:

Creating 3 columns.

Items to distribute:

 - Item A - height 5
 - Item B - height 3
 - Item C - height 7
 - Item D - height 2
 - Item E - height 3

Desired output:

Column 1: Item A, Item D
Column 2: Item C
Column 3: Item B, Item E

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

紫南 2024-09-23 12:45:13

最快的方法可能是将每个新项目插入到最小的列表中(其中“最小”是列表中所有项目的大小之和)。

The quickest thing to do is probably just insert each new item into the smallest list (where "smallest" is the sum of the sizes of all the items in the list).

看海 2024-09-23 12:45:13

这似乎是包装盒(或装箱)问题的一个变体,您尝试将可变大小的物品集合放入尽可能少的容器中:

http://en.wikipedia.org/wiki/Bin_packing_problem

根据你的项目集的大小,你可能可以相当简单地暴力破解一个解决方案,看看用于尺寸差异最小的组合。对于较大的集合,这成为一个相当困难的问题,并且您可能会更好地使用“简单”算法,让您接近一个好的答案。

This seems like a variant of the Packing Boxes (or Bin Packing) problem, which is where you try to fit a collection of variable sized items into as few containers as possible:

http://en.wikipedia.org/wiki/Bin_packing_problem

Depending on the size of your set of items, you could probably brute force a solution fairly simply, looking for the combination with the smallest difference between sizes. For larger sets this becomes quite a difficult problem, and you might be better with a "simple" algorithm that gets you somewhere close to a good answer.

偏爱自由 2024-09-23 12:45:13

看看作业车间调度算法,其中我们有许多不同大小的作业分散在机器上,使总生产时间最短。

Have a look at job shop scheduling algorithms where we have a number of jobs of varying sizes to be distrubted over machines so that the total production time is minimal.

醉态萌生 2024-09-23 12:45:13

这是另一个版本,可以创建所需长度的组。

        public static List<List<int>> Balance(List<int> input, int desiredLimit)
    {
        var result = new List<List<int>>();

        if (input.Count > 0)
        {
            var values = new List<int>(input);
            values.Sort();

            var start = 0;
            var end = values.Count - 1;
            var orderedValues = new List<int>(values.Count);
            while (start < end)
            {
                orderedValues.Add(values[start]);
                orderedValues.Add(values[end]);
                start++;
                end--;
            }
            if (values.Count % 2 != 0)
            {
                orderedValues.Add(values[values.Count / 2]);
            }

            var total = 0;
            var line = new List<int>();

            for (int i = 0; i < orderedValues.Count; i++)
            {
                var v = orderedValues[i];
                total += v;
                if (total <= desiredLimit)
                {
                    line.Add(v);
                }
                else
                {
                    total = v;
                    result.Add(line);
                    line = new List<int>() { v };
                }
            }
            result.Add(line);
        }

        return result;
    }

Here's the other version which can create groups of desired length.

        public static List<List<int>> Balance(List<int> input, int desiredLimit)
    {
        var result = new List<List<int>>();

        if (input.Count > 0)
        {
            var values = new List<int>(input);
            values.Sort();

            var start = 0;
            var end = values.Count - 1;
            var orderedValues = new List<int>(values.Count);
            while (start < end)
            {
                orderedValues.Add(values[start]);
                orderedValues.Add(values[end]);
                start++;
                end--;
            }
            if (values.Count % 2 != 0)
            {
                orderedValues.Add(values[values.Count / 2]);
            }

            var total = 0;
            var line = new List<int>();

            for (int i = 0; i < orderedValues.Count; i++)
            {
                var v = orderedValues[i];
                total += v;
                if (total <= desiredLimit)
                {
                    line.Add(v);
                }
                else
                {
                    total = v;
                    result.Add(line);
                    line = new List<int>() { v };
                }
            }
            result.Add(line);
        }

        return result;
    }
东风软 2024-09-23 12:45:13

尝试一些非常非常基本的

        public static List<List<int>> Balance(List<int> input)
    {
        var result = new List<List<int>>();

        if (input.Count > 0)
        {
            var values = new List<int>(input);

            values.Sort();

            var max = values.Max();
            var maxIndex = values.FindIndex(v => v == max);
            for (int i = maxIndex; i < values.Count; i++)
            {
                result.Add(new List<int> { max });
            }
            var limit = maxIndex;
            for (int i = 0; i < limit / 2; i++)
            {
                result.Add(new List<int> { values[i], values[(limit - 1) - i] });
            }
            if (limit % 2 != 0)
            {
                result.Add(new List<int> { values[limit / 2] });
            }
        }

        return result;
    }

方法如果您需要按两个元素进行分组,则可以使用此方法。您可以将其更改为对元素进行分组,直到达到预定义值(例如 10)。也许我会将其他版本发布到。

Try something very very basic

        public static List<List<int>> Balance(List<int> input)
    {
        var result = new List<List<int>>();

        if (input.Count > 0)
        {
            var values = new List<int>(input);

            values.Sort();

            var max = values.Max();
            var maxIndex = values.FindIndex(v => v == max);
            for (int i = maxIndex; i < values.Count; i++)
            {
                result.Add(new List<int> { max });
            }
            var limit = maxIndex;
            for (int i = 0; i < limit / 2; i++)
            {
                result.Add(new List<int> { values[i], values[(limit - 1) - i] });
            }
            if (limit % 2 != 0)
            {
                result.Add(new List<int> { values[limit / 2] });
            }
        }

        return result;
    }

This method can be used in case you need to group by two elements. You can change it to group elements till a predefined value is reached (e.g. 10). Probably I'll post the other version to.

我要还你自由 2024-09-23 12:45:13

如果您有两列,这听起来像是分区问题的应用。该问题是 NP 完全问题,但存在伪多项式时间的动态规划解决方案。
http://en.wikipedia.org/wiki/Partition_problem

如果您将列数增加到超过二、则不存在伪多项式时间解。
http://en.wikipedia.org/wiki/3-partition_problem

If you have two columns, this sounds like an application of the Partition Problem. The problem is NP-complete, but there is a dynamic programming solution that is pseudo-polynomial time.
http://en.wikipedia.org/wiki/Partition_problem

If you increase the number of columns beyond two, then there is no pseudo-polynomial time solution.
http://en.wikipedia.org/wiki/3-partition_problem

锦上情书 2024-09-23 12:45:13

这是实现接受的答案的通用代码:

  • 只需将每个新项目插入最小的列表
  • 首先对项目进行排序(从大到小),这将改善很多

     class Item { 内部 Item(int 重量) { 重量 = 重量; } 内部 int 权重 { 获取; } }
    
      [测试]
      公共无效测试1(){
    
         var A = 新项目(5);
         var B = 新项目(3);
         var C = 新项目(7);
         var D = 新项目(2);
         var E = 新项目(3);
    
         Item[][] 结果 = AlgoToBuildBalancedPartition.Go(new[] { A, B, C, D, E }, t => t.Weight, 3);
         Assert.AreEqual(结果.Length, 3);
         Assert.Contains(C, 结果[0]);
         Assert.Contains(A, 结果[1]);
         Assert.Contains(D, 结果[1]);
         Assert.Contains(B, 结果[2]);
         Assert.Contains(E, 结果[2]);
      }
    
      //------------------------------------------------ --
    
      公共静态类 AlgoToBuildBalancedPartition {
    
         公共静态 T[][] Go(
                IEnumerable序列,
                Func;获取权重过程,
                int maxNbPartitions) 其中 T : 类 {
            Debug.Assert(!seq.IsNullOrEmpty());
            Debug.Assert(getWeightProc != null);
            调试.Assert(maxNbPartitions >= 2);
    
            var partitions = new List>(maxNbPartitions);
    
            T[] seqDescending = seq.OrderByDescending(getWeightProc).ToArray();
            int count = seqDescending.Length;
    
            for (var i = 0; i < count; i++) {
               T 项 = seqDescending[i];
               if (partitions.Count < maxNbPartitions) {
                  Partitions.Add(new Partition(item, getWeightProc));
                  继续;
               }
    
               // 获取权重最小的分区
               Debug.Assert(partitions.Count == maxNbPartitions);
               varpartitionWithMinWeight = 分区[0];
               for (var j = 1; j < maxNbPartitions; j++) {
                  var 分区 = 分区[j];
                  if (partition.TotalWeight >partitionWithMinWeight.TotalWeight) { 继续; }
                  partitionWithMinWeight = 分区;
               }
    
               PartitionWithMinWeight.AddItem(项目);
            }
    
            返回partitions.Select(p => p.Items.ToArray()).ToArray();
         }
    
    
         私有密封类 Partition;其中 T : 类 {
            内部分区(T firstItem, Func getWeightProc) {
               调试.Assert(firstItem != null);
               Debug.Assert(getWeightProc != null);
               m_GetWeightProc = getWeightProc;
               添加项目(第一个项目);
            }
            私有只读 Func m_GetWeightProc;
            内部列表项目{得到; } = 新列表();
            内部无效AddItem(T项){
               调试.Assert(item != null);
               项目.添加(项目);
               TotalWeight += m_GetWeightProc(item);
            }
            内部 int TotalWeight { 获取;私人套装; } = 0;
         }
      }
    

Here is the generic code that implement the accepted answer:

  • just insert each new item into the smallest list
  • sort the items first (from big to small), that will improve a lot

      class Item { internal Item(int weight) { Weight = weight; } internal int Weight { get; } }
    
      [Test]
      public void Test1() {
    
         var A = new Item(5);
         var B = new Item(3);
         var C = new Item(7);
         var D = new Item(2);
         var E = new Item(3);
    
         Item[][] result = AlgoToBuildBalancedPartition.Go(new[] { A, B, C, D, E }, t => t.Weight, 3);
         Assert.AreEqual(result.Length, 3);
         Assert.Contains(C, result[0]);
         Assert.Contains(A, result[1]);
         Assert.Contains(D, result[1]);
         Assert.Contains(B, result[2]);
         Assert.Contains(E, result[2]);
      }
    
      //--------------------------------------------------
    
      public static class AlgoToBuildBalancedPartition {
    
         public static T[][] Go<T>(
                IEnumerable<T> seq,
                Func<T, int> getWeightProc,
                int maxNbPartitions) where T : class {
            Debug.Assert(!seq.IsNullOrEmpty());
            Debug.Assert(getWeightProc != null);
            Debug.Assert(maxNbPartitions >= 2);
    
            var partitions = new List<Partition<T>>(maxNbPartitions);
    
            T[] seqDescending = seq.OrderByDescending(getWeightProc).ToArray();
            int count = seqDescending.Length;
    
            for (var i = 0; i < count; i++) {
               T item = seqDescending[i];
               if (partitions.Count < maxNbPartitions) {
                  partitions.Add(new Partition<T>(item, getWeightProc));
                  continue;
               }
    
               // Get partition with smallest weight
               Debug.Assert(partitions.Count == maxNbPartitions);
               var partitionWithMinWeight = partitions[0];
               for (var j = 1; j < maxNbPartitions; j++) {
                  var partition = partitions[j];
                  if (partition.TotalWeight > partitionWithMinWeight.TotalWeight) { continue; }
                  partitionWithMinWeight = partition;
               }
    
               partitionWithMinWeight.AddItem(item);
            }
    
            return partitions.Select(p => p.Items.ToArray()).ToArray();
         }
    
    
         private sealed class Partition<T> where T : class {
            internal Partition(T firstItem, Func<T, int> getWeightProc) {
               Debug.Assert(firstItem != null);
               Debug.Assert(getWeightProc != null);
               m_GetWeightProc = getWeightProc;
               AddItem(firstItem);
            }
            private readonly Func<T, int> m_GetWeightProc;
            internal List<T> Items { get; } = new List<T>();
            internal void AddItem(T item) {
               Debug.Assert(item != null);
               Items.Add(item);
               TotalWeight += m_GetWeightProc(item);
            }
            internal int TotalWeight { get; private set; } = 0;
         }
      }
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文