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

编写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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:51:59