【问题标题】:Bin packing bruteforce without duplicates and empty bins没有重复和空垃圾箱的垃圾箱包装蛮力
【发布时间】:2015-07-26 10:37:08
【问题描述】:

我想找到所有方法将 n 元素分发到 b 垃圾箱,但没有“重复”和空垃圾箱。

示例

如果我有 n = 3 元素和 b = 2 垃圾箱并从这个 stackoverflow 线程 Bin packing bruteforce method 应用蛮力方法,我会得到以下结果:

[[0, 1, 2, 3], []]
[[0, 1, 2], [3]]
[[0, 1, 3], [2]]
[[0, 1], [2, 3]]
[[0, 2, 3], [1]]
[[0, 2], [1, 3]]
[[0, 3], [1, 2]]
[[0], [1, 2, 3]]
[[1, 2, 3], [0]]
[[1, 2], [0, 3]]
[[1, 3], [0, 2]]
[[1], [0, 2, 3]]
[[2, 3], [0, 1]]
[[2], [0, 1, 3]]
[[3], [0, 1, 2]]
[[], [0, 1, 2, 3]]

“重复”的定义

一半的结果是重复的。仅切换 bin 的顺序:first 和 last 相同,2nd 和 2nd to last 相同,等等...

空箱的定义

我不希望任何垃圾箱是空的。如果你看前面的例子,第一行和最后一行都有一个空的 bin。

【问题讨论】:

  • 所以你想添加一个验证步骤?如果是这样,请显示您的代码,以便可以添加该步骤。
  • 我宁愿创建一个算法,该算法本质上只产生满足上述标准的解决方案,而不是在创建每个可能的解决方案后丢弃解决方案。我用于上述结果的算法取自linked stackoverflow thread
  • @conscho 您的问题不清楚。经典的bin-packing 试图最小化垃圾箱的数量。在您的情况下,您想找到所有方法将 n 项目分发到 b 垃圾箱,不是吗?
  • 你想要的东西与对称性破坏密切相关。本文Discussion about Constraint Programming Bin Packing Models 列出了实现这一目标所需的约束。但这可能不容易添加到您的代码中,因为这些约束是为约束编程方法量身定制的,我可以推荐。
  • @karthik 你是绝对正确的。我更新了描述。

标签: algorithm brute-force bin-packing


【解决方案1】:

这样的分区数称为第二类斯特林数。 这些数字上的Wikipedia article 给出了递归关系,可以对其进行修改以提供用于生成这些分区的递归函数。以下 Python 实现使用memoization 来保持计算可行:

def add(a,p,i):
    #adds a to the ith cell of partition p
    #returns a new partiton
    return [piece + [a] if j == i else piece for j, piece in enumerate(p)]

def addToAll(a,p):
    #adds a to all pieces of p
    #returns a list of partitions
    return [add(a,p,i) for i in range(len(p))]

def partition(n,k):
    memoDict = {}
    def helperPart(n,k):
        if n == 0 and k == 0: return [[]]
        elif n == 0 or k == 0: return []
        elif (n,k) in memoDict:
            return memoDict[(n,k)]
        else:
            kParts = helperPart(n-1,k)
            kMinusParts = helperPart(n-1,k-1)
            parts = [part + [[n]] for part in kMinusParts]
            for p in kParts:
                parts.extend(addToAll(n,p))
            memoDict[(n,k)] = parts
            return parts
    return helperPart(n,k)

例如:

>>> partitions = partition(5,3)
>>> for p in partitions: print(p)

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

相当高效:将 10 个对象的 42,525 个分区生成到 5 个 bin 中只需不到一秒的时间。

【讨论】:

    【解决方案2】:

    看来我自己找到了解决办法。

    我改编了 this stackoverflow answer 的 Ruby 代码以满足我的需要:

    class Array
      def distribute_to_bins(bins_left)
        Enumerator.new do |yielder|
          if self.empty?
            yielder.yield([])
          else
    
            # If there is only one bin left, fill all remaining items in it
            min_elements_in_bin = if bins_left == 1
                                    self.size
                                  else
                                    1
                                  end
            # Make sure that there are sufficient items left to not get any empty bins
            max_elements_in_bin = self.size - (bins_left - 1)
    
            (min_elements_in_bin..max_elements_in_bin).to_a.each do |number_of_elements_in_bin|
              self.drop(1).combination(number_of_elements_in_bin - 1).map { |vs| [self.first] + vs }.each do |values|
                (self - values).distribute_to_bins(bins_left - 1).each do |group|
                  yielder.yield([values] + group)
                end
              end
            end
          end
        end
      end
    end
    

    执行如下:

    pp (1..5).to_a.distribute_to_bins(3).to_a
    

    将产生没有空箱或重复的所有可能性:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-15
      • 2012-09-27
      相关资源
      最近更新 更多