「解题记录」C++ 循环结构 阶乘问题

首先,这里是原题目:

写一个程序,计算N!以10进制数形式表示的数中最右的非零数字,并找出在它有边又几个零。

(程序应适用于N <= 30000的正整形数)

方案1 ——fail

那么,拿到这个题目,我首先给出了我的第一个程序,由于这个程序很快就被删掉重写,所以这里就不拿出来展示,只简单讲一讲逻辑。

首先,输入N;随后for循环计算N的阶乘;再用while循环从得数的最右边开始,寻找非零数字,并且一边找,一边记录0的个数。

这一程序对于较小的N值而言是没有问题的,比如题目中给出的示例12,由于12的阶乘仅为479001600,所以在程序运行时并不会出现问题。

但一旦N开始变大,变量fac(阶乘结果)就无法储存阶乘后的值了,所以,我开始思考新的解决方法。

方案2——fail

阶乘末尾零的个数是可以通过计算得出的,所以,我可以用这样的方式在程序开头计算出末尾零的个数:

int ..., five = 1;
while(pow(5, five) <= n){
    zeros += n / int(pow(5, five));
    five++;
}

由于阶乘结果中,5的因子必然比2少,所以这里不需要考虑2,可以直接计算因子5的个数,便能知道末尾零的个数。

随后,知道了末尾0的个数,我们就可以知道我们要求的最末非零数字在倒数第几位了,这样,我们就能够抹掉计算过程中前面的位数,只考虑需要使用的最末几位即可。完整的程序如下:

#include<iostream>
#include<cmath>
using namespace std;
int main(){
    int n, zeros = 0, fac = 1, five = 1;
    cin >> n;
    while(pow(5, five) <= n){
        zeros += n / int(pow(5, five));
        five++;
    }
    for(int i = 2; i <= n; i++){
        fac *= i;
        fac = fac % int(pow(10, zeros + 1));
    }
    cout << fac / pow(10, zeros) << " " << zeros;
    return 0;
}

的确,这一程序缩小了fac的值,能够处理更大的N,但是,还不够大。测试表明,即使将fac的类型设为long long,当N超过30,仍会发生溢出。这离题目所要求的N <= 30000还相差太远。

大概估算一下,按照这一程序,计算30000的阶乘,fac需要储存超过7000位的数据,明显不现实。

方案3——success

当时脑子短路,所以把程序放了一下,先去干别的事情。然后,下午查了下教程,看了求n得阶乘得最后一位非零数字 - butterflier这篇blog,虽然没完全搞懂,其中的数组、函数也是现在也不能使用(尽管我会,但是老师没教,所以不能用,我会是因为我自学过,而且有python和swift的基础),不过我还是从中受到了启发。

所以,这最后的一个方案,我会在计算阶乘的过程中,删去阶乘结果中的因子2和5,这样,计算结果末尾的0就会被全部抹掉,所以,我就可以让fac永远仅储存1位的数据,计算30000便毫无问题。

#include<iostream>
using namespace std;
int main(){
    int n, zeros = 0, two = 0, fac = 1, si;
    cin >> n;
    for(int i = 2; i <= n; i++){
//      cout << i << " -> ";
        si = i;
        while(si % 2 == 0){         //首先去掉 i 中所有的2,因为2与5相乘后会在末尾增加零 
            si /= 2;
            two += 1;
//          cout << si << " -> ";
        }
        while(si % 5 == 0){         //去掉 i 中所有的5,由于因子2永远比因子5多,所以在配对的时候可以直接去掉一个2 
            si /= 5;
            two -= 1;
            zeros += 1;             //与2配对后,末尾就多一个0 
//          cout << si << " -> ";
        }
            fac *= si;
            fac %= 10;              //只需要考虑最末尾的数 
//          cout << "fac: " << fac << " two: " << two << endl;
    }
    for(int i = 1; i <= two; i++){  //将未被配对的2乘回结果 
        fac *= 2;
        fac %= 10;
    }
    cout << fac << " " << zeros;
    return 0;
}

这段代码显然比blog中的那段代码要简单很多,但是我并不清楚哪段的运行效率更高,反正不管怎么说,这道题目已经完成了。

将调试代码前的//去掉,你就能够看到程序运算的全过程。拿26举例:

26
2 -> 1 -> fac: 1 two: 1
3 -> fac: 3 two: 1
4 -> 2 -> 1 -> fac: 3 two: 3
5 -> 1 -> fac: 3 two: 2
6 -> 3 -> fac: 9 two: 3
7 -> fac: 3 two: 3
8 -> 4 -> 2 -> 1 -> fac: 3 two: 6
9 -> fac: 7 two: 6
10 -> 5 -> 1 -> fac: 7 two: 6
11 -> fac: 7 two: 6
12 -> 6 -> 3 -> fac: 1 two: 8
13 -> fac: 3 two: 8
14 -> 7 -> fac: 1 two: 9
15 -> 3 -> fac: 3 two: 8
16 -> 8 -> 4 -> 2 -> 1 -> fac: 3 two: 12
17 -> fac: 1 two: 12
18 -> 9 -> fac: 9 two: 13
19 -> fac: 1 two: 13
20 -> 10 -> 5 -> 1 -> fac: 1 two: 14
21 -> fac: 1 two: 14
22 -> 11 -> fac: 1 two: 15
23 -> fac: 3 two: 15
24 -> 12 -> 6 -> 3 -> fac: 9 two: 18
25 -> 5 -> 1 -> fac: 9 two: 16
26 -> 13 -> fac: 7 two: 17
4 6

其实说句实话,这道题目难吗?也还好,离信息学竞赛还远得很。经过这一番摸索,我得出了一个结论——我好菜[doge]。

最近真的忙死了,同学也在催着改程序,我都没时间搞,GitHub一片白色……这是部分聊天记录(惨~):

聊天记录

从我的博客查看本文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335