卡普列加数是满足n²拆分为左右两部分(右段非空、长度≤n的位数,且不能全为零)后两部分和等于n的正整数;关键边界是右段长度取1到n位数之间,且右段需含有效数字。

什么是卡普列加数,以及判断逻辑的关键边界
卡普列加数的定义容易被误读:一个正整数 n(n > 1),其平方 n * n 拆成左右两部分(右半部分必须非空且长度 ≤ 原数位数),使得左右两部分之和恰好等于 n。注意:左半部分可以为 0(即允许前导零截断),但右半部分不能全为零(否则所有平方末尾带 0 的数都“成立”,比如 10 → 100 → "10"+"0" → 10+0=10,但这是错的)。
常见错误是忽略“右半部分必须有有效数字”这一约束,或错误地固定拆分位置(比如总按一半切)。实际上,对 n 位数,n<em>n</em> 最多有 2 digits(n) 位,应尝试所有合法拆分点:右段长度从 1 到 digits(n)(含)。
用 std::to_string 和字符串切分实现判断
适合小范围验证(n ≤ 10⁶),代码直观、不易越界:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 先计算
square = static_cast<long long>(n) * n</long>,避免int溢出 - 将
square转为std::string,记作s - 枚举右段长度
len_r∈ [1,std::to_string(n).size()] - 若
s.size() ,跳过(右段不能比整个字符串还长) - 取右段:
s.substr(s.size() - len_r);左段:s.substr(0, s.size() - len_r) - 左段为空字符串时视作 0;右段若以 '0' 开头但长度 > 1(如 "00", "05"),仍合法(卡普列加定义允许前导零)
- 转为整数相加,检查是否等于
n
bool isKaprekar(int n) {
if (n == 1) return true; // 特例:1² = 1 → "" + "1" → 0 + 1 = 1
long long sq = 1LL * n * n;
std::string s = std::to_string(sq);
int n_digits = std::to_string(n).size();
for (int r_len = 1; r_len <h3>用数学方式避免字符串转换,提升性能与兼容性</h3><p>当 <code>n</code> 较大(如接近 10⁹)时,<code>std::to_string</code> 和 <code>std::stoll</code> 有开销,且可能触发异常(如超长数字)。改用除法/取模更稳:</p>
- 设
sq = (long long)n * n - 枚举右段位数
r_len,对应除数base = pow(10, r_len) - 右段 =
sq % base - 左段 =
sq / base - 注意:仅当
base 时才需检查(否则右段超出数值范围) - 同样要排除右段为 0 的情况(即
sq % base == 0且base > sq不成立,但sq % base == 0本身不违法——例如 9 → 81 → 8+1=9,没问题;而 10 → 100 → 100%10==0,100/10==10,10+0==10,但按定义右段 "0" 是单数字,允许;不过标准定义要求右段非空且不能是全零值,所以 100%100==0 时右段是 "00",此时应拒绝)
关键点:只接受 sq % base != 0 或 base == 1(个位数右段)的情形,但更稳妥做法是——只要 sq % base == 0,就要求 r_len == 1,否则跳过。实际主流实现(OEIS, Rosetta Code)接受 sq % base == 0,只要左+右=n,即承认 10 是卡普列加数。但严格按原始论文,Kaprekar 要求右段是“非零整数”,因此推荐加判断:if (r_len > 1 && sq % base == 0) continue;
容易被忽略的特例和溢出陷阱
-
n = 1 是公认的卡普列加数,但它的平方是 1,无法自然拆成左右两非空段,需单独返回 true
-
int 类型在计算平方时极易溢出:即使 n 是 int 最大值(~2e9),平方达 ~4e18,远超 long long?不,long long 通常为 64 位(上限 ~9e18),所以对 n ≤ 1e9 安全;但若输入是 unsigned int(最大 4e9),平方必溢出,必须用 unsigned long long
- 字符串方式中,
std::stoll("00") 返回 0,没问题;但 std::stoll("") 抛异常,所以左段为空时必须显式判空
- 拆分点上限不是
sq 的位数,而是原数 n 的位数(定义明确限定右段长度 ≤ digits(n))
n = 1 是公认的卡普列加数,但它的平方是 1,无法自然拆成左右两非空段,需单独返回 true
int 类型在计算平方时极易溢出:即使 n 是 int 最大值(~2e9),平方达 ~4e18,远超 long long?不,long long 通常为 64 位(上限 ~9e18),所以对 n ≤ 1e9 安全;但若输入是 unsigned int(最大 4e9),平方必溢出,必须用 unsigned long long
std::stoll("00") 返回 0,没问题;但 std::stoll("") 抛异常,所以左段为空时必须显式判空sq 的位数,而是原数 n 的位数(定义明确限定右段长度 ≤ digits(n))卡普列加数判定真正麻烦的地方不在算法,而在对“右段有效性”的解释分歧——不同资料对 10、100、1000 是否属于该数列说法不一。写生产代码前,先确认你所遵循的定义版本。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










