SWI约束:如何设置变量的列表元素数量与值域约束?
解答SWI-Prolog约束相关问题
嘿,这几个都是CLP(FD)里很常见的约束场景,我给你逐个拆解,附上代码示例:
1. 限制列表最多包含N个域D中的元素
这里分两种常见理解,我都给你实现方案:
情况A:统计列表中属于域D的元素数量 ≤ N
用CLP(FD)的布尔约束+求和来实现,精准统计符合域要求的元素个数:
:- use_module(library(clpfd)). max_domain_elements(List, D, N) :- % 给每个元素生成布尔变量:1表示元素属于D,0表示不属于 maplist(elem_in_domain(D), List, Bools), % 布尔变量的总和不能超过N sum(Bools, #=<, N). elem_in_domain(D, X, B) :- X in D #<==> B. % 建立元素归属和布尔值的等价约束
比如调用max_domain_elements([1,2,6,7], 1..5, 2).会成功,因为前两个元素属于1..5,数量刚好是2。
情况B:列表长度≤N,且所有元素都属于D
如果需求是列表本身最多N个元素,且每个元素都在D里,直接结合长度约束和元素域约束即可:
max_length_domain(List, D, N) :- length(List, Len), Len #=< N, maplist(in(D), List).
2. 让变量接受指定域内的值组成的列表
完全可以实现!核心就是给列表变量的每个元素加上域约束,还能按需限制列表长度:
如果只要求列表元素都属于指定域(长度不限):
:- use_module(library(clpfd)). list_in_domain(L, D) :- maplist(in(D), L).
如果需要限制列表长度范围(比如1到5个元素):
list_in_domain_bounded(L, D, MinLen, MaxLen) :- length(L, Len), Len in MinLen..MaxLen, maplist(in(D), L).
调用list_in_domain_bounded(L, 1..3, 2, 3), label(L).会生成所有长度为2或3、元素是1-3的列表。
3. 设置约束让列表最多包含3个值域为1..5的元素
对应上面的情况B,直接套用就能实现:
:- use_module(library(clpfd)). max_3_elements_1to5(List) :- length(List, Len), Len #=< 3, maplist(in(1..5), List).
如果你的需求是列表中属于1..5的元素不超过3个(不管列表总长度),就复用第一个问题的情况A代码:
max_3_in_1to5(List) :- max_domain_elements(List, 1..5, 3).
4. 设置变量Y使其接受最多3个元素,且元素属于变量X的域(X的域为1..5)
先明确X的域范围,再把这个域应用到Y的每个元素上,同时限制Y的长度不超过3:
:- use_module(library(clpfd)). y_constraint(Y) :- % 定义X的域 X in 1..5, % 获取X的域范围(如果X的域后续会动态缩小,这步依然有效) domain(X, Min, Max), Domain = Min..Max, % Y最多3个元素,每个都属于X的域 length(Y, Len), Len #=< 3, maplist(in(Domain), Y), % 可选:标签化生成具体的解 label(Y).
调用y_constraint(Y).会输出所有符合条件的Y,比如Y = []、Y = [1]、Y = [2,5]、Y = [3,1,4]等等。
内容的提问来源于stack exchange,提问作者sten
相关产品推荐
相关产品推荐

