高精度阶乘其实就是进行了 n 次高精乘以低精,只要高精乘以低精会写,高精度阶乘就没问题。
那么先说一说高精乘以低精吧。
高精乘以低精就是把高精度数的每一位乘以低精度数,然后该位的数就是每一次乘积 mod 10 的余数,而 / 10 得到的数作为进位用的数。(这是在数组中每一个元素只存一位的前提下)
不过关于进位,有必要说一下。是先进位,在相乘,还是先相乘,再进位。在高精度加法中是无所谓的,然而在乘法中就必须分出个先后顺序了。(顿时感觉和线段树区间修改 lazy 标记有点像,也是考虑运算数序的问题)
假设先进位再相乘。比如2895 * 64(假设高精度数是2895)那么先给得数的最后一位进位,进0,然后再5 *64 = 320,留下来0,32记着用作进位,没毛病。下一位,先进位9 + 32 = 41,再用41 * 64 = 2624,留下来4,262记着用作进位,但是答案是185280,第二位是8,发现错了,可见这种方法并不能这么写。
回想一下小学竖式,确实是先把这一位相乘,再加上上一位进的数。
其实这两种写法都行,只不过先相乘再进位要有些小运算,没有先进位再相乘直观些。这里就只讲一下先进位再相乘吧。
具体的乘法上面已经模拟过了,就不再赘述。我只是想强调一下,得数的位数问题,因为最终的积有多少位,我们并不清楚。这就需要动态更新积的位数了。
理论上是积的位数最多等于乘数的位数之和,然而如果我们每一次都去数一遍乘数的位数之和,就显得有些愚笨了。有一个更简单的方法:假设题中的 n <= 1000,那么每一次相乘,最多增加4位。这时我们可以每一次都让积的位数增加4位,然后再删除前导零,这时得到的位数就是这一次相乘的真实位数了。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 const int maxn = 1e5 +5; //打表可知,开这么大时,n 最多能达到26439 7 int a[maxn], n, len = 0; 8 int main() 9 { 10 scanf("%d", &n); 11 a[0] = 1; 12 for(int i = 2; i <= n; ++i) 13 { 14 int jinwei = 0; 15 for(int j = 0; j <= len + 4; ++j) //假设每一次位数都增加4 16 { 17 a[j] = a[j] *i + jinwei; //先将这一位相乘,在进位 18 jinwei = a[j] / 10; 19 a[j] %= 10; 20 } 21 int x = len + 4; 22 while(x > 0 && a[x] == 0) x--; //删除前导零 23 len = x; //得到了真正的位数 24 } 25 while(a[len] == 0 && len > 0) len--; //最后再删除一遍前导零 26 for(int i = len; i >= 0; --i) printf("%d", a[i]); 27 printf(" "); 28 return 0; 29 }