问题1:阶乘结尾0的个数
要想阶乘的结尾是0,那么这个0已经通过5的倍数乘以另一个数获得,例如:5,10,15,20,25...,同时可以注意到这样一个规律,5,10,15,20,30,35,40与某个数相乘后可以贡献1个0,而25,50,75,100...与某个数相乘可以贡献2个0,5^3, 2* 5^3, 3*5^3与某个数相乘时贡献了3个0,依次类推。同时我们注意到,在统计5,10,15,20,25...时已经统计了一次25,50,75, 100,5^3, 2* 5^3, 3* 5^3一次了,而在统计25、50、75...时也统计了一次5^3, 2* 5^3...一次了,这样最终0的个数就是计算5的倍数出现了多少次+ 25的倍数出现了多少次 + 5^3出现了多少次,知道5^x > N,据此分析程序如下,如NumberOfZero1所示,
#include <stdio.h>
int NumberOfZero(int var) {
int num = 0;
int j;
for (int i = 1; i <= var; ++i) {
j = i;
while (j % 5 == 0) {
num++;
j /= 5;
}
}
return num;
}
int NumberOfZero1(int var) {
int num = 0;
int base = 1;
int i = 1;
while (true) {
base = base * 5;
if (base > var) {
break;
}
i = 1;
while (base * i <= var) {
num++;
i++;
}
}
return num;
}
int main(int argc, char** argv) {
printf("method1: num = %d\n", NumberOfZero(14000));
printf("method2: num = %d\n", NumberOfZero1(14000));
}
NumberOfZero是编程之美中给出了另一种求5、25、 5^3、个数的另一中求法,更加简洁一些。
问题2:二进制表示中最后一个1所在的位置
根据二进制数的性质,如果最后一位是1,那么该数为奇数,因为如果结尾为0,那么这个二进制数一定为偶数,因为结尾为0的数可以看作某个结尾为1的数,左移1位得到,即乘2后得到的数,因此一定是偶数,那么这个偶数再加1后,一定为奇数。
相似的分析,如果倒数第2位为1一定是2的倍数
如果倒数第3位是1一定是4的倍数
...
如果与问题1类比,如果N!有多少个2的倍数,相当于左移了多少位,相当于问题1的有多少个0,有多少个4的倍数,相当多少个左移2位,相当于问题1的贡献2个0...
同时注意到除了1!外,没有一个数的阶层是奇数,因为2以上的阶乘至少含有一个2这个因子。
因此问题2其实和问题1是相同的问题。
程序如下:
#include <stdio.h>
int PositionOf1(int var) {
int pos = 0;
while (var) {
var>>=1;
pos += var;
}
return pos;
}
int PositionOf1_1(int var) {
int pos = 0;
int base = 1;
int i = 0;
while (true) {
base *=2;
if (base > var) {
break;
}
i = 1;
while (base * i <= var) {
pos++;
i++;
}
}
return pos;
}
int main(int argc, char** argv) {
printf("method1: num = %d\n", PositionOf1_1(14));
printf("method2: num = %d\n", PositionOf1(14));
}
参考文献
编程之美p125 2.2