【问题标题】:what is the best algorithm to solve 'toy matching puzzle'?解决“玩具匹配难题”的最佳算法是什么?
【发布时间】:2019-06-05 08:04:45
【问题描述】:

想象一下这样的谜题: puzzle

我有几种形状,例如:

  • 10 圈

  • 8 个三角形

  • 9 个方格

我还有一些盘子可以放形状,例如:

  • 板A:2个圆孔,3个三角孔,1个方孔

  • 板B:1个圆孔,0个三角孔,3个方孔

  • 板C:2个圆孔,2个三角孔,2个方孔

我想找到最小数量的盘子来放置所有形状(盘子不需要完全填充)

例如:

  • 我可以挑6个板[A、A、A、B、B、C],我可以插入所有形状

  • 但我也可以选择 [A, A, C, C, C],这也可以,

  • 所以这个问题的答案是:5

如果这个问题推广到 N 型形状和 M 型板, 解决这个问题的最佳算法是什么?答案的时间复杂度是多少?

【问题讨论】:

  • 你见过“改变”DP问题吗?很确定您可以应用类似的东西,但只需为您的 DP 矩阵添加更多维度。盘子就像面额,形状的数量就是尺寸的数量。
  • 你可以尝试一个branch and bound算法来解决这个线性约束优化问题
  • 这是一个简单的带约束的线性规划问题。查找该字段的支持模块。

标签: algorithm


【解决方案1】:

这个问题是一个NP-hard问题,一旦你意识到从bin packing problem到这个问题有一个非常简单的polynomial time reduction,就会更容易看到它。

我建议您使用integer linear programming 技术来解决它。

解决您的问题的 ILP 可以是:

// Data
Shapes  // array of integers of size n, contains the number of each shape to fit
Plates  // 2D array of size n * m, Plates[i][j] represents the number of shape of type i
        // that fit on a plate of type j
// Decision variables
X       // array of integer of size m, will represent the number of plates of each type to use
// Constraints
For all j in 1 .. m, X[j] >= 0   // number of plates cannot be negative
For all i in 1 .. n, sum(j in 1..m) Plates[i][j] * X[j] >= Shapes[i] // all shapes must fit
Objective function:
minimize sum(j in 1..n) X[j]

在 OPL 中编写伪代码,将其提供给 linear programming solver,鉴于此问题与装箱问题的相似性,您应该相当快地得到解决方案。

编辑:如果您不想费力学习 LP 基础知识、OPL、LP 求解器等......那么解决这个问题的最佳和最简单的方法将是一个很好的旧 branch and bound 实现问题。分支定界是一种非常简单且功能强大的算法,可用于解决范围广泛的问题......必须知道。

【讨论】:

    【解决方案2】:

    我认为应该使用动态编程来解决这个问题。

    这是一个伪代码解决方案(我还没有测试过,但我认为它应该可以工作):

    parts = the number of shapes we want to fit as a vector
    plates = the of plates we can use as a matrix (vector of vector)
    
    function findSolution(parts, usedPlates):
        if parts < 0: //all elements < 0
            return usedPlates;
        else:
            bestSolution = null //or anything that shows that there is no solution yet
            for X in plates:
                if (parts > 0 on any index where X is > 0): //prevents an infinite loop (or stack overflow because of the recursion) that would occur using only e.g. the plate B from your question
                    used = findParts(parts - X, used.add(X)); //elementwise subtraction; recursion
                    if (used.length < best.length):
                        //the solution is better than the current best one
                        best = used;
    
            //return the best solution that was found
            return best
    

    使用您问题中的值,初始变量将是:

    parts = [10, 8, 9]
    plates = [[2, 3, 1], [1, 0, 3], [2, 2, 2]]
    

    你会像这样启动函数:

    solution = findSolution(parts /*= [10, 8, 9]*/, new empty list);
    //solution would probably be [A, A, C, C, C], but also [C, C, C, C, C] would be possible (but in every case the solution has the optimal length of 5)
    

    使用此算法,您可以使用递归(这是大多数动态规划算法所做的)将问题划分为更小的问题。

    这种方法的时间复杂度并不高,因为您必须搜索所有可能的解决方案。 根据master theorem,时间复杂度应该类似于: O(n^(log_b(a))) 其中 n = a = 使用的板数(在您的示例 3 中)。 b(对数的底)不能在这里计算(或者至少我不知道如何计算),但我认为它会接近 1,这使得它成为一个很大的指数。但这也取决于部件向量中条目的大小和车牌向量中的项目(需要更少的车牌 -> 更好的时间复杂度,需要更多的车牌 -> 糟糕的时间复杂度)。

    所以时间复杂度不是很好。对于较大的问题,这将需要很长时间,但对于像您的问题这样的小问题,它应该可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-21
      • 2020-04-12
      • 2023-03-20
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      • 2010-11-08
      • 2016-06-02
      相关资源
      最近更新 更多