本文给出一个使用Fortran语言的计算大数阶乘的程序,该程序可以计算出1-21万之间的数的阶乘。Fortran表示公式翻译语言,是最古老的高级语言,主要用途是科学计算,曾经很流行,但现在用的少了,他的大部分市场被C语言取代。在编写本程序之前,我从未写过Fortran程序,这个程序是现学现编。为了方便大家学习,我给出这个程序的一些注释。
1. Fortran语言的一些语法特点,主要和C语言比。
1.1 运算符主要有+,-,*,/和**,分别表示加,减,乘,除和乘方,他没有求余数的运算符,求余数使用函数MOD来做。
1.2 数组的访问形式是array(i),他使用圆括号,而c语言使用方括号。
1.3 数组可以定义下标的范围,下标可以为负数,默认的下标从1开始。而c语言的下标总是从0开始。
1.4 变量定义的特点,INTEGER类似于c中的int, 变量定义要写成“变量类型::变量”的形式,不可改变的变量的定义形式为“变量类型,PARAMETER::变量名=”,动态数组
申明的格式为“变量类型,DIMENSION(:),ALLOCATABLE::变量名”
1.5 语句后面不需要任何符号,c语言中,语句后面必须使用分号。
1.6 动态数组的分配使用ALLOCATE,释放使用DEALLOCATE,不需要使用指针。
1.7 输入使用READ,输出使用WRITE。WRITE支持格式符,在语言中,格式符中的特殊格式开始于“%”,可以在格式符中使用字符串,而在Fortran中,格式符需要用()括起来,格式符中的字符串必须使用“”,如 '(I6"!="I4.1\)')。在C语言中,输出默认是不换行,Fortran恰好相反。如果要换行,需在格式符最后加上"\". ,另外,也可以用advance="no"的语法来说明,不换行。
1.8 c是强类型语言,变量必须定义,然后才可以使用。默认的,Fortran中的变量可以不定义而使用,语句“IMPLICIT NONE”表示变量必须定义。
1.9 c的行注释是//,Fortran是 !
1.10 Fortran程序中,程序,循环块必须配对,程序以PROGRAM开始,以END PROGRAM结束。
1.11 Fortran中,关键字和变量是大小不敏感的,Fortran90推荐使用小写格式。这里则保持了大小格式。
2. 主要算法描述
2.1 阶乘的结果是一个很大的数,这里的大数用数组来表示,大数使用10000进制存储,低位在前,高位再后,和书写数序相反。buff[1]表示大数的最低4位,buff[2]表示次低4位,以此类推。
2.2 n!的有多少为10进制数。
然后计算N!万进制表示的长度。因为n!小于(n+1)/2的n次方,故首先使用函数log10,估算(n+1)/2)^n的10进制的位数,然后除以4+1得到万进制表示的位数的一个上限。n!小于(n+1)/2的n次方的证明见下:
Caes 1:n是奇数
n!可表示为连续n个数的乘积,中间的那个数mid=(n+1)/2, 除了这个数外,我们可以将1到n之间的数分成n/2组,每组的两个数为 mid-i和mid+i (i=1到mid-1),如1,2,3,4,5,6,7,中间的那个数是4,其余的3对数是(3,5),(2,6)和(1,7),容易知道,每对数的积都于小mid*mid,故n!小于mid的n次方,即 n! <((n+1)/2)^n。
Case 2: n 是个偶数,
n!可表示为连续n个数的乘积,则中间的两个数(n-1)/2和(n+1)/2, 我们将(n+1)/2记做mid,则中间的一对数是(mid-1)和mid,其它的几对数是(mid-2,mid+1),(mid-3)(mid+2)等等,故n!小于mid 的n次方,n! <((n+1)/2)^n。
2.3 具体计算过程就不说了,请看《大数阶乘之计算从入门到精通》系列中的入门篇。
3. Fortran开发环境的搭建
3.1 集成开发环境
在Windows, 我使用微软Development Studio的早期版本4.0,这个版本包含了VC4.0 和一个Fortran语言的集成开发环境Fortran PowerStation4.0,是一个32位的编译器。我下载的是http://download.csdn.net/download/nethouyi/1897519,而我主要参考的文档来自这本数,主要的参考文档则来源于http://download.csdn.net/download/goudanbaoma/2134239。在编写这个程序的过程中,遇到一些困难,最终是用百度来搜索得到的。 这个集成开发环境和VC6类似,关于在集成开发环境下编译和链接的方法,这里就不多说了。
3.2 命令行编译。编译使用 C:\MSDEV\BIN\ML32.exe,语法和微软的编译器cl.exe 差不多。倒是link.exe几乎会使人疯掉。这个链接其的版本3.00.5270,不支持在命令行输入libpath来指定库文件的路径,用这个程序编译的时候,你需要显式指定一大堆lib文件,最终,我不的不放弃这个链接器,而使用了一个更高的版本。下面是我的命令行编译过程。
3.2.1 首先运行下面的行,将编译器和链接器的路径加入到搜索路径。也可以创建批处理文件,方便以后使用。c:\masm32\bin下包括了一个版本更高的链接器,它是Microsoft (R) Incremental Linker Version 5.12.8078. C:\MSDEV\BIN目录下包含了fortran语言的编译器和链接器。因为路径 C:\masm32\bin 优先于C:\MSDEV\BIN,故如果你这行了这个批出里,当运行link的时候,他总是使用 C:\masm32\bin下的link.
set PATH=C:\masm32\bin;C:\MSDEV\BIN;%PATH%;
3.2.2 编译和链接程序命令,我的源程序为fac.f90
fl32 /c fac.f90
link fac.obj /machine:i386 /subsystem:console /libpath="c:\msdev\lib" /out:"fac.exe"
3.3 在Linux下编译,请参考我的博文在Linux安装Fortran编译器
下面是完整的源代码
PROGRAM PROG1
IMPLICIT NONE
!Calc fraction
INTEGER::I,J,N,C,PROD !C:进位,PROD:乘积
INTEGER::LEN,BUFF_LEN
INTEGER,PARAMETER::RAD=10000 !使用万进制
INTEGER,DIMENSION(:),ALLOCATABLE::BUFF
WRITE(*,'(" n!="\)') !不知道"后面的第一个字符为什么不显示,故"后跟一个空格
READ *,N BUFF_LEN=INT(REAL(N) * LOG10( (REAL(N)+1.0)/2.0)) ! n!的10进制位数的上限
BUFF_LEN=BUFF_LEN/4+1 ! n!的万进制位数的上限
ALLOCATE(BUFF(BUFF_LEN))
LEN=1
BUFF(1)=1
DO I=1,N
C=0
DO J=1,LEN
PROD=BUFF(J)*I+C
BUFF(J)=MOD(PROD,RAD)
C=PROD/RAD
END DO
DO
IF (C==0) EXIT
BUFF(LEN+1)=MOD(C,RAD)
C=C/RAD
LEN=LEN+1
END DO
END DO
Write(*,'(I6"!="I4.1\)') N,BUFF(LEN) !\表示不换行
DO I=LEN-1,1,-1
WRITE(*,"(I4.4)",advance="no") BUFF(I)
END DO
PRINT *
DEALLOCATE(BUFF)
END PROGRAM PROG1