zoukankan      html  css  js  c++  java
  • 高精度阶乘的运算

    阶乘:1x2x3x4.....N,仿照2的N次方的手法,只不过这次从前往后计算,得到的数从左往后,依次为个位十位百位...等等。
    例如:021,表示120

    #include <iostream>
    using namespace std;
    #define max 100
    /*保存结果的数组长度*/
    int f[max];
    int main()
    {
         int n=10; /*n表示n!,自由修改*/
         f[0] = 1;
         for(int i = 2;i <= n;i++)
         {
              int c = 0;/*进位值*/
              for(int j = 0;j < max;j++)/*整个数组乘一遍*/
                  {
                       int s = f[j] * i + c;
                       f[j] = s % 10;
                       c = s / 10;
                 }    
         }
         for(int k = max - 1;k >= 0;k--) /*扫描清除右边的0*/
         if(f[k]) break;
         for(i = k;i >= 0;i--) /*从右往左打印结果*/
         cout<<f[i];
         return 0;
    }

    这个算法的一个缺点是,每次都要把数组乘一遍,有什么办法能优化它呢?

    一、首先对阶乘结果的位数进行优化,采用的思路是:

    int f(int n)
    {
        double a=0.0;
        while(n)
        {
            a+=log10(n);
            n--;
        }
        return (int)a+1;
    }

    得到位数的好处是,不用事先定义一个大数组,而是需要多少位就分配多大的数组(堆分配),这是解决空间的问题。
    接着,再想着时间的问题,假如定义100个元素的数组,每次代码都要从数组的首位一直到末位乘以一个数,假如算到5!=120的时候,数组存储的情况是:
    0  2  1  0  0  0  0  0  0....
    现在又要这个数组每位再乘以6,则右边是一连串的0,还有必要再依次乘一遍吗?设想了几种可能,均告失败。记录一下错误的尝试:
    这种思路是,初始化时先把数组全部置为某个字符,比如'C’,而在循环相乘的判定中,如果没有进位,且下一个数组元素是标志‘C',就停止循环,但是忽略了这个标志’C'也需要参与运算,于是失败!
    现在设想,如果把已经得到结果的最末位数做一个监视哨flag,每次循环乘数只要乘到这个监视哨就结束,就可以解决这个问题!如图所示:
    flag作为监视哨,必定指向每次运算之后的结果的最末位。假如现在j又要乘以6,从第一位0开始,一直移位到监视哨。即:j<=flag。
    第二种情况,假如在监视位出现进位了,依然还要继续乘下去,于是,得到另一个条件:f>0,只要有进位也要继续往下乘。
    第三种情况:假如现在结果已经是0  2  7,(720),再乘以7之后,得到:
    0  4  0  5,由于flag现在还指向第三位,所以要把位置提到末位,也就是说,每次循环完成之后,还要提升监视哨的位置.
    到此,问题算是解决了!
    这个模型相当于,如何在数组的循环运算中截断到某个位置!(主要判定监视哨的位置和中止的条件

    /*算法改进,效率提升很多!*/
    #include <iostream>
    using namespace std;
    #define max 30000
    int a[max];
    int main()
    {
        int n,x;
        cin>>n;
        a[0]=1;
    
        for(int i=2,flag=0;i<=n;i++)/*监视哨初始指向数组首位*/
        {
            for(int f=0,j=0;j<=flag || f>0;j++)/*j未到监视哨,或者有进位*/
            {
                x=a[j]*i+f;
                f=x/10;/*进位*/
                a[j]=x%10;
            }
            while(a[j]==0)/*提升监视哨的位置,j有两种可能的位置*/
            j--;
            flag=j;
        }
        while(flag>=0) /*输出结果*/
        {        
        cout<<a[flag];
        flag--;
        }    
        return 0;
    }

  • 相关阅读:
    初始面向对象
    python之路——迭代器和生成器
    函数进阶
    python之路——初识函数
    实现css两端对齐
    http
    background-size
    call和apply的区别
    js兼容性
    面试题
  • 原文地址:https://www.cnblogs.com/tinaluo/p/5325140.html
Copyright © 2011-2022 走看看