分解质因数
把整数拆成质因数的幂次乘积,顺带给出全部约数、约数个数、约数和,以及它到底是质数、合数还是完全平方数。多个数一起填还能算最大公约数与最小公倍数。
上限是 1,000,000,000,000,000,000。单次计算的试除预算约 20,000,000 步,用完会标成「预算内没算完」而不是硬给个结果。
概览
| 数值 | 性质 | 分解式 | 约数个数 |
|---|---|---|---|
| 360 | 合数 | 2³ × 3² × 5 | 24 |
360
全部约数(共 24 个)
1、2、3、4、5、6、8、9、10、12、15、18、20、24、30、36、40、45、60、72、90、120、180、360
小质数表
100 以内的质数一共 25 个。试除法就是从这些数开始往上试的,先除掉小质数能让后面的候选范围迅速变小。
2、3、5、7、11、13、17、19、23、29、31、37、41、43、47、53、59、61、67、71、73、79、83、89、97
常见数的分解
| 整数 | 分解式 | 约数个数 | 特点 |
|---|---|---|---|
| 360 | 2³ × 3² × 5 | 24 | 又常见又好算 |
| 1024 | 2¹⁰ | 11 | 2 的幂 |
| 9973 | 9973 | 2 | 质数 |
| 123456 | 2⁶ × 3 × 643 | 28 | 拆出大质因数 643 |
| 9240 | 2³ × 3 × 5 × 7 × 11 | 64 | 约数特别多 |
| 65535 | 3 × 5 × 17 × 257 | 16 | 2^16-1 |
试除法到底在做什么
质因数分解的基本办法是试除:从最小的质数开始,能整除就一直除,除不动就换下一个候选。关键是候选数只需要试到根号 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 完全一样。要确认符号看输入值那一行就行,下面所有结果都属于它的绝对值。
相关:进制转换