代码:500)this.width=500'>BigDate.rar
上次一位兄弟向我提出了1000的阶乘如何求得。其实这里的难点就在于结果如何保存,以及一个数值如何 与很大的数值相乘。
实现原理:
处理的方法可能有很多种,我下面便是一种平时用笔做乘法的做法。大家先看看这个图:
500)this.width=500'>
在程序中我定义一个足够大的unsigned int数组,然后数组中的每一个单元存放0-999,999之间的数(可 大可小,但不能太大,后面会解释),实际上我们形成了一个1,000,000进制的算法。每个单元大于999,999 时,向前进位。然后我们一单元一单元地相乘,直到被乘值的每一单元乘到为止。这样一个数乘以大数就完 成了。最低位在数组的起始地址中,然后依次排向最高位。
显示我们的结果时,我们可采用精确显示,也可使用科学计数法显示。精确显示时,将从最高单元到 最低单元一个个显示,每一位转化6个十进制字符(最高位除外),不足补0,这样结果就以十进制显示出来 了。
主要代码:(以下在VC6.0中通过调试。代码从附件截取)
计算阶乘并显示结果{... unsigned int buf[500]; int lenth=CalculateFactorial(date, buf);
//结果作字符串处理 //最高位 char tmp[10]; ::sprintf(tmp, "%d", *(buf+lenth-1)); this->m_strResult = tmp; //其余位 for ( int i=lenth-2; i>=0; --i ) { ::sprintf(tmp, "%.6d", *(buf+i)); this->m_strResult += tmp; }
//结果作科学计算法处理 this->m_strRecult2 = this->m_strResult.GetAt(0); this->m_strRecult2 +=’.’; if ( this->m_strResult.GetLength()==1 ) this->m_strRecult2 += ’0’; else this->m_strRecult2 += this->m_strResult.Mid(1, 15); this->m_strRecult2 += "e+"; ::sprintf(tmp, "%d", this->m_strResult.GetLength()-1); this->m_strRecult2 += tmp; ...}
计算阶乘,结果存在Buf中,低位在数组的启始点int CBigDateDlg::CalculateFactorial(int date, unsigned int *Buf){ int carry=0; //进位值 unsigned int *pTop=Buf; //当前值的最高位 unsigned int *p=NULL; int i; const int eachValue=1000000; //进制,也就是指缓冲区中每个单达到此值就溢出 *Buf = 1; //阶乘的每个数 for ( i=2; i<=date; ++i ) { //每一个数将乘上原有的结果 for ( p=Buf; p<=pTop; ++p ) { *p = *p*i+carry; if ( *p>=eachValue )//溢出测向前进位 { carry = *p/eachValue; *p %= eachValue; } else //否则进位值为0 { carry=0; } } if ( carry>0 ) //跟结果相乘后,整个数有溢出 { ++pTop; *pTop = carry; carry=0; } } return pTop-Buf+1; //有多少个单元}
注意点:
1、我们可以定义为unsigned char数组,然后形成10进制或100进制。我之所以用1,000,000进制是为了加 快处理速度,这样不存在巨大的进位操作。2、对结果位数的估计,以便缓冲区空间大小的定义。我们知 道一个数乘以一个X位的数,最多增加X位,那么我们可粗略估计1000!=1*10+2*90+3*900=2890位,然后我 们数组中每一单元存放6位,则得出我们需要定义的空间大小=2890/6+1=482;3、我们要注意数值的溢出 ,上面我提到进制不能太大,主要是防止乘法运算时产生溢出。因为unsigned int型最大能表示 2^32=4,292,967,295, 对现在的算法来说,即最大可达1,000,000,000进制,但由于你得保证乘数与被乘数不 溢出,所以得减小,现在我们用的1,000,000进制,则乘数可达3位数(1,000也可包含在内),当我们的乘数再 增大时,那我们就得减小进制。4、此题没有考虑乘数很大,因为在这里最大时都只有1000。如果很大时 ,我们就得将乘数分开来做。跟我们小学学的两位数和多位数乘法差不多。5、在这里实际上是建立在原 始的10进制的基础上,缓冲区真的值只能我们自己解释,所以你简单地显示16制是错误的,要显示16进制则 需我们按照10->16进制的做法,再进行运算后得出。
所附代码实现了0-1000内任一数的阶乘的运算。并将结果以精确值和科学计数法进行显示。粗 步测试跟计算器的运算结果没有出入。运行图如下:
500)this.width=500'>
如果大家有更好、更快的方法请指教!
代码:
|