史密斯数是合数,其各位数字之和等于其所有质因数(含重复)的各位数字之和;例如22是史密斯数,因2+2=4且质因数2×11的各位和2+1+1=4;判定须排除质数、最小为4。

什么是史密斯数?先看判定逻辑
史密斯数不是质数,但它的各位数字之和等于其所有质因数(含重复)的各位数字之和。比如 22:它本身不是质数;各位和是 2 + 2 = 4;质因数分解为 2 × 11,质因数各位和是 2 + 1 + 1 = 4,所以它是史密斯数。
关键点有三个:必须合数、各位数字和要算原数、质因数要拆到最细(比如 12 = 2×2×3,不是 2×6)。很多人一开始漏掉“非质数”这个前提,直接对质数做判断,结果 13 也被误判为史密斯数——它各位和是 4,质因数只有自己,各位和也是 4,但史密斯数定义明确排除质数。
怎么写一个可靠的 isSmith 函数
核心分三步:先快速排除质数和小于 4 的数(最小史密斯数是 4),再算原数各位和,最后做质因数分解并累加每个质因数的各位和。
-
isPrime(n)要高效且正确:只试除到sqrt(n),注意n == 2返回 true,n 或偶数(除 2 外)直接 false - 各位数字和用循环取余更稳:
sumDigits(int n)里用n % 10和n /= 10,别用字符串转换(小题大做还慢) - 质因数分解时,**必须包含重复因子**:比如
96 = 2×2×2×2×2×3,不能只取2, 3;循环从i = 2开始,每次整除尽就累加该i的各位和,再继续除 - 别忘了处理剩余的大于 1 的因子:比如
22分解完2后剩11,它本身是质数,得把11的各位和也加上
示例片段:
立即学习“C++免费学习笔记(深入)”;
bool isSmith(int n) {
if (n < 4 || isPrime(n)) return false;
int sumOrig = sumDigits(n);
int sumFact = 0;
int temp = n;
for (int i = 2; i * i <= temp; i++) {
while (temp % i == 0) {
sumFact += sumDigits(i);
temp /= i;
}
}
if (temp > 1) sumFact += sumDigits(temp);
return sumOrig == sumFact;
}常见错误和边界情况
最容易出错的是这几个地方:
-
4是史密斯数(2×2→2+2 = 4),但有人写质因数分解时没进 while 循环,漏掉第二个2 -
1和0:题目限定“正整数”,但代码里没提前if (n <= 1)检查,可能导致sumDigits输入 0 时返回 0,而分解逻辑崩溃 - 溢出风险低,但若扩展到
long long,i * i <= temp可能溢出,应改用i <= temp / i - 性能上,
isPrime和质因数分解共用一套试除逻辑更省事,没必要单独写两个相似循环
验证时别只靠几个例子
手算几个典型值很必要:27(3×3×3 → 3+3+3 = 9,原数各位和 2+7 = 9,是史密斯数);30(2×3×5 → 2+3+5 = 10,原数 3+0 = 3,不是);1、2、3 都应返回 false。
真正麻烦的是大一点的数,比如 999:它不是质数,各位和是 27;分解为 3×3×3×37,各位和是 3+3+3+3+7 = 19,不等。这种靠直觉容易错,必须让代码跑一遍。
史密斯数稀疏但存在规律,实际使用中一般不会暴力枚举,但判定逻辑一旦写错,后续所有依赖都不可信——尤其是质因数分解那块,少一次 while 或漏掉最终 temp,结果就全偏了。


















