大数阶乘算法实现及优化

大数阶乘算法实现及优化题目:求N!TimeLimit:10000/5000MS(Java/Others)MemoryLimit:65536/32768K(Java/Others)TotalSubmission(s):63958AcceptedSubmission(s):18171ProblemDescription:Givenaninteger

大家好,又见面了,我是你们的朋友全栈君。

                         题目:求N!  

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K
(Java/Others) Total Submission(s): 63958 Accepted Submission(s):
18171

Problem Description:
Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N! Input One N in one line, process to the end of file. Output For each N, output N! in one line.

Sample Input
1
2
3

Sample Output
1
2
6

解决思路:
由于题目要求计算的范围为10000以内,为了符合题目要求我们先分析10000!有多大,根据公式N!的位数=[lg(1)+lg(2)+…..log(N)]+1([]表示向上取整)可知,10000!大概37000多位,所以可以用40000个元素的int数组res保存.。为了方便起见,res[0]保存结果的个位,res[1]保存百位………(为什么要逆序呢,这样方便进位)。
代码实现:

//============================================================================
// Author : Up_Junior
// Version : Vs2012
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <time.h> //clock_t使用导入头文件。
using namespace std;
const int Max=40000;   //int 数组范围
int res[Max];
int main() {
    int n;
    clock_t start, finish;  //主要保存起始时间和终止时间
    double duration;  //耗时
    while(cin>>n && n>=0 && n<=10000){
        start=clock();
        memset(res,0,sizeof(res));   //初始化申请空间,并每位置0
        res[0]=1;  //0!=1
        for(int i=1;i<=n;i++)   //1~N
             //carry为进位,初始进位为0,个位没有进位
            for(int j=0,carry=0;j<Max;j++){
                res[j]=res[j]*i+carry;
                carry=res[j]/10;
                res[j]=res[j]%10; 
        }
        int i;
        for( i=Max-1;i>=0;i--) if(res[i]) break; 
        //从后往前(高位到低位)求出不为0的一项,即结果的最高位
        for(int j=i;j>=0;j--)   //高位到低位输出
            printf(j==0?"%d\n":"%d",res[j]);
        finish=clock();
        duration=(double)(finish-start)/CLOCKS_PER_SEC;//耗时
        cout<<n<<"! 耗时"<<duration<<"s"<<endl;
    }
    return 0;
}

运行效果:
10000阶乘普通方法求解耗时:
10000阶乘
500!:
这里写图片描述
注意:

10000!的求解普通方法求解耗时是惊人的,要想AC过杭电acm1042题目必须优化,提高效率。

一次优化:

for(int i=1;i<=n;i++)   //1~N
             //carry为进位,初始进位为0,个位没有进位
            for(int j=0,carry=0;j<Max;j++){
                res[j]=res[j]*i+carry;
                carry=res[j]/10;
                res[j]=res[j]%10; 
        }

仔细观察可以发现,j的循环次数是可以减少的(j的次数是由i!的位数决定的,大家这里可以好好理解一下哈),因为这里每次j都要被执行Max次,然而我们发现N!的位数=[lg(1)+lg(2)+…..log(N)]+1([]表示向上取整),即每次j运行的次数只要满足N!的位数即可,这样可以提高效率,尤其是N很大的时候。所以用一个数组extra保存1~N的位数。废话不多说,直接上代码:

#include <iostream>
#include<math.h>
#include <time.h>
using namespace std;
const int Max=40000;
int res[Max];
double extra[10000]; //用来保存N!的位数
int main() {
    int n;
    clock_t start, finish;
    double duration;
    extra[0]=0;
    for(int i=1;i<=10000;i++)  //初始化直接求10000以内所有的位数
            extra[i]=extra[i-1]+log10(i);
    while(cin>>n && n>=0 && n<=10000){

        start=clock();
        memset(res,0,sizeof(res));  
        res[0]=1;
        for(int i=1;i<=n;i++)
        //j<=(int)extra[i]+1;这句有效减少了次数
            for(int j=0,carry=0;j<=(int)extra[i]+1;j++){
                res[j]=res[j]*i+carry;
                carry=res[j]/10;
                res[j]=res[j]%10;
        }
        int i;
        //i=(int)extra[n]+1;这里也有优化
        for( i=(int)extra[n]+1;i>=0;i--) if(res[i]) break;
        for(int j=i;j>=0;j--)
            printf(j==0?"%d\n":"%d",res[j]);
        finish=clock();
        duration=(double)(finish-start)/CLOCKS_PER_SEC;
        cout<<n<<"! 耗时"<<duration<<"s"<<endl;
    }
    return 0;
}

运行效果:
10000阶乘优化后方法求解耗时,可以看出耗时减少一半:
这里写图片描述
500!:
这里写图片描述
二次优化:

for(int i=1;i<=n;i++)
        //j<=(int)extra[i]+1;这句有效减少了次数
            for(int j=0,carry=0;j<=(int)extra[i]+1;j++){
                res[j]=res[j]*i+carry;
                carry=res[j]/10;
                res[j]=res[j]%10;
        }

继续看这段代码,这里是以10进位的,但是如果把它换成100、1000呢?
你就会发现,效率突然便高了,为什么呢?因为以前是以10进位,j会运行j<=(int)extra[i]+2次,如果以100或1000为基准呢,他会继续缩小原来的100或1000倍。这里测试取基准100000效果最佳,因为超过100000会出错。这样 次数 count=j<=(int)extra[i]+1/5;,为什么会是除以5呢,因为数组的一个元素可以保存5位,超过五位就进位。仔细想想,是不是还要取一次余是吧,因为要是除不尽5呢?其实这里已经包含了。废话不多说上代码:

