DuckDB 最近合并的提交 https://github.com/duckdb/duckdb/pull/26932 ,给bignum数据类型增加了乘法和整除功能。
-
下载二进制文件
C:\d>wget https://artifacts.duckdb.org/staged/v2.0-cyanoptera/duckdb-cli-windows-amd64.tar.gz -O duckdb-cli-windows-amd64-261020a.tar.gz
2026-10-20 17:46:48 (54.8 KB/s) - 'duckdb-cli-windows-amd64-261020a.tar.gz' saved [19907268/19907268]
-
用3种不同的方法求20000!的10进制位数
方法1:从小到大顺序,将积和下一个数逐个相乘
C:\d>duckdb1020a
DuckDB v2.1.0-alpha49002 (Unknown Version)
Enter ".help" for usage hints.
memory D .timer on
memory D with recursive t as (select 1 a,'1'::bignum b union all select a+1,b::bignum*(a+1)::bignum from t where a<20000)select length(b::varchar)from t where a=20000;
┌────────────────────────────┐
│ length(CAST(b AS VARCHAR)) │
│ int64 │
├────────────────────────────┤
│ 77338 │
└────────────────────────────┘
Run Time (s): real 0.637 user 0.625000 sys 0.000000
方法2:从小到大顺序,分别将奇数和偶数之积和下一个同类数逐个相乘,目的是减少中间值的位数
memory D with t1 as(
with recursive t as (select 1 a,'1'::bignum b union all select a+2,b::bignum*(a+2)::bignum from t where a<19999)from t where a=19999
),
t2 as(
with recursive t as (select 2 a,'2'::bignum b union all select a+2,b::bignum*(a+2)::bignum from t where a<20000)from t where a=20000
)
select length((t1.b*t2.b)::varchar) from t1,t2;
┌────────────────────────────────────────┐
│ length(CAST((t1.b * t2.b) AS VARCHAR)) │
│ int64 │
├────────────────────────────────────────┤
│ 77338 │
└────────────────────────────────────────┘
Run Time (s): real 0.405 user 0.562500 sys 0.031250
方法3:将相邻的数两两分别相乘,保存积,再对积同样处理,继续迭代直到只剩1个数,目的是尽量使相乘的两数大小类似
memory D with recursive t(a) as(
select list(i::bignum) from range(1,20001)t(i)
union all
select [a[i]*case when i+1<=length(a) then a[i+1] else 1 end for i in range(1,length(a)+1,2)]
from t where length(a)>1)select length(a[1]::varchar)from t where length(a)=1;
┌───────────────────────────────┐
│ length(CAST(a[1] AS VARCHAR)) │
│ int64 │
├───────────────────────────────┤
│ 77338 │
└───────────────────────────────┘
Run Time (s): real 0.050 user 0.046875 sys 0.000000
补记:DeepSeek建议的两种写法
1.使用duckdb1.3以上的using key语法减少保存无用中间结果
memory D WITH RECURSIVE fact(k, n, val) USING KEY (k) AS (
SELECT 1, 1, 1::BIGNUM
UNION ALL
SELECT k, n + 1, val * (n + 1)::BIGNUM
FROM fact
WHERE n < 20000
)
SELECT length(val::VARCHAR)
FROM fact;
┌──────────────────────────────┐
│ length(CAST(val AS VARCHAR)) │
│ int64 │
├──────────────────────────────┤
│ 77338 │
└──────────────────────────────┘
Run Time (s): real 0.734 user 1.390625 sys 0.015625
2.使用lambda语法消除递归CTE
memory D select length(list_reduce(range(1,20001)::bignum[], lambda x,y: x*y)::varchar) a;
┌───────┐
│ a │
│ int64 │
├───────┤
│ 77338 │
└───────┘
Run Time (s): real 0.542 user 0.531250 sys 0.000000
memory D select length((list_reduce(range(1,20001,2)::bignum[], lambda x,y: x*y)*list_reduce(range(2,20001,2)::bignum[], lambda x,y: x*y))::varchar) a;
┌───────┐
│ a │
│ int64 │
├───────┤
│ 77338 │
└───────┘
Run Time (s): real 0.309 user 0.281250 sys 0.031250