【FOJ】Problem 1050 Number lengths
数论的题目就是在我们看似只能用暴力方法解决或者甚至无解的时候,往往几个名人的XX公式,三下五除二就解决了。由此真心叹服这些数学家的杰出贡献!
下面介绍几个常见的类似问题,然后集中解决这个问题:
一、 求整数的最高位
总结来看,求某个数N的位数,常见的解决方法就是化成 10log10(N),接着将log10(N)分解成整数部分INT和小数部分FRA,即:log10(N)=INT+FRA -->10log10(N)= 10INT+FRA。 因为10INT肯定是10的倍数,对最高位没有任何贡献(因为肯定是10……),而10FRA ∈(1,10),所以最高位数可能取值∈(1,10),相应是10FRA的整数部分。
故只需先将N变形为10log10(N),接着取log10(N)的小数部分 FRA = log10(N)-[log10(N)](向下取整),最后取 [10FRA] = [10log10(N)-[log10(N)]](向下取整)即可。
1、求N^N的最高位
Step1: 变形为 10log10(N^N)=10N*log10(N);
Step2: 取N*log10(N)的小数部分FRA=N*log10(N)-[N*log10(N)];
Step3: 取[10FRA] =[10N*log10(N)-[N*log10(N)]]即可。
2、求N!的最高位
Step1: 变形为 log10(N!)=log10(1*2*3…*N)=log10(1)+log10(2)+…+log10(N)
注意: 这里可以运用stirling公式进行化简:最后得到 log10(N!) = log10(sqrt(2 * pi * N)) + N * log10(N / e).
stirling原始公式:lim(n→∞) ((n/e)^n)*√(2πn) / n! = 1,也就是说当n很大的时候,n!与√(2πn) * (n/e) ^ n的值十分接近。 变形后有:n!= ((n/e)^n)*√(2πn) [lim(n→∞)] (这个表示很不正规,呵呵,但是能明白就好~)。
但是stirling公式往往对于n很大的时候结果越精确,所以在不影响时间效率以及不会溢出(int的话貌似是小于12?)的情况下,对于n比较小的情况下可采取直接求n!的做法。
得到: log10(N!)= log10(√(2πN) * (N/e) ^ N)
就有10log10(N!)=10log10(√(2πN) * (N/e) ^ N)
Step2: 取log10(N!)的小数部分FRA=log10(N!)-[log10(N!)] = log10(√(2πN) * (N/e) ^ N) - [log10(√(2πN) * (N/e) ^ N)];
Step3: 取[10FRA] =[10log10(√(2πN) * (N/e) ^ N) - [log10(√(2πN) * (N/e) ^ N)] ]。
二、求整数的位数
log10(1) = 0
log10(10) = 1
log10(100) = 2
log10(1000) = 3
…… …… …… …… …… ……
由此可见数N的位数应该是[log10(N)]+1(向下取整) 或者 [log10(N)+1](向下取整)。
此外说明下个人的看法:看到过一些说法认为整数的位数应该是[log10(N)]向上取整,但是这种说法显然没有考虑到N为10的倍数的情况,例如N=10,[log10(10)]=1,(此时log10(10)=1为整数,向上取整,向下取整结果都一样。)但是10应该是两位数。
现在看来,求位数的情况现在看来是不是应该要比求最高位的简单呢?
1、求N^N的位数
直接套用公式:[log10(N^N)]+1 = [N*log10(N)]+1
2、求N!的位数
直接套用公式:[log10(N!)]+1 = [log10(√(2πN) * (N/e) ^ N)]+1
FOJ的这道题目正是基于这个知识点下展开的,当时就是参考这个博客才开始系统性学习这方面的只是的~(弱菜一株啊,没办法 = =)
http://blog.csdn.net/linbbq/article/details/6316529
三、求整数的最低位
这个问题显然是针对于N^N的问题展开的,如果是N!,那出题人显然是脑残,或者当我们是脑残吗?(1!=1, 2!=2, 3!=6, 4!=24, 5!=120……之后的N!的个位数都是0了~) 针对N!的问题应该问的是尾数0的个数的多少这类问题比较常见。
曾经在某位大牛的博客上看到过很雷人的代码,虽然觉得郁闷,但是现在想来这可能可以归结到寻找循环节的方法中吧!
思路如下(这种方法类似于找规律):
0*0=0 …… 循环节为1
1*1=1 …… 循环节为1
2*2=4 4*2=8 8*2=16 6*2=12 …… 循环节为4
3*3=9 9*3=27 7*3=21 1*3=3 …… 循环节为4
4*4=16 6*4=24 …… 循环节为2
5*5=25 …… 循环节为1
6*6=36 …… 循环节为1
7*7=49 9*7=63 3*7=21 1*7=7 …… 循环节为4
8*8=64 4*8=32 2*8=16 6*8=48 …… 循环节为4
9*9=81 1*9=9 …… 循环节为2
由此可见,对于循环节为1的数直接输出这个数的个位数即可;而对于循环节为2的数,我们寻找规律发现如果输入的数N为偶数,则输出第一个尾数,反之,为奇数,则输出第二个尾数;对于循环节为4的数,我们只需对数N模4取相应的数即可,这里需要注意的是N%4==0:取第3个的尾数,N%4==1:取第四个的尾数,N%4==2,取第一个的尾数,N%4==3,取第二个的尾数。
于是一个不错的打表的方法诞生了,参考自http://cs1003.blog.163.com/blog/static/20409623620123301162120/:
last[10][4]={{0,0,0,0},{1,1,1,1},{6,2,4,8},{1,3,9,7},{6,4,6,4},{5,5,5,5},{6,6,6,6},{1,7,9,3},{6,8,4,2},{1,9,1,9}}
浙公网安备 33010602011771号