【问题标题】:Square Puzzle Problem Solution with Constraint Programming使用约束编程的方形拼图问题解决方案
【发布时间】:2020-12-29 19:29:09
【问题描述】:

问题:用不接触或不重叠的正方形(任何大小)填充网格,即使在角落也是如此。下面的数字和在 右表示填充的网格方块的数量 对应的列/行。

为了解决这个问题,我应用了以下约束:放置的正方形应该是不相交的,并且为了确保网格正方形的数量是正确的,我将与给定行/列相交的正方形的长度总和限制为等于该行/列数。

但是,输出的解是 [1, 0, 0, 1] ([NumSquares, X, Y, SquareSize],一个在坐标 (0, 0) 中长度值为 1 的正方形,它应该是如右图所示(13 个不同大小和坐标的正方形)。

:- use_module(library(clpfd)).

:- include('utils.pl').

solve(Rows, Columns, Vars) :-
    % Domain and variables definition

    length(Rows, Size),   

    MaxNumSquares is Size * Size,                
    NumSquares #>= 0,                               
    NumSquares #< MaxNumSquares,      

    length(StartsX, NumSquares),                    
    length(StartsY, NumSquares),                   
    length(SquareSizes, NumSquares),                

    S is Size - 1,           
                           
    domain(StartsX, 0, S),                         
    domain(StartsY, 0, S),                          
    domain(SquareSizes, 1, Size),                  

    construct_squares(Size, StartsX, StartsY, SquareSizes, Squares), 

    % Constraints

    disjoint2(Squares, [margin(0, 0, 1, 1)]),
    lines_constraints(0, Rows, StartsX, SquareSizes),
    lines_constraints(0, Columns, StartsY, SquareSizes),

    % Solution search

    VarsList = [NumSquares, StartsX, StartsY, SquareSizes],
    flatten(VarsList, Vars),
    labeling([], Vars).

construct_squares(_, [], [], [], []). 

construct_squares(Size, [StartX|T1], [StartY|T2], [SquareSize|T3], [square(StartX, SquareSize, StartY, SquareSize)|T4]) :-
    StartX + SquareSize #=< Size,              
    StartY + SquareSize #=< Size,
    construct_squares(Size, T1, T2, T3, T4).  

% Rows and columns NumFilledCells cells constraints

lines_constraints(_, [], _, _).

lines_constraints(Index, [NumFilledCells|T], Starts, SquareSizes) :-
    line_constraints(Index, NumFilledCells, Starts, SquareSizes),
    I is Index + 1,
    lines_constraints(I, T, Starts, SquareSizes).

line_constraints(Index, NumFilledCells, Starts, SquareSizes) :-
    findall(
        SquareSize,
        (
            element(N, Starts, Start),  
            element(N, SquareSizes, SquareSize),  
            intersect(Index, Start, SquareSize)
        ),
        Lines),
    sum(Lines, #=, NumFilledCells).
    
% Check if a square intersects a row or column

intersect(Index, Start, SquareSize) :-
    Start #=< Index,
    Index #=< Start + SquareSize.

【问题讨论】:

    标签: prolog clpfd sicstus-prolog


    【解决方案1】:

    问题在于您的 line_constraint/4 谓词。在其中,您在 findall/3 中发布了一些 clpfd 约束。这意味着这些约束仅在findall/3 内有效。这是一种重写谓词以保持发布约束的方法(鉴于您使用的是 SICStus,我使用 do 循环样式,它只是递归谓词周围的语法糖):

    line_constraints(Index, NumFilledCells, Starts, SquareSizes) :-
        (
          foreach(Start,Starts),
          foreach(SquareSize,SquareSizes),
          foreach(Usage,Usages),
          param(Index)
        do
          Intersect #<=> ( Start #=< Index #/\ Index #< Start + SquareSize),
          Usage #= Intersect * SquareSize
        ),
        sum(Usages, #=, NumFilledCells).
    

    (注意我把第二个不等式改成了严格的不等式:方格的末端就在Start + SquareSize之前。)

    您可能会体验到,这个公式在减少搜索空间方面非常薄弱。改进它的一种方法(但我自己没有尝试过)是将lines_constraints/4 替换为一些累积约束。

    【讨论】:

    • 我已经替换了line_constraints 谓词,但程序在无限循环中运行。我做错什么了吗?我尝试使用以下输入 [2,2,2,2,3,2,2,5,3,4] (Rows) 和 [4,5,4,4,0,0,0,4,3,3](Columns) 运行它,这与我提交的拼图图像相对应
    • 我尝试了[1,0,1],[1,0,1][2,2,0],[2,2,0],它给出了正确的答案。正如我所说,输入越多,效率就会变得非常低...
    • 看起来像添加对称破坏约束确实允许解决更大的输入。在你的 solve/3 谓词中添加这个(在声明变量之后和标签之前):(foreach(X,StartsX), foreach(Y,StartsY), foreach([X,Y],StartsXY) do true),lex_chain(StartsXY),
    • 是的,但似乎我必须明确指定正方形的数量
    • 奇怪,我没有同样的问题。我在三个domain/3 调用之后添加了额外的行。在代码中的这一点上,正方形的数量无论如何已经固定(通过length(StartsX, NumSquares) 调用)。
    【解决方案2】:

    由于问题出在正方形的数量上,我将它们固定为尽可能高的(单元格总数除以四,因为它们必须不相交),但允许其宽度/高度等于零,有效不存在,然后允许将平方数限制在零和最大平方数之间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多