如何将CLP(FD)整数解列表转换为连续区间(SWI-Prolog)
解决方案:CLP(FD)变量解列表转连续区间
方法1:直接从变量定义域生成区间(推荐)
如果你的变量Xi有明确的上下界约束(比如Xi in Low..High),完全不需要先枚举解列表,直接通过CLP(FD)内置的fd_dom/2获取定义域,再用适配步长区间的dom_intervals/2生成结构化区间:
- 先确保变量有上下界与模约束:
Xi in 1..100, Xi mod 3 #= 0.
- 扩展原
dom_intervals/2以支持步长区间:
dom_intervals(Dom, Intervals) :- phrase(dom_intervals1(Dom), Intervals). dom_intervals1(I) --> {integer(I)}, [I]. dom_intervals1(L..U) --> [L..U]. dom_intervals1(L..U#Step) --> [L..U step Step]. % 新增步长区间处理 dom_intervals1(D1 / D2) --> dom_intervals1(D1), dom_intervals1(D2).
- 调用方式:
fd_dom(Xi, Dom), dom_intervals(Dom, Intervals).
针对上面的示例,Intervals会直接返回[3..99 step 3],无需枚举所有解,效率更高。
方法2:从解列表生成区间(必须枚举解时)
如果因为变量无明确上下界等原因,必须通过res(Xi, Li)获取解列表,可以写一个谓词将有序的解列表合并为区间:
通用连续整数合并(步长为1)
list_to_continuous_intervals([], []). list_to_continuous_intervals([X], [X]). list_to_continuous_intervals([X,Y|Rest], Intervals) :- Y =:= X + 1, !, merge_continuous(X, Y, Rest, Merged), list_to_continuous_intervals(Merged, Intervals). list_to_continuous_intervals([X,Y|Rest], [X|Intervals]) :- list_to_continuous_intervals([Y|Rest], Intervals). merge_continuous(Start, Current, [], [Start..Current]). merge_continuous(Start, Current, [Next|Rest], Merged) :- Next =:= Current + 1, !, merge_continuous(Start, Next, Rest, Merged). merge_continuous(Start, Current, Rest, [Start..Current|Rest]).
针对倍数(固定步长N)的合并
如果解列表是N的倍数(比如3,6,9,...),可以用针对性的高效版本:
multiples_to_intervals(List, Intervals) :- List = [First|_], N is 3, % 替换为你的模数 (List = [_] -> Intervals = [First] ; multiples_merge(First, First + N, N, List, Intervals)). multiples_merge(Start, _, _, [Last], [Start..Last step N]) :- Last =:= Start + ((Last - Start) // N)*N. multiples_merge(Start, Current, N, [_, Next|Rest], Intervals) :- Next =:= Current, !, multiples_merge(Start, Current + N, N, [Next|Rest], Intervals). multiples_merge(Start, Current, N, Rest, [Start..(Current-N) step N|Intervals]) :- multiples_to_intervals(Rest, Intervals).
调用示例:
res(Xi, Li), % 得到Li = [3,6,9,...,99] multiples_to_intervals(Li, Intervals). % 返回Intervals = [3..99 step 3]
之前尝试失败的原因
fd_dom(Li, Di)错误:fd_dom/2的第一个参数必须是CLP(FD)变量,而非解列表Li,正确用法是直接对Xi调用fd_dom(Xi, Di)。member(Xtmpi, Li)枚举问题:member/2会逐个取出列表元素,每次只能处理单个值,自然只能得到单元素区间,无法合并整个列表的连续序列。
内容的提问来源于stack exchange,提问作者M.V.
相关产品推荐
相关产品推荐

