You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SWI-Prolog中基于CLPFD的通用幻方程序实现问题求助

幻方CLPFD实现问题描述

我正在尝试在SWI-Prolog中实现一个通用版本的“幻方”程序,但遇到了问题。以下是我的实现代码:

:- use_module(library(clpfd)).

sumok(Sum, Lst) :-
    sum_list(Lst, Sum).

solve(Size, Grid) :-
    length(Grid, Size), maplist(same_length(Grid), Grid),
    append(Grid, Cells),

    MaxVal #= Size * Size,
    Cells ins 1..MaxVal,
    all_distinct(Cells),

    RowColSum is round((1 + MaxVal) / 2 * MaxVal / Size),
    format("The sum in each row and column is ~d.\n", [RowColSum]),

    maplist(sumok(RowColSum), Grid),
    transpose(Grid, TransposedGrid),
    maplist(sumok(RowColSum), TransposedGrid),
    !.

run :-
    solve(4, Solution),
    write(Solution).

问题在于,在sumok中对行求和时,变量均未初始化,因此出现如下错误信息:

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [16] _33164 is 0+_33172
ERROR:   [15] lists:sum_list([_33210,_33216|...],0,34) at c:/program files/swipl/library/lists.pl:650
ERROR:   [12] '__aux_maplist/2_sumok+1'([[_33254|...],...|...],34) at prolog/magic_square_4x4.prolog:6
ERROR:   [11] solve(4,[[_33298|...],...|...]) at prolog/magic_square_4x4.prolog:17
ERROR:   [10] run at prolog/magic_square_4x4.prolog:23

我当然可以在计算RowColSum后插入labeling,但这样程序会运行到笔记本电池耗尽。请问正确的CLPFD实现方式是什么?


解决方案

错误核心

你使用了普通的sum_list/2求和,这个谓词要求参数必须完全实例化,但CLPFD中变量还处于约束状态、没有具体数值,因此触发了“参数未充分实例化”的错误。CLPFD提供了专门的约束谓词来处理这类未实例化变量的求和需求,不需要提前给变量赋值。

正确实现代码

:- use_module(library(clpfd)).

solve(Size, Grid) :-
    length(Grid, Size),
    maplist(same_length(Grid), Grid),
    append(Grid, Cells),

    MaxVal #= Size * Size,
    Cells ins 1..MaxVal,
    all_distinct(Cells),

    % 用整数运算计算幻和,避免浮点精度误差,同时用CLPFD约束符保持一致性
    RowColSum #= (1 + MaxVal) * MaxVal // (2 * Size),
    format("The sum in each row and column is ~d.\n", [RowColSum]),

    % 用CLPFD的sum/3约束行和列的和,替代sum_list
    maplist(sum_row(RowColSum), Grid),
    transpose(Grid, TransposedGrid),
    maplist(sum_row(RowColSum), TransposedGrid),
    % 补充完整幻方的对角线约束
    diagonal_sum(Grid, RowColSum),
    reverse(Grid, ReversedGrid),
    diagonal_sum(ReversedGrid, RowColSum),

    % 采用优化的labeling策略,大幅减少搜索空间
    labeling([ff, bisect], Cells).

sum_row(Sum, Row) :-
    sum(Row, #=, Sum).

diagonal_sum([], _).
diagonal_sum([[H|_]|Rest], Sum) :-
    sum_diag(Rest, H, 1, Sum).

sum_diag([], Acc, _, Acc).
sum_diag([Row|Rest], Acc, Index, Sum) :-
    NextIndex #= Index + 1,
    nth1(NextIndex, Row, Val),
    NewAcc #= Acc + Val,
    sum_diag(Rest, NewAcc, NextIndex, Sum).

run :-
    solve(4, Solution),
    maplist(writeln, Solution).

关键优化说明

  1. 替换求和逻辑:用CLPFD原生的sum(Row, #=, Sum)代替sum_list,支持在变量未实例化时添加求和约束,完全符合CLPFD的约束求解逻辑。
  2. 幻和计算优化:用整数除法//替代浮点运算和round,彻底避免精度误差,同时用CLPFD的#=保持约束语法一致性。
  3. 补充对角线约束:完整幻方要求两条对角线的和也等于幻和,之前的实现遗漏了这部分关键约束。
  4. 优化搜索策略:使用[ff, bisect]的labeling选项——ff优先选择约束最紧的变量,bisect将变量值域二分,能快速剪枝无效搜索分支,大幅提升求解速度。

运行效果

对于4阶幻方,该实现能快速输出结果,不会出现长时间卡顿的情况。运行run后会先打印幻和,再输出一个合法的4阶幻方解。

内容的提问来源于stack exchange,提问作者Hennes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 06:05:22