zoukankan      html  css  js  c++  java
  • 大数阶乘题解

    题目来源:校软件班第二周作业第一题

    参考:阶乘,递归函数,C语言常见数据类型范围,算术溢出,高精度算法

    阶乘:

    一个正整数的阶乘(factorial)是所有小于及等于该数的正整数,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。
    亦即n!=1×2×3×...×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n。
     
    递归函数:
    一个函数可以调用其他函数,而当它调用自己的时候,就称其为递归(注意:递归函数必须要有终止条件)
     
    C语言常见数据类型范围:
    signed 和 unsigned 分别对应 有符文  和   无符号  
    字节数:  用sizeof()来求,例如 sizeof(int)==4
    位数 即bit数  : 一个字节是8位,所以 sizeof(int)的位数是32位
    取值范围:  2进制下的最大取值 转换成的 10进制数
          例如:sizeof(int)的位数是32位,二进制下除最高位符号位为0(表示正数),剩下31位取1,相加得+2147483647
          注意:剩下31位下标是0-30,最高位是2^30。
          
    算术溢出:算术溢出(arithmetic overflow)是指计算机进行算术运算产生的结果超出机器所能表示的范围。
     
    高精度算法:计算机对于超大数据的一种模拟加,减,乘,除,乘方阶乘,开方等运算。---重点是模拟!
          模拟谁呢?。。当然是模拟人啊,你想想人是怎么计算加减乘除的。
     
    一种寻常的思路是直接递归:
    #include<stdio.h>
    int fac(int n){
        if(n<=1) return 1;
        return n*fac(n-1);
    }
    int main(){
        int n;
        scanf("%d",&n);
        printf("%d",fac(n));
        return 0;
    }

    但会出现一个问题,在20!的时候,出现了负数?(算术溢出)

    事实上,在第13!的时候,int就不够储存了,int的最大值是+2147483647

    13!正确的答案是  6 227 020 800

    而这段代码的错误答案是:

    一种简单的解决方式是,是用取值范围比int还大的数据类型来储存

    比如:long long ,double,long double  etc....

    例如 long long

    但是,在21!的时候。。。

    这算是基本满足了题目的要求吧,但对于进阶的数据范围200<n<1000,还是远远不够,

    也许你想到了用其他类型,比如最大的long double,4932位

    但是。。mingw使用windows的C运行库,里面的printf不支持long double的输出(你可以在linux环境下试试)

    也许你试了试就会发现,看似4932位计算的如此之多,但long double的有效位只有18-19位,所以后面的4000多位,全部是0。

    那怎么办呢??

    用模拟的思想,

    我们人是怎么进行加减乘除运算的?。

    相信聪明的你已经想到了幼儿园老师交的办法

    竖式计算:

    指在计算过程中列一道竖式计算,使计算简便。加法计算时相同数位对齐,若和超过10,则向前进1。

    减法计算时相同数位对齐,若不够减,则向前一位借1当10。

    很简单易懂的思想对吧,那怎么实现呢?,如何把每一位数都存起来呢?

    当然是用数组啦。。。。

     1 #include<stdio.h>
     2 const int N=10000;// 定义一个不可改变的常数N 
     3 int a[N];
     4 int main(){
     5     int n;
     6     scanf("%d",&n);
     7     int s=0;
     8     a[0]=1;10     for(int i=1;i<=n;i++){
    11         int t=0;//进位
    12         for(int j=0;j<N;j++){//模拟计算
    13             s=a[j]*i+t;//每一位乘以i,再加上t 
    14             a[j]=s%10;//余数 
    15             t=s/10;
    16         }
    17     }
    18     int k=N-1;
    19     while(a[k]==0) k--;//删除前导0 
    20     for(int i=k;i>-1;i--) printf("%d",a[i]);
    21     printf("
    %d
    ",k+1);//计算位数 
    22     return 0;
    23 }

    这是定义了10000位,假如需要计算更大的数,可以改变N的值。

  • 相关阅读:
    [转载]NSDistributedNotificationCenter
    protobuf在IOS中的使用
    mac 下大dmg文件分割成多部分
    IOS 后台任务、多任务的理解2
    越狱iPhone、ipad通过网页安装ipa
    Android中的几种多线程实现
    Android多线程-AsyncTask异步任务
    2----IOS、Android数据存储方式
    Andriod 页面跳转传值
    svn 分支创建 合并
  • 原文地址:https://www.cnblogs.com/lukelmouse/p/9858780.html
Copyright © 2011-2022 走看看