编写Prolog谓词判断平方数序列是否收敛于1
判断序列是否收敛到1的Prolog实现
我来帮你搞定这个判断序列收敛到1的Prolog谓词!先明确需求:给定数字N,按照「每个数是前一个数各位数字的平方和」生成序列,要判断这个序列最终会不会收敛到1——其实这就是判断N是不是快乐数对吧?
你已经写了两个关键的辅助谓词,我先帮你梳理下它们的作用,再补全核心的收敛判断逻辑:
辅助谓词解析
1. 拆分数字为各位数字列表
splitRev(0, []). splitRev(N, [A|As]) :- N > 0, % 新增判断,避免N=0时的冗余匹配 N1 is floor(N/10), A is N mod 10, splitRev(N1, As).
这个谓词的作用是把数字N的各位数字拆成一个反转后的列表,比如splitRev(44, [4,4]),逻辑完全没问题:通过取模拿到最后一位,整除10缩小数字,递归处理剩下的部分。
2. 计算列表元素的平方和
sumSquares([], 0). sumSquares([X|L], N) :- X1 is X*X, sumSquares(L, N1), N is N1 + X1.
这个谓词负责累加列表中每个元素的平方值,比如sumSquares([4,4], 32),递归逻辑也是正确的,先处理剩余列表的平方和,再加上当前元素的平方。
核心收敛判断谓词
这里要注意一个关键问题:非快乐数的序列会进入循环(比如4→16→37→58→89→145→42→20→4...),如果不跟踪已经出现过的数字,程序会无限递归下去。所以我们需要一个额外参数来记录已访问的数字,避免死循环。
完整的实现代码如下:
% 拆分数字为各位数字的反转列表 splitRev(0, []). splitRev(N, [A|As]) :- N > 0, N1 is floor(N/10), A is N mod 10, splitRev(N1, As). % 计算列表元素的平方和 sumSquares([], 0). sumSquares([X|L], N) :- X1 is X*X, sumSquares(L, N1), N is N1 + X1. % 对外接口:判断N是否是快乐数(序列收敛到1) happy(N) :- converge(N, []). % 终止条件:当前数是1,判定为收敛 converge(1, _) :- !. % 截断回溯,避免不必要的计算 % 终止条件:当前数已出现过,说明进入循环,判定为不收敛 converge(Current, Visited) :- member(Current, Visited), !, fail. % 递归步骤:计算下一个数,加入已访问列表继续判断 converge(Current, Visited) :- splitRev(Current, Digits), sumSquares(Digits, Next), converge(Next, [Current|Visited]).
代码逻辑说明
happy(N)是对外调用的主谓词,初始化已访问列表为空,调用converge开始判断。converge(1, _):如果当前数是1,直接返回成功,!用来截断回溯,防止程序继续尝试其他子句。converge(Current, Visited):如果当前数已经在已访问列表中,说明序列进入了循环,直接返回失败。- 最后一个子句:计算当前数的下一个平方和数,把当前数加入已访问列表,递归判断下一个数。
测试示例
测试你给出的N=44:
?- happy(44). true.
因为序列是44→32→13→10→1,最终收敛到1。
测试非快乐数N=4:
?- happy(4). false.
因为序列进入循环,永远到不了1。
内容的提问来源于stack exchange,提问作者Norhther
相关产品推荐
相关产品推荐