#include <iostream>
#include<math.h>
#include<time.h>
using namespace std;
const int Max=8000;//猜猜为什么是8000?
int res[Max];
double extra[10000];
int main() {
    int n;
    clock_t start, finish;
    double duration;
    extra[0]=0; 
    for(int i=1;i<=10000;i++)
        extra[i]=extra[i-1]+log10(i);
    while(cin>>n && n>=0 && n<=10000){  
        start=clock();
        memset(res,0,sizeof(res));  
        res[0]=1;
        for(int i=1;i<=n;i++)
        {
            int x=(int)extra[i]+1; 
            for(int j=0,carry=0;j<=x/5;j++){
  
  //大大缩减j运行的次数
                res[j]=res[j]*i+carry;
                carry=res[j]/100000;
                res[j]%=100000;
            }
        }
        int i=(int)extra[n]+1;
        for(i=i/5;i>=0;i--) if(res[i]) break; //这里i/5也很关键哦
        printf("%d",res[i--]); //为什么因为高位有可能不足5位,直接输出
        for(int j=i;j>=0;j--)
            printf("%05d",res[j]);
/*"%05d" 为什么会是他呢?而不是"%d"?????因为如果结果为102000各位取余后res[0]=2000;res[1]=1,输出的时候直接为12000,会出现错误,这里你就明白了吧。*/
        cout<<endl;
        finish=clock();
        duration=(double)(finish-start)/CLOCKS_PER_SEC;
        cout<<duration<<"s"<<endl;  
}
    return 0;
}

运行效果:
是不是发现效率又高了很多???!!!!
这里写图片描述
500!:
这里写图片描述

杭电AC代码:

#include <iostream>
#include<math.h>
using namespace std;
const int Max=8000;
int res[Max];
double extra[10000];
int main() {
    int n;
    extra[0]=0; 
    for(int i=1;i<=10000;i++)
        extra[i]=extra[i-1]+log10(i);
    while(cin>>n && n>=0 && n<=10000){  
        memset(res,0,sizeof(res));  
        res[0]=1;
        for(int i=1;i<=n;i++)
        {
            int x=(int)extra[i]+1; 
            for(int j=0,carry=0;j<=x/5;j++){
                res[j]=res[j]*i+carry;
                carry=res[j]/100000;
                res[j]%=100000;
            }
        }
        int i=(int)extra[n]+1;
        for(i=i/5;i>=0;i--) if(res[i]) break;
        printf("%d",res[i--]); 
        for(int j=i;j>=0;j--)
            printf("%05d",res[j]);
        cout<<endl;
    }
    return 0;
}

另附杭电AC截图:

这里写图片描述

从上依此往下是第二次优化,网上其他博客代码运行,第一次优化。
这里是其中代码博客网址:
http://blog.csdn.net/lishuhuakai/article/details/8077688
这里就算是结束了,是不是觉得算法很有趣呢,我只是刚刚学了一点点,这里就班门弄斧了哈,如果有错误还请大家指出,第一次写算法博客,如果觉得对你有帮助的话还请评论下哈,谢谢。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/164019.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 网易视频 java_合并网易视频中英文字幕文件,解决Java输入输出的中文乱码问题…「建议收藏」

    网易视频 java_合并网易视频中英文字幕文件,解决Java输入输出的中文乱码问题…「建议收藏」packagehebingsrt;importjava.io.BufferedReader;importjava.io.BufferedWriter;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.FileReader;importjava.i…

    2022年7月11日
    19
  • 快速搭建一个自己的服务器详解(java环境)「建议收藏」

    快速搭建一个自己的服务器详解(java环境)「建议收藏」一.服务器的购买1.我选择的是阿里云的服务器,学生价9.5元一个月,百度直接搜索阿里云,然后点击右上角登录,推荐大家用支付宝扫码登录,方便快捷。阿里云官网的东西比较多,登录后我找了很久也没有找到学生服务器在哪里卖,最后在咨询里找到了这个网址,https://promotion.aliyun.com/ntms/campus2017.html,购买的时候需要进行学生认证,按照他的要求一步步…

    2022年6月11日
    25
  • 串口通信-MSComm控件使用详解

    串口通信-MSComm控件使用详解MSComm控件通过串行端口传输和接收数据,为应用程序提供串行通讯功能。MSComm控件在串口编程时非常方便,程序员不必去花时间去了解较为复杂的API函数,而且在VC、VB、Delphi等语言中均可使用。 MicrosoftCommunicationsControl(以下简称MSComm)是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件,它为应用程序提供了通…

    2025年6月26日
    2
  • 使用SQL语句创建表_用sql语句创建员工表

    使用SQL语句创建表_用sql语句创建员工表1.创建表的语法createtable表名(列1数据类型1,列2数据类型)tablespace表空间SQL:createtablestudent(IDNUMBERnotnull,NAMEVARCHAR2(20));表已创建…

    2022年10月16日
    4
  • RAID技术全解图解-RAID0、RAID1、RAID5、RAID100

    图文并茂RAID技术全解–RAID0、RAID1、RAID5、RAID100……  RAID技术相信大家都有接触过,尤其是服务器运维人员,RAID概念很多,有时候会概念混淆。这篇文章为网络转载,写得相当不错,它对RAID技术的概念特征、基本原理、关键技术、各种等级和发展现状进行了全面的阐述,并为用户如何进行应用选择提供了基本原则,对于初学者应该有很大的帮助。一、RAID概…

    2022年4月7日
    147
  • 用c语言实现顺序表_顺序表代码讲解以及实现

    用c语言实现顺序表_顺序表代码讲解以及实现一、学习内容:1、创建顺序表2、按数值查找3、按位置查找4、插入一个数值5、删除一个数值6、销毁顺序表7、求前驱算法8、求后继算法

    2025年6月3日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号