PHP递归算阶乘易卡顿因无尾调用优化致栈溢出与内存耗尽,应改用迭代、对数累加或bcmath/gmp扩展,并严格校验输入范围。

递归算阶乘为什么越算越卡
PHP 默认递归没有尾调用优化,每次调用都压栈,n 一过百,memory_limit 就可能被撑爆;n = 1000 时常见 Fatal error: Allowed memory size exhausted。更隐蔽的问题是,递归函数反复创建作用域、传参、返回,CPU 时间其实也被悄悄吃掉。
- 别用
function factorial($n) { return $n 算大数 - 递归深度超过 100 就该警惕——不是代码错,是 PHP 运行模型限制
- 开启
xdebug时卡顿会更明显,因为额外增加了调用栈追踪开销
改用 for 循环后还是慢?检查数据类型
PHP 的整数溢出和自动转 float 是隐形性能杀手。比如 171! 已超出 float 精度(PHP float 最大约 1.8e308),之后所有计算变成近似值,且 float 运算比 int 慢得多;而 gmp 或 bcmath 虽能保精度,但调用开销大,不能无脑套用。
for ($i = 2; $i 在$n > 100后会悄悄把$result转成float,后续乘法变慢 + 失去精度- 确认是否真需要完整结果:如果只取末尾零个数、模某个数,完全不用算全量
- 小数据(
$n )用原生int最快;中等(20 ≤ $n ≤ 1000)考虑bcmath;超大数再上gmp
用 bcmath 加速但结果不对?注意参数传入格式
bcmath 所有函数只认字符串,传整数或 float 会导致截断或科学计数法解析失败。例如 bcadd('123', 456) 中的 456 会被转成字符串,但若原始值已是 1.23e+40 这类 float,再进 bcmul() 就直接失真。
- 必须统一用字符串初始化:
$result = '1';,循环里写$result = bcmul($result, (string)$i); - 别在循环内混用
intval()或(int)强转——1000!远超 PHPINT_MAX(通常 231−1),强转直接变-1或0 -
bcscale(0)可设精度,但阶乘不需要小数位,保持默认即可,设高了反而拖慢
实际场景怎么选方案
没有“最快通用解”,关键看你要什么结果、输入范围、部署环境是否启用了扩展。本地开发开着 xdebug,gmp 可能比 bcmath 还慢,因为 GMP 函数调用路径更深。
立即学习“PHP免费学习笔记(深入)”;
- 仅判断是否大于某阈值(如
n! > 1e100)→ 改用对数累加:$log_sum += log($i);,避免大数运算 - Web 接口返回阶乘字符串 → 优先
bcmath(多数主机默认启用),$n > 5000再切到gmp - CLI 脚本批处理且已装
gmp→gmp_fact($n)比手写bcmath循环快 30%~50% - 输入
$n来自表单或 URL,务必先filter_var($n, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 10000]]),防恶意大数打崩服务
真正卡顿往往不在算法本身,而在没意识到 PHP 的整数生命周期、扩展加载状态、以及调试工具带来的干扰。跑一次 php -m | grep -E 'bcmath|gmp',比重写三遍循环更有用。











