什么是阿姆斯特朗数
阿姆斯特朗数(Armstrong Number),也叫做自幂数(Narcissistic Number),指的是一个 n 位数,其各个位上的数字的 n 次幂之和等于它本身。
对于三位数来说,就是每位数字的立方和等于该数本身。
常见的阿姆斯特朗数有:0、1、153、370、371、407、1634等。
具体验证(以407为例):
407 = 4³ + 0³ + 7³
= 64 + 0 + 343
= 407
个人经验:很多资料只说“立方和”,但严格来说那是针对三位数的特殊情况。像1634是四位数,需要计算的是4次方和。初学阶段通常先掌握三位数的立方和版本,理解之后再扩展到通用次方。
核心实现逻辑
判定一个数是否为阿姆斯特朗数,需要以下几个步骤:
-
保存原始数值(后面需要做比较)
-
用循环逐位取出每一位数字(通过取模和整除)
-
对每一位数字进行立方运算并累加
-
循环结束后,比较累加和与原始数值
关键操作符:
-
%取模运算符:得到之后一位数字 -
/除法运算:去掉之后一位数字
示例一:判定固定数字407
下面这段代码展示了如何判定407是不是阿姆斯特朗数。
<?php
$num = 407;
$sum = 0;
$temp = $num; // 保存原始值,因为后面$temp会被修改
while ($temp != 0) {
$remainder = $temp % 10; // 取出之后一位
$sum = $sum + $remainder * $remainder * $remainder; // 立方后累加
$temp = (int)($temp / 10); // 去掉之后一位,注意强制转整数
}
if ($num == $sum) {
echo "{$num} 是阿姆斯特朗数";
} else {
echo "{$num} 不是阿姆斯特朗数";
}
?>
输出:407 是阿姆斯特朗数
注意上面代码中的一个细节:$temp = (int)($temp / 10) 这里用 (int) 强制转换。PHP中除法运算的结果可能是浮点数,比如 407/10 = 40.7,转换成整数后变为40,这样才能继续循环。如果忘记转换,浮点数取模运算会出现意料之外的结果。
示例二:通过表单动态判定任意数字
代码号学习编程中,用表单接收用户输入是常见的交互方式。
<html>
<body>
<form method="post">
请输入一个数字:
<input type="number" name="number" step="1">
<input type="submit" value="判断阿姆斯特朗数">
</form>
</body>
</html>
<?php
if ($_POST) {
$number = $_POST['number'];
// 输入有效性检查
if (!is_numeric($number) || $number < 0) {
echo "请输入有效的非负整数";
} else {
$temp = $number;
$sum = 0;
while ($temp != 0) {
$remainder = $temp % 10;
$sum = $sum + ($remainder * $remainder * $remainder);
$temp = (int)($temp / 10);
}
if ($number == $sum) {
echo "{$number} 是阿姆斯特朗数";
} else {
echo "{$number} 不是阿姆斯特朗数";
}
}
}
?>
示例三:找出指定范围内的所有阿姆斯特朗数
这个扩展例子展示了如何批量查找,对于学习循环嵌套有参考价值。
<?php
$start = 1;
$end = 1000;
echo "{$start} 到 {$end} 之间的阿姆斯特朗数有:<br>";
for ($num = $start; $num <= $end; $num++) {
$sum = 0;
$temp = $num;
while ($temp != 0) {
$remainder = $temp % 10;
$sum = $sum + ($remainder * $remainder * $remainder);
$temp = (int)($temp / 10);
}
if ($num == $sum) {
echo $num . " ";
}
}
?>
输出:1 153 370 371 407
本节课程知识要点
| 知识点 | 说明 |
|---|---|
| 数字拆分技巧 | 反复用%10取末位,用/10去掉末位 |
| 类型转换 | PHP除法可能产生浮点数,需要(int)转换后再继续循环 |
| 原始值保存 | 循环过程中会修改临时变量,必须先保存原始值用于最终比较 |
| 边界条件 | 0和1也满足定义(0^3=0, 1^3=1),需要正确处理 |
扩展到n位数的自幂数判定
三位数的立方和判定是特例。如果要写一个更通用的函数,应该计算数字的位数,然后计算每位数字的位数次方之和。
function isArmstrong($num) {
$original = $num;
$digits = str_split((string)$num);
$power = count($digits);
$sum = 0;
foreach ($digits as $digit) {
$sum += pow((int)$digit, $power);
}
return $sum == $original;
}
echo isArmstrong(1634) ? "是" : "不是"; // 1634: 1⁴+6⁴+3⁴+4⁴ = 1634
为什么要用字符串方式处理:用数学方式(反复取模)需要预先知道数字位数,或者先统计位数再处理。用str_split转换成数组更直观,但要注意这种写法引入了字符串转换的开销。对于学习阶段来说两种方式都可以理解。
常见问题与排查
问题1:判断153时返回false
-
可能原因:
$temp/10没有转整数,导致浮点数累积误差 -
解决:用
intdiv($temp, 10)或(int)($temp/10)
问题2:输入0时循环不执行
-
分析:
while($temp != 0)在temp=0时直接跳过,temp=0时直接跳过,sum保持0,与原始值相等,结果正确。但逻辑上应该单独处理,方便他人阅读理解
问题3:大数判定结果错误
-
原因:PHP整数溢出或立方和超过变量范围
-
解决:对于大数判定,用BCMath扩展的任意精度函数
个人经验分享
当初学这个知识点时,我犯过一个低级错误——直接拿着$num做循环,之后拿修改过的$num去比较累加和,结果对不上。后来才意识到需要复制一份临时变量。这个小细节现在看起来很简单,但当时确实卡了一会儿。
另外说一下为什么不用pow($remainder,3)而写成$remainder*$remainder*$remainder。两个写能一样,但乘法运算比函数调用更快。虽然对于单个判定来说差别可以忽略,但如果在循环中频繁调用,或者在做范围查找时要执行成千上万次,乘法方式的性能会好一些。不是说要刻意优化,而是养成写高效代码的习惯。