递归阶乘在php中$n>100应停止,因无尾调用优化,易栈溢出;建议改用迭代、关闭xdebug、校验输入范围,并监控内存使用。

递归阶乘卡在 $n > 100 就该停了
PHP 没有尾调用优化,factorial(200) 这种调用会压入 200 层栈帧,每层都新建作用域、拷贝参数、等待返回——不是代码写错了,是 PHP 运行时根本扛不住。更隐蔽的是:xdebug 开着时,栈追踪会让卡顿翻倍;memory_limit 被耗尽前,CPU 已经在反复调度上下文里空转。
实际建议:
- 把
$n > 100当作硬警戒线,超过就拒绝递归,改用迭代或扩展 - 上线环境务必关掉
xdebug,开发机也别开着它跑阶乘测试 - 用
ini_get('memory_limit')和memory_get_usage()监控,别等Fatal error: Allowed memory size exhausted才发现
别信“尾递归”能救性能
像 factorialTail($n, $acc = 1) 这种写法,看着像尾递归,但 PHP 不识别也不优化它——函数调用栈深度一点没少,只是把中间结果从返回值挪到了参数里。它对栈溢出没缓解,对执行速度也没提升,反而多传一个参数,增加开销。
真正有用的替代方案:
- 直接改用
for循环:$result = 1; for ($i = 2; $i - 如果
$n可能超PHP_INT_MAX(通常 231−1),循环里$result会悄悄转成float,精度和速度双崩——这时必须切到字符串计算 - 别在循环里写
(int)$i或intval($i),大数强转会变0或负值
bcmath 阶乘必须全字符串输入
bcmul() 看起来是救星,但传错类型就全白忙:bcadd('123', 456) 里那个 456 会被转成字符串,但如果原始值是 1.23e40 这种 float,进 bcmul() 前就已经失真。
正确姿势只有这一种:
- 初始化:
$result = '1'; - 循环中:
$result = bcmul($result, (string)$i); - 别设
bcscale(),阶乘无小数位,设高了反拖慢 - 确认主机开了
bcmath扩展:extension_loaded('bcmath')判断一下再用
真要算大阶乘?先问自己要不要完整结果
90% 的生产场景根本不需要 1000! 的全部数字——比如只校验末尾零个数、只比大小、只取模 1e9+7,这些完全不用构造大整数。
例如判断 n! > 1e100:
- 用对数累加:
$log_sum = 0; for ($i = 2; $i - 然后比
$log_sum > log(1e100),毫秒级完成,内存占用恒定 - 若部署环境没装
gmp,bcmath又嫌慢,这是最稳的兜底方案
最易被忽略的点:卡顿往往不在算法,而在没校验输入范围。表单或 URL 来的 $n 必须过 filter_var($n, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1000]]),否则一个 ?n=100000 就能让服务假死。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











