SWI-Prolog中模式匹配计数的高效算法实现咨询
高效SWI-Prolog模式计数优化方案(五子棋AI场景)
针对五子棋AI频繁棋盘评估的性能需求,以下是对原有模式计数实现的优化方案,核心目标是降低findall和append带来的开销,提升匹配与计数效率。
问题分析
原有实现依赖findall收集所有匹配结果再求和,且用append拆分列表实现子串匹配,在高频调用场景下会产生大量内存分配与列表操作开销,不利于AI决策的实时性。
优化方案1:递归累加计数+前缀匹配
该方案移除findall和append,改用递归遍历+前缀匹配的方式直接累加计数,避免临时列表存储与列表拆分开销。
% 主谓词:计算Pattern在List中的出现次数 count_occurrences(Pattern, List, Count) :- phrase(Pattern, PatternList), length(PatternList, PatternLen), count_occurrences_rec(List, PatternList, PatternLen, 0, Count). % 递归终止条件:剩余列表长度不足模式长度,返回当前计数 count_occurrences_rec(List, _Pattern, PatternLen, Current, Current) :- length(List, L), L < PatternLen, !. % 匹配成功:计数+1,从下一个元素继续遍历 count_occurrences_rec(List, Pattern, PatternLen, Current, Final) :- prefix(Pattern, List), !, NextCurrent is Current + 1, List = [_|Rest], count_occurrences_rec(Rest, Pattern, PatternLen, NextCurrent, Final). % 匹配失败:直接遍历下一个元素 count_occurrences_rec([_|Rest], Pattern, PatternLen, Current, Final) :- count_occurrences_rec(Rest, Pattern, PatternLen, Current, Final). % 高效前缀匹配谓词,替代原sublist实现 prefix([], _) :- !. prefix([H|T], [H|Rest]) :- prefix(T, Rest). % 原有DCG模式定义(修正HTML转义的箭头符号) rep(S, L) --> [], {L is 0} | [S], {L is 1} | [S], { L_1 is L - 1 }, rep(S, L_1), !. open_rep(S, L) --> [v], rep(S, L), [v], !.
测试验证
1 ?- count_occurrences(open_rep(n, 1), [v,n,v,n,v,v,v,v,v,v,n,v,v], Count). Count = 3. 2 ?- count_occurrences(open_rep(n, 3), [v,n,v,n,v,v,v,v,v,v,n,n,n,v,v], Count). Count = 1. 3 ?- count_occurrences(open_rep(n, 1), [v,n,v,n,v,v,v,v,v,v,n,n,n,v,v], Count). Count = 2.
优化点说明
- 移除
findall与sum_list:递归累加计数,避免创建存储所有匹配结果的临时列表,减少内存开销 - 前缀匹配替代列表拆分:
prefix/2直接匹配列表前缀,无需用append拆分列表,大幅降低列表操作的时间消耗 - 提前终止递归:当剩余列表长度小于模式长度时直接返回,避免无效匹配尝试
优化方案2:纯DCG流式计数
该方案完全基于DCG实现匹配与计数,省去将Pattern转换为列表的步骤,进一步提升性能。
% 主谓词:通过DCG直接计数Pattern出现次数 count_occurrences_dcg(Pattern, List, Count) :- phrase(count_pattern(Pattern, 0, Count), List). % DCG规则:匹配成功则计数+1,继续遍历 count_pattern(Pattern, Current, Final) --> call(Pattern), !, { Next is Current + 1 }, count_pattern(Pattern, Next, Final). % DCG规则:匹配失败则遍历下一个元素 count_pattern(Pattern, Current, Final) --> [_], count_pattern(Pattern, Current, Final). % DCG终止规则:遍历结束,返回最终计数 count_pattern(_, Final, Final) --> []. % 原有DCG模式定义 rep(S, L) --> [], {L is 0} | [S], {L is 1} | [S], { L_1 is L - 1 }, rep(S, L_1), !. open_rep(S, L) --> [v], rep(S, L), [v], !.
测试验证
1 ?- count_occurrences_dcg(open_rep(n, 1), [v,n,v,n,v,v,v,v,v,v,n,v,v], Count). Count = 3. 2 ?- count_occurrences_dcg(open_rep(n, 3), [v,n,v,n,v,v,v,v,v,v,n,n,n,v,v], Count). Count = 1. 3 ?- count_occurrences_dcg(open_rep(n, 1), [v,n,v,n,v,v,v,v,v,v,n,n,n,v,v], Count). Count = 2.
优化点说明
- 纯DCG流式处理:直接在DCG中调用Pattern进行匹配,省去
phrase(Pattern, PatternList)的转换步骤,减少额外计算 - 无中间存储:遍历列表时边匹配边计数,全程无需存储中间结果,性能更优
五子棋AI场景额外优化建议
- 预编译常用模式:将五子棋中高频使用的模式(如连子、活二等)提前转换为固定列表,避免每次调用时重新解析DCG
- 区域剪枝:在棋盘评估时,仅遍历可能形成目标模式的区域(如已有棋子的周边区域),减少无效遍历范围
- 启用SWI-Prolog编译优化:执行
set_prolog_flag(optimise, true)开启编译级优化,提升代码执行速度
内容的提问来源于stack exchange,提问作者René Chenard
相关产品推荐
相关产品推荐

