杂记 › 技术笔记 › 两 tab 各算一遍同一个 C(n,r):n=20 写满 40 个因子、n=21 一格不剩,300 位大数与 r=0 的两种读法

两 tab 各算一遍同一个 C(n,r):n=20 写满 40 个因子、n=21 一格不剩,300 位大数与 r=0 的两种读法

组合计算器把同一句阶乘除法写了两遍:12组里10组大数字格逐字相等、2组是分支差;展开行只在n≤20出现而结果照算,项数只跟r走;r=0两tab读法相反,大数全程BigInt整除无余数。

同一道除法,页上写了两遍

起因是我想核对一个数:先在该页的计算 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)5656
(15, 7)6,4356,435
(20, 10)184,756—
(7, 0)17

展开式有开关,结果没有

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」。

固定 r=3:展开的 6 个因子不随 n 变 n = 3 6 因子 25 字符 结果 1 n = 15 6 因子 n = 5 6 因子 25 字符 结果 10 n = 20 6 因子 n = 10 6 因子 26 字符 结果 120 n = 21 展开消失 项数最大 (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 阶乘旧文。

位数与 fmtBig 插进去的逗号数 C(20,10) 6 位 1 逗号 184,756 C(50,25) 15 位 4 逗号 126,410,606,437,752 C(100,50) 30 位 9 逗号 C(1000,500) 300 位 99 逗号 逗号数 = floor((位数-1)/3)

图 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,两套读法。

📎 相关文章

星号段长写死:脱敏页五套掩码固定切出 4、3、11、12 枚星号,18 位串被手机档抢走、空框回落 * 2026-11-13 锁是单向的、百分比是两把尺:pct 50 得 619×430,PNG 四档 quality 全 6971 B 2026-11-13 相对时间的「一个月」写死 30 天:时间戳页六档除数、13 位毫秒输入与 UTC+8/UTC-5 两档截断实测 2026-11-13 8 个元素算得出 40320 条却只列 5000 行:排列计算器的两道互不相干的上限、重复元素按 3! 计数与 n≤20 的过程断点 2026-11-12 投放链接的装配顺序与大小写折叠:UTM 生成器六态标注、两道硬上限与五栏各用一把尺实测 2026-11-12