将可变大小的项目平衡为粗略平衡的集合的算法

时间:2010-08-26 12:37:31

标签: c# asp.net algorithm math logic

我正在寻找一种算法,将不同大小的项目列表拆分为“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

7 个答案:

答案 0 :(得分:3)

最快的事情可能就是将每个新项目插入最小的列表中(其中“最小”是列表中所有项目大小的总和)。

答案 1 :(得分:1)

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

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

根据您的项目大小,您可能会相当简单地强制解决方案,寻找具有最小尺寸差异的组合。对于较大的集合,这变得非常困难,使用“简单”算法可能会让您更接近一个好的答案。

答案 2 :(得分:1)

看看job shop scheduling algorithms我们有多个不同大小的工作要在机器上分配,以便总生产时间最短。

答案 3 :(得分:1)

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

        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;
    }

答案 4 :(得分:0)

尝试非常基本的东西

        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)。可能我会将其他版本发布到。

答案 5 :(得分:0)

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

如果增加列数超过2,则没有伪多项式时间解决方案。 http://en.wikipedia.org/wiki/3-partition_problem

答案 6 :(得分:0)

以下是实现公认答案的通用代码:

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

      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;
         }
      }