同一道除法,页上写了两遍
起因是我想核对一个数:先在该页的计算 tab 填 n=15、r=7,大数字格给出「6,435」;切到全组合生成 tab,用 E1 到 E15 凑出 15 个元素,格子又写「6,435」。把这页模板(9838 字节 / 236 行)原样摘进 Node 跑了一圈才看清:计算侧 L120 与生成侧 L207 是字面同构的一句 factorialBig(n) / (factorialBig(r) * factorialBig(n - r)),同一个数被算了两遍;再算上 combos 递归条数与统计行文案,同一组 (n,r) 有三个来源对账。
交叉测了 12 组同 (n,r),10 组的大数字格字符串逐字相等,连「6,435」这种带逗号形状也一致;另 2 组不等,且都不是算错,是分支差:(20,10) 撞在 15 元素闸门上、格子停在 markup 默认的「—」;(7,0) 计算侧出「1」,生成侧把 r<1 静默改成 1,算的其实是 C(7,1),出「7」。这就是组合计算器两 tab 的口径差。
| (n, r) | 计算 tab | 生成 tab |
|---|---|---|
| (8, 3) | 56 | 56 |
| (15, 7) | 6,435 | 6,435 |
| (20, 10) | 184,756 | — |
| (7, 0) | 1 | 7 |
展开式有开关,结果没有
L130 那句 if (n <= 20) 只圈住「展开」这一行。0≤r≤n≤20 的 231 格实测 231 格都带展开行;n=21..60 的 1660 格实测一格展开都没有,可同一格 (21,10) 的结果「352,716」照样给得出来。整段展开的项数只跟 r 走、不跟 n 走:固定 r=3,n=3、5、10、15、20 五格全是分子 3 项加分母 3 项,凑成 6 个因子,只有整串从 25 字符慢慢长到 28 字符;n 一到 21,这 6 个因子一起消失。
项数最多的是 (20,20):分子 20 项加分母 20 项,展开行写满 40 个因子、183 字符,算出来却是「1」;次一格的 (20,19) 是 38 个因子、174 字符,结果「20」。
图 1:固定 r=3 时五档 n 的展开项数与整串字符,以及项数封顶的两格。
对称性那句是真文案,算的却是三个整阶乘
L150「利用对称性简化计算」出现的那一屏,L120 仍照算 factorialBig(20)、factorialBig(19)、factorialBig(1) 三个完整阶乘,实测 factorialBig(20) = 2432902008176640000,19 位;展开行照旧写满 38 个因子,页内没有任何 min(r, n-r) 的约简。阶乘本身的账另有一页:阶乘计算器。
大数这侧:整除从不留余数,逗号按公式插
扫 0≤r≤n≤60 共 1891 格逐格取余,余数非 0 的 0 格——L120 是无取整保护的 BigInt 整除,在这段里从不截断。位数最长的一行 C(1000,500) 出 300 位,fmtBig(L99)给它插 99 枚千分位逗号,逗号数 = floor((位数-1)/3) 在整张表逐格相等。参照数 Number.MAX_SAFE_INTEGER = 9007199254740991(16 位)。另有一条不在页面路径上的 double 对照,第一处偏离落在 n=55、r=27,精确值 3824345300380220 对 double 的 3824345300380219.5。逐位账不在这篇重算,旧文里已列过表:BigInt 阶乘旧文。
图 2:四档结果的位数与逗号数,逗号按那条公式整张表逐格对齐。
r=0 的两种读法,和「看不见不等于清空」
计算 tab 里 5/0、0/0、20/0 三行都全过守卫出「1」,展开行退化成一字面「() / ()」(7 字符、0 个数字)——0≤0 这种边界不落到提前返回,因为 r > n 在这里是假。生成 tab 则完全两样:L184 把 r<1 静默改掉,r 输入「0」「-1」「abc」、空串、「1.9」五行都无 toast、统计行统一「每个组合取:1 个」、格子都是「4」。
两条隐藏面板的提前返回(L110 与 L114)差别只在有没有那句 toast,实测两种都不清格子:上一轮的「56」和 4 行 formula 全部残留,看不见并不等于被清空。输入框也都不设 max——comb-n、comb-r 是 min="0",comb-gen-r 是 min="1",JS 侧没有上限闸门(isFinite 与 Infinity 在本模板出现 0 次),所以 n=1000、r=500 直接出 300 位。
切一次 tab 会多算一遍
switchMode 的 L86 会顺手再算一次。实测:6 个元素配 r=3 连调 200 次生成函数,去重只剩 1 个状态「20:20:0」——combos 是函数内常量、每次重开,不会越点越多;同输入连跑两次 calcComb,formula 407 字符逐字节相同。切到生成 tab 会让格子从「—」变「6」。
代码本体这几笔账
脚本体 6607 字节 / 159 行,markup 里 id 共 13 个、math-* 类 27 处;7 个函数从上下文读回的源码在模板原文里逐字命中,最长的 generateCombinations 2532 字符。列表那一栏另有一条 5000 的显示条数旧账。
Q:两 tab 出的数一定相同吗?
不一定。同一句除法写了两遍,12 组里 10 组大数字格逐字相等;但 (20,10) 生成侧停在「—」、(7,0) 一侧「1」一侧「7」——差别全在分支,不在算式。相等只指大数字格字符串逐字相等,不涉及渲染或性能。
Q:为什么 n=21 就没有展开过程了?
L130 的 n <= 20 只管展开行的显隐,结果照常算。(21,10) 的「352,716」照样出来,只是那一屏少了分子与分母两排可见片段。
Q:r 填 0 到底算什么?
分 tab:计算侧认它合法,出 1;生成侧被 L184 改成 1,于是给出的是 C(n,1)。同是 r=0,两套读法。