分解质因数

把整数拆成质因数的幂次乘积,顺带给出全部约数、约数个数、约数和,以及它到底是质数、合数还是完全平方数。多个数一起填还能算最大公约数与最小公倍数。

上限是 1,000,000,000,000,000,000。单次计算的试除预算约 20,000,000 步,用完会标成「预算内没算完」而不是硬给个结果。

试除法到底在做什么

质因数分解的基本办法是试除:从最小的质数开始,能整除就一直除,除不动就换下一个候选。关键是候选数只需要试到根号 n——如果 n 还有一个大于根号的因子,那它必然配着一个小于根号的因子,而后者早就被试出来了。所以 n 是 360 时试到 18 就够了,不需要试到 359。

候选数也不必真的都是质数。这里用 6k±1 优化:除了 2 和 3,所有质数都形如 6k-1 或 6k+1,于是循环每轮只检查两个候选,比逐个数字试快大约三倍。合数候选(比如 25、35)在轮到它们之前就已经被更小的质因数除干净,所以就算试到也不会影响结果。

为什么必须用 BigInt

JavaScript 的 Number 是双精度浮点,能精确表示的整数上限是 2 的 53 次方减一,也就是 9,007,199,254,740,991。一旦超过,相邻整数会被压到同一个浮点数上,取模和比较都可能出错,而且不会有任何报错——那种「看起来合理但就是不对」的结果最难查。质因数分解天生要处理大数,所以这里从解析开始就换成 BigInt,约数、约数和、最大公约数、最小公倍数也一路用 BigInt 传递。

代价是 BigInt 比 Number 慢,而试除法本身就是根号 n 级的。页面给了一段试除预算(约 20,000,000 步):10 的 15 次方附近的质数大致要一千多万步、不到一秒;10 的 18 次方附近的半素数会在预算内停下并标成「预算内没算完」。这不是 bug,而是试除法在位数上的固有上限,真要分解这类数得换 Pollard rho、二次筛法这类算法。

约数的三个结论从哪来

设 n 分解成 p₁ 的 e₁ 次方乘 p₂ 的 e₂ 次方……,约数个数就是把每个指数加一再相乘:每个质因数可以选 0 到 e 个,组合数有限。约数和则是把每个质因数的等比数列分别求和再相乘,因为约数可以按「每个质因数取几个」独立组合。这两条说明约数个数是积性函数,也是很多数论题目的起点。

约数列表的规模可能比想象中大得多,10 的 18 次方以内约数最多的整数有一万多个约数。页面默认先列 120 个,超出的折起来点「展开全部」再看,复制按钮复制的始终是完整列表。顺便一提,约数个数是奇数当且仅当这个数是完全平方数——约数成对出现,只有平方根跟自己配对,所以页面上单独标了「完全平方数」这一项方便核对。需要做进制转换或大数运算,可以顺手用进制转换和科学计算器。

常见问题

为什么结果里的质因数带着上标,还另给一份纯文本的写法?

上标那份(2³ × 3² × 5)是给人看的,指数的每一位都有对应的上标字符,所以任何指数都能写成上标。纯文本那份(2^3 × 3^2 × 5)用 ^ 代替上标,是为了贴进终端、任务单这类只认纯 ASCII 的地方。两份说的是同一件事,看哪份顺眼就复制哪份。

0 和 1 为什么没有分解结果?

1 既不是质数也不是合数,它没有质因数,硬拆只能得到空乘积,所以这里直接标注性质而不给分解式。0 是乘法里的吸收元,任何数乘 0 都还是 0,谈不上分解,也不能参与最大公约数和最小公倍数,所以同样单独提示。

为什么必须用 BigInt,普通的 Number 不行吗?

JavaScript 的 Number 是双精度浮点,只能精确表示到 2 的 53 次方减一(9,007,199,254,740,991)。超过这个数之后相邻整数会挤在同一个浮点数上,取模和比较都会得到错误结果,而且不报错。质因数分解要处理的正是大数,所以这里从解析到约数、公约数、公倍数全程用 BigInt。

试除到一半停下来是什么意思?

试除法要试到根号 n,位数一多就跑不完。页面给了一段试除预算,用完还没分解完就如实标成「预算内没算完」,并告诉你已经试到哪个因数、花了多少毫秒,而不是把剩下的部分当成质数硬塞进结果。要分解这类大数需要 Pollard rho、二次筛法这类算法,或者直接查因数库。

一个 10 的 15 次方附近的质数要算多久?

根号约三千万,用 6k±1 优化后大约一千多万次试除,常见机器上不到一秒。再往上代价按平方增长:到 10 的 18 次方附近,根号是十亿级,就算跑满预算也试不完,会直接标成未算完。这个变慢是试除法的固有上限,不是页面卡住了。

最小公倍数会溢出吗?

不会,全程用 BigInt,不受 2 的 53 次方这个上限约束,几个大数的最小公倍数再大也算得出来。要注意的是它可能大得离谱——几个互质的大数相乘时位数会一下子涨上去,数值本身没有错,只是比输入长很多。

约数个数和约数和是怎么来的?

先把数写成质因数幂次的乘积:约数个数就是每个指数加一再相乘,约数和是把每个质因数的等比数列(1 + p + p² 一直到 p 的 e 次方)各自求和再相乘。比如 360 = 2³ × 3² × 5,约数个数是 4 × 3 × 2 = 24,约数和是 15 × 13 × 6 = 1170。

相关:科学计算器

负数怎么处理?

负号不参与质因数分解,先把绝对值拆开,再单独提示原数是负数。因为质因数分解本身定义在自然数上,-360 的分解式和 360 完全一样。要确认符号看输入值那一行就行,下面所有结果都属于它的绝对值。

相关:进制转换