~$ 存档

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  514 随笔 :: 26 文章 :: 15 评论 :: 62万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

阶乘: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;
}
复制代码

————————————————
版权声明:本文为博客园博主「天涯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
posted on   LuoTian  阅读(2024)  评论(2编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示