大整数

大整数高精度计算3——快速傅里叶/数论变换及分治除法

在上一篇介绍了基础优化算法后,本篇介绍更复杂的内容。本篇的三大内容:FFT,NTT,分治除法。中文资料中我尚未发现有博客文章在介绍分治除法的,所以我就来写第一个介绍吧。 FFT (Schönhage–Strassen algorithm) FFT就是快速傅里叶变换的缩写,FFT这里不重点介绍,参见oiwiki中对FFT的介绍 用比较显浅且简要的方式来介绍的话,FFT可以在$O(nlogn)$时间里(注:严格来说应该是$O(nlognloglogn)$,论文链接在这,在274页7.8处开始证明)求出两个多项式的乘积,而大整数乘法就可以视为两个整数系数的多项式的积再处理进位即可。那这样就产生一个问题,如果这个大整数采用的基数base,且长度为n,那么多项式的积的最大值可能会达到$n(base-1)^2$,因为FFT采用浮点运算,那么这个最大值必须在double的精度内,所以如果采用万进制,那么很容易导致精度不足进而结果错误,另外,还有求单位复根时的误差也会让它提早出现结果错误。实际应用的时候,一般采用10进制来增加长度的上限。所以如果你的大整数采用压位表示,那么在运用FFT之前,要先做拆位拆成10进制。总之,因为浮点精度的问题,不建议在大整数实现里使用FFT,建议采用下文要介绍的NTT来代替。所以这里对FFT的优化不做介绍,重心放在NTT上。 NTT NTT就是快速数论变换,它和FFT很像,但它在整数域上做变换,具体数学介绍参见oiwiki中对NTT的介绍。由于没有浮点数参与,于是没有精度上的问题。

大整数高精度计算2——乘法优化及进制转换

在上一篇介绍了基础算法后,本篇介绍算法级别的优化。本篇的三大内容:乘法优化,任意进制输入,任意进制输出。 乘法优化 这里要介绍的,是Karatsuba发现的分治法,我们假设要相乘的两个数,都有2n位,那么这两个数就可以分别表示为$a_1base^n+a_2, b_1base^n+b_2$,其中,$a_1,a_2,b_1,b_2$是n位的大整数,那么,它们的积就是 $$\begin{align} & (a_1base^n+a_2) \times (b_1base^n+b_2) \\ & = a_1b_1base^{2n} + (a_1b_2+a_2b_1)base^n + a_2b_2 \\ & = a_1b_1base^{2n} + ((a_1+a_2)(b_1+b_2)-a_1b_1-a_2b_2)base^n + a_2b_2 \end{align}$$ 如果这样不够明显的话,我们用$c_1$代替$a_1b_1$,用$c_3$代替$a_2b_2$,得到 $c_1base^{2n} + ((a_1+a_2)(b_1+b_2)-c_1-c_3)base^n + c_3$ 于是,这里一共有3次乘法,比起原来的4次暴力乘法减少了1次。而里面的乘法又可以进行递归优化,时间复杂度从$O(n^2)$下降到$O(n^{log_23})$约$O(n^{1.585})$

大整数高精度计算——有趣的实现

这里收录一些有意思的实现,不过我都有进行改编以更方便使用,不过千万不要指望这性能有多高。收录的条件: 支持四则运算,必须包含除法及求余 支持字符串输入输出 代码不长 small biginteger library for contest 代码原作者Jane Alam Jan,你可以在Google上直接搜索small_biginteger_library_for_contest.pdf并下载到原始说明文档及代码,这里提供一份代码,改动不多,增加了int的构造函数和其它不等号的重载,乘法效率有优化,且增加输出到string而不直接输出终端,以便在其它场合使用更方便。

大整数高精度计算1——基础算法

最近在编写大整数库的过程中,踩到不少的坑,于是把一些有用的细节准备写成文章做整理。如果你只是想直接查找并使用一个大整数库,那直接上GMP即可,如果是用在比赛,那直接用我的MiniBigInteger项目,如果你是想学习个中细节,那你可以坐下来细品。 所谓大整数,又叫高精度运算,就是运算对象是上千位甚至到百万位,总之远远超过内置数据类型的表示范围,这类数字都叫大整数。而C/C++的标准库里目前并没有大整数库,于是这个轮子被反复制造了无数个,不过在github上比较有质量的轮子并没有很多。本文除了介绍基础实现,主要还是介绍优化方法。 大整数的表示 大整数的表示方法最常见的有4种: 直接使用string 使用定长数组(仅适用于竞赛) 使用链表 使用变长线性表例如vector 直接用string的方式适合初学者,输入输出直观,但缺点也非常明显,因为计算时需要在字符与数值之间来回转换,浪费太多不必要的时间,效率会非常差。不过如果你是初学者,先用string表示法来写未尝不是个好主意。但有个细节就是,如果想要效率高,最好把string前后倒置调整为低位在前再做运算,这样速度和实现难度都会低一些。 至于使用链表,好处是变长容易,变短也不难,但性能比用string的更差还更难写,这里就不谈了,以下介绍使用数组的表示法 为了在数组里表示一个大整数,如果我们采用10进制,表示123456789,那很简单,例如这样: