九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

当n非常非常大时(比如为1万),求n!问题的学习,解答和疑问。

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。

首先说明,这本来是钱能C++程序设计习题及解答中的一个习题。也就是在自然数范围内求n!,其实用他的方法还不能说在自然树范围内,按他的方法,不考虑机子性能,最多可以算到2^32-1(32位机),原题解答如下:

程序1:

#include <iostream>  
#include <iomanip>  
#include <cmath>  
#include <cstdlib>  
using namespace std;  
//--------------------------------------------------------------------  
int getN();  
int getBitNum(int n);  
char *init(int size);  
void calc(char *a,int n);  
void display(char *a,int size);  
//--------------------------------------------------------------------  
int main()  
{  
 int n = getN();  
 int size = getBitNum(n);  
 char *pa = init(size);  
 calc(pa,n);  
 display(pa,size);  
 delete []pa;  
 return 0;  
}  
//----------------------------------------------------------------------  
int getN()  
{  
 int n;  
 cout <<"请输入n!中的n:";  
 cin >>n;  
 while(n<0)  
 {  
  cout <<"输入有错,请重输:";  
  cin >>n;  
 }  
 if(n == 0)  
  exit(1);  
 return n;  
}  
//--------------------------------------------------------------------  
int getBitNum(int n)  
{  
 double sum = 1.0;  
 for(int i = 1;i <= n;++i)  
  sum += log10(double(i));  
 return int(sum);  
}  
//--------------------------------------------------------------------  
char *init(int size)  
{  
 char *pa = new char[size];  
 if(!pa)  
 {  
  cout<<"Too large factor of"<<size<<endl;  
  exit(1);  
 }  
 pa[0] = 1;  
 for(int i = 1;i <size;++i)  
  pa[i] = 0;  
 return pa;  
}  
//--------------------------------------------------------------------  
void calc(char *a,int n)  
{  
 double bitcount = 1;  
 int begin = 0;  
 for(int i = 2;i <= n;++i)  
 {  
  long and = 0;  
  bitcount += log10(double(i));  
  if(a[begin] == 0)  
   ++begin;  
  for(int j = begin;j < int(bitcount); ++j)  
  {  
   and += i * a[j];  
   a[j] = char(and % 10);  
   and /= 10;  
  }  
 }  
}  
//--------------------------------------------------------------------  
void display(char *a,int size)  
{  
 int bit = 0;  
 for(int i = size - 1;i >= 0; --i)  
 {  
  if(bit % 50 == 0)  
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";  
  cout <<(int)a[i];  
  ++bit;  
 }  
 cout <<endl;  
}  
//--------------------------------------------------------------------

程序的运行效率和结构化设计都很不错,这里赞一句清华大学钱能教授的C语言编程水平,注意,仅仅是佩服他的C语言编程水平而已,这根本就不能算是一个好的C++程序,虽然他是在一个C++教程里面给出来的。我把自己按他的算法重新编写了代码,没有他那么好看了,但是因为int明显是正的,所以用了unsigned类型的size_t,这样就让n可接受的范围到了2^32-1。

程序2:

#include <iostream>  
#include <iomanip>  
#include <cmath>  
#include <cstdlib>  
using namespace std;

size_t getbit(const size_t &aN);  
void init(unsigned char *p,size_t &am);  
void compute(unsigned char *p,const size_t &an);  
void print(unsigned char *p,const size_t &am);  
int main()  
{  
 cout <<"Input the /"n/" you want to compute:";  
 size_t n;  
 cin >> n;  
 if(n == 0)           //检验输入  
 {  
  cerr<<"Please input a integer more than zero."<<endl;  
 }  
 size_t m = getbit(n);               
 unsigned char *pn = new unsigned char[m];  
 if(!pn)            //检验空间分配  
 {  
  cerr<<"The factor is too big."<<endl;  
 }  
 init(pn,m);        //初始化  
 compute(pn,n);   //计算n!  
 print(pn,m);         //输出  
 delete []pn;  
 return 0;  
}  
//-----------------------------------------------------------  
size_t getbit(const size_t &aN)  
{  
 double sum = 1.0;  
 for(size_t i = 1;i<=aN;++i)  
 {  
  sum+=log10(double(i));  
 }  
 return size_t(sum);  
}  
//------------------------------------------------------------  
void init(unsigned char *p,size_t &am)  
{  
 p[0]=1;  
 for(size_t i = 1;i != am;++i)  
 {  
  p[i] = 0;  
 }  
}  
//------------------------------------------------------------  
void compute(unsigned char *p,const size_t &an)  
{  
 double bitcount = 1.1;  
 size_t begin = 0;  
 for(size_t i = 2;i<=an;++i)  
 {  
  size_t and = 0;  
  bitcount +=log10(double(i));  
  if(p[begin]==0)  
   ++begin;  
  for(size_t j = begin ; j < size_t(bitcount);++j)  
  {  
   and += i * p[j];  
   p[j] = unsigned char(and % 10);  
   and /= 10;  
  }  
 }  
}  
//----------------------------------------------------------------  
void print(unsigned char *p,const size_t &am)  
{  
  size_t bit = 0;  
 for(size_t i = am;i != 0;--i)  
 {  
  if(bit % 50 == 0)  
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";  
  cout << size_t(p[i-1]);  
  ++bit;  
 }  
 cout<<endl;  
}

我这里几乎都继承了钱能的算法,哪怕结构都差不多,仅仅是改变成了unsigned,可是竟然有问题,在运行时,当n比较小时(如50),有的时候会出错,大的时候反而不会(比如1000,2000),不知道是为什么。为了方便以后的使用,并体现一点C++的精神,我把这个程序作成一个类,就变成下面这样.

程序3:

Factor.h:

#pragma once  
typedef  unsigned char unchar;  
class Factor  
{  
size_t n;             //求n的n!  
size_t m;             //n!有多少位  
unchar *pc;       //动态分配的数组指针  
private:  
 void getbit();  
 void compute();   //计算n!  
 void init();  
 Factor(Factor &);  
public:  
 void print()const;         //输出  
 Factor(const size_t &);  
 ~Factor(void);  
   
};

Factor.cpp

#include "Factor.h"  
#include <iostream>  
#include <cmath>  
#include <iomanip>  
using namespace std;

Factor::Factor(const size_t &nval)  
{  
 if(nval == 0)           //检验输入  
 {  
 cerr<<"Please input a integer more than zero."<<endl;  
 }  
 n = nval;  
 init();  
}  
Factor::~Factor()  
{  
 delete []pc;  
}  
void Factor::init()  
{  
 getbit();               
 pc = new unchar[m];  
 if(!pc)            //检验空间分配  
 {  
  cerr<<"The factor is too big."<<endl;  
 }  
 pc[0]=1;  
 for(size_t i = 1;i < m;++i)  
 {  
  pc[i] = 0;  
 }  
 compute();  
}

void Factor::getbit()  
{  
 double sum = 1.0;  
 for(size_t i = 1;i<=n;++i)  
 {  
  sum+=log10(double(i));  
 }  
 m = size_t(sum);  
}  
//------------------------------------------------------------  
void Factor::compute()  
{  
 double bitcount = 1.1;  
 size_t begin = 0;  
 for(size_t i = 2;i<=n;++i)  
 {  
  size_t and = 0;  
  bitcount +=log10(double(i));  
  if(pc[begin]==0)  
   ++begin;  
  for(size_t j = begin ; j < size_t(bitcount);++j)  
  {  
   and += i * pc[j];  
   pc[j] = unchar(and % 10);  
   and /= 10;  
  }  
 }  
}  
//----------------------------------------------------------------  
void Factor::print()const  
{  
  size_t bit = 0;  
 for(size_t i = m;i != 0;--i)  
 {  
  if(bit % 50 == 0)  
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";  
  cout << size_t(pc[i-1]);  
  ++bit;  
 }  
 cout<<endl;  
}

main.cpp

#include "Factor.h"  
#include <iostream>  
using namespace std;  
int main()  
{  
 size_t anum;  
 cout<<"Input the number you want to compute:";  
 cin >>anum;  
 Factor a(anum);  
 cout<<"the "<<anum<<"! is:"<<endl;  
 a.print();  
 return 0;  
}

主程序仅仅是用来测试,这个类可以比较好的计算n!,问题是还是有些类似程序2的问题,我也是很困惑,难道是因为用了无符号的问题?因为这2个程序和原题答案这是个最大的区别,其他几乎一样。然后我为了用上C++的STL特性,编写了如下代码:

程序4:

Factor.h:

#pragma once  
#include <vector>  
class Factor  
{  
size_t n;             //求n的n!  
std::vector<size_t> vec;       //储存结果的vector  
private:  
 void compute();   //计算n!  
 void init();  
 Factor(Factor &);  
public:  
 void print();         //输出  
 Factor(const size_t &);  
 ~Factor(void);  
};

Factor.cpp

#include "Factor.h"  
#include <iostream>  
#include <cmath>  
#include <cmath>  
#include <iomanip>  
using namespace std;  
typedef vector<size_t>::iterator iter;

Factor::Factor(const size_t &nval)  
{  
 if(nval == 0)           //检验输入  
 {  
 cerr<<"Please input a integer more than zero."<<endl;  
 }  
 n = nval;  
 compute();  
}  
Factor::~Factor()  
{  
}  
//------------------------------------------------------------  
void Factor::compute()            //这个最主要的函数有问题  
{  
 vec.push_back(1);  
 for(size_t i = 2;i<=n;++i)  
 {  
  size_t and = 0;  
  for(iter it = vec.begin();it != vec.end();)      //每次都计算了end(),但是还是出错,不知道为什么  
  {  
   and += i * (*it);  
   *(it++) = and % 10;   
   if(and /= 10)  
    if(it == vec.end())  
    {  
     vec.push_back(0);         //知道这个操作会改变容器  
     continue;  
    }  
  }  
 }  
}  
//----------------------------------------------------------------  
void Factor::print()  
{  
  size_t bit = 0;  
 for(iter it = vec.end();it != vec.begin();--it)  
 {  
  if(bit % 50 == 0)  
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";  
  cout << size_t( *(it-1) );  
  ++bit;  
 }  
 cout<<endl;  
}

这个程序利用了vector容器的动态添加,不需要一次添加那么多,不过感觉执行效率也许并比不上一次添加那么多,不过程序的大部分理解起来要更简单了,比如不需要提前用对数算好需要多少空间,这可以减轻很多负担,特别是根本不知道怎么算的时候,但是这样做付出了代价,就是主运算函数明显更加复杂,因为你还要在循环里面动态处理vector,我就是在这个里面出了问题,请高人给我一些提示。

分类:  C++ 
标签:  C++ 

Posted By 九天雁翎 at 九天雁翎的博客 on 2007年05月03日

前一篇: 别让习惯害了你,注意size_t变量作循环条件时,逆序是不可行的! 后一篇: 浅谈C++类(8)--重载输入输出操作符