阶乘

生活百科 2023-01-25 19:31生活百科www.aizhengw.cn

阶乘

n!一般指本词条

阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号,是数学术语。

一个正整数的阶乘factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。

亦即n!=1×2×3×...×n。阶乘亦可以递归方式定义0!=1,n!=(n-1)!×n。

基本介绍

  • 中文名阶乘
  • 外文名factorial
  • 分类数学
  • 提出者基斯顿卡曼
  • 时间1808年
  • 特点小于及等于该数的正整数的积

概念

阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号,是数学术语。
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。

计算方法

大于等于1
任何大于等于1 的自然数n 阶乘表示方法
0的阶乘
0!=1。
定义的必要性
由于正整数的阶乘是一种连乘运算,而0与任何实数相乘的结果都是0。所以用正整数阶乘的定义是无法推广或推导出0!=1的。即在连乘意义下无法解释“0!=1”。
给“0!”下定义只是为了相关公式的表述及运算更方便。
在离散数学的组合数定义中,对于正整数
满足条件
的任一非负整数
都是有意义的,特别地在
时,有
。 对于组合数公式
来说,在
时,都由于遇到0的阶乘没有定义而发生巨大尴尬。
对照结论
和公式
,我们顺势而为地定义“0!=1”就显得非常必要了。这样,组合数公式在
时也通行无阻,不会有任何尴尬了。
使用的广泛性
(1)在函式
的麦克劳林级数展开式中
明确地用到了“0!=1”的定义,没有这个定义就只能麻烦地表示为
(2)作为阶乘延拓的伽玛函式是定义在複数範围内的亚纯函式,与之有密切联繫的函式还有贝塔函式(他们分别被称为欧拉第二积分与欧拉第一积分)。
拿伽玛函式
来说,显然有
是大于1的正整数时,有公式
,当0的阶乘被定义为0!=1后,公式
对任意正整数
就都成立了。
为什幺0!=1?
必须清楚地说明
只是一种定义出来的特殊的“形式”上的阶乘记号。它无法用演绎方法来论证。
为什幺0!=1”这个问题是伪问题,而初学者总要追问这个伪问题。这就说明了我们在教材和教学实践中都没有把“有关‘0!=1’只是一种‘定义’的概念”讲清楚。
有教辅材料上把上述必要性及合理性视作为推导的过程,那是大错特错了。必要性及合理性只是有限几个例子,“0!=1”这种定义是不能用举若干例子的方法来证明的。
这个定义使用至今可谓久经考验方便多多,没有出现过任何逻辑上不合理的现象。

定义範围

通常我们所说的阶乘是定义在自然数範围里的(大多科学计算器只能计算 0~69 的阶乘),小数科学计算器没有阶乘功能,如 0.5!,0.65!,0.777!都是错误的。,有时候我们会将Gamma 函式定义为非整数的阶乘,因为当 x 是正整数 n 的时候,Gamma 函式的值是 n-1 的阶乘。
伽玛函式(Gamma Function)
定义伽马函式
运用积分的知识,我们可以证明Γ(s)=(s-1)× Γ(s-1)
所以,当 x 是整数 n 时,
这样 Gamma 函式实际上就是阶乘的延拓。
计算机科学
用Ruby求 365 的阶乘。
def AskFactorial(num) factorial=1;
step(num,1){|i| factorial=i}
return factorial end factorial=AskFactorial(365)
puts factorial
阶乘有关公式
该公式常用来计算与阶乘有关的各种极限。
此为斯特林公式的简化公式(完整的见下图)。

双阶乘

双阶乘用“m!!”表示。
当 m 是自然数时,表示不超过 m 且与 m 有相同奇偶性的所有正整数的乘积。如
当 m 是负奇数时,表示绝对值小于它的绝对值的所有负奇数的绝对值积的倒数。
当 m 是负偶数时,m!!不存在。
自然数双阶乘比的极限
阶乘的逼近函式公式
对于正整数
此逼近函式把近1 数处理到最小,函式指数误差详右侧图片
对于带小数大于1的正实数的阶乘计算公式为
(1)
对于0.5到1之间的小数拟合公式如下
对于0到0.5之间的小数阶乘,先计算(1-n)!,再按一下公式计算
计算时不要把先后顺序弄反,否则误差会增加,以上公式通过了伽马函式的校验,误差极小。
以下是拟合公式与实际值的对比图
此处按(1)式拓展到负数区间,(-n)!的阶乘如下,但与词条定义冲突
如此进一步计算为
(2)
这样把阶乘拓展到负数区间,形成了负数区间的周期震荡阶乘函式。
对于负小数-1<n<0区间,的阶乘其值就等于其决定值的阶乘了
此处有待商榷,如此,阶乘的原定义就发生了改变。。。以下是按(1).(2)式拓展到正负数区间的阶乘倒数图像

拓展与再定义

一直以来,由于阶乘定义的不科学,导致以后的阶乘拓展以后存在一些理解上得困扰,和数理逻辑的不顺。
阶乘从正整数一直拓展到複数。传统的定义不明朗。所以必须科学再定义它的概念
真正严谨的阶乘定义应该为对于数n,所有绝对值小于或等于n的同余数之积。称之为n的阶乘,即n!
对于複数应该是指所有模n小于或等于│n│的同余数之积。。。对于任意实数n的规范表达式为
正数 n=m+x,m为其正数部,x为其小数部
负数n=-m-x,-m为其正数部,-x为其小数部
对于纯複数
n=(m+x)i,或n=-(m+x)i
我们再拓展阶乘到纯複数
正实数阶乘: n!=n│!=n(n-1)(n-2)....(1+x).x!=(i^4m).│n│!
负实数阶乘: (-n)!=cos(m
)n│!=(i^2m)..n(n-1)(n-2)....(1+x).x!
(ni)!=(i^m)│n│!=(i^m)..n(n-1)(n-2)....(1+x).x!
(-ni)!=(i^3m)│n│!=(i^3m)..n(n-1)(n-2)....(1+x).x!

广义複数阶乘

对于一般的複数而言, 所有模n小于或等于│n│的同余数之积,意味着其实部与虚部必须满足一定条件,条件如下
複数z=ak+ibk
当z的幅角a 相等时zk=(n-k)(cosa+isina),它的阶乘为
说明複数阶乘存在路径问题,路径不同阶乘的结果就不相同,幅角a相等是指按直线从0点附近到z,不等时是按曲线取阶乘。複数阶乘存在方向问题,就是说它是有方向的量。。。广义阶乘涵括正负实数阶乘。。。

套用

求n!的位数
方法一
可以将n!表示成10的次幂,即n!=10^M(10的M次方)则不小于M的最小整数就是 n!的位数,对该式两边取对数,有 M =log10^n!

M = log10^1+log10^2+log10^3...+log10^n
循环求和,就能算得M值,该M是n!的精确位数
代码
#include "iostream"#include "math.h"using namespace std;int main(){      int n,i;      double d;      while (scanf("%d",&n)!=EOF){          d=0;          for (i=1;i<=n;i++)d+=(double)log10(i);          printf("%d\n",(int)d+1);      }return 0;}  
方法二
利用斯特林(Stirling)公式的进行求解。下面是推导得到的公式
res=(long)( (log10(sqrt(4.0acos(0.0)n)) + n(log10(n)-log10(exp(1.0)))) + 1 );
当n=1的时候,上面的公式不适用,所以要单独处理n=1的情况!
有关斯特林(Stirling)公式及其相关推导,这里就不进行详细描述,
这种方法速度很快就可以得到结果。
求n!末尾0的个数
思路
一个数 n 的阶乘末尾有多少个 0 取决于从 1 到 n 的各个数的因子中 2 和 5 的个数
而 2 的个数是远远多余 5 的个数的, 求出 5 的个数即可
题解中给出的求解因子 5 的个数的方法是用 n 不断除以 5, 直到结果为 0
然后把中间得到的结果累加. 例如, 100/5 = 20, 20/5 = 4, 4/5 = 0
则 1 到 100 中因子 5 的个数为 (20 + 4 + 0) = 24 个
即 100 的阶乘末尾有 24 个 0. 其实不断除以 5
是因为每间隔 5 个数有一个数可以被 5 整除, 然后在这些可被 5 整除的数中
每间隔 5 个数又有一个可以被 25 整除, 故要再除一次, ... 直到结果为 0, 表示没有能继续被 5 整除的数了.
代码
#include <cstdio>#include <iostream>using namespace std;int main(){    int N,i,mod5,d5,count0=0;    scanf("%d",&N);    for (i=1;i<=N;i++)    {        mod5=i%5;        d5=i/5;        while(mod5==0)        {            count0++;            mod5=d5%5;            d5=d5/5;        }    }    printf("%d的个数 %d\n",N,count0);}

计算机阶乘

Logo 语言
Logo 语言因为是少儿的学习语言,阶乘方法要複杂一些,而且时间较慢,下面是低精度、高精度、统计位数的阶乘算法
TO DJDJC :N ;低精度阶乘
MAKE "S 1;累乘器开始的值是1
FOR "I 1 :N[MAKE "S :S :I]
(PR :N [!=] :S)
END
TO GJDJC :N;高精度阶乘
IF :N>=1000 THEN PR "请输入不大于999的数! STOP
MAKE "PRECISION 6;计算显示位数设定为六位
MAKE "A ARRAY 860;定义数组空间0-859组
ASET :A 1 1;乘法数组第1空间赋值为1
FOR "I 2 859[ASET :A :I 0] ;其他数组空间赋值为0
FOR "I 1 :N [JC :I];调用阶乘过程
MAKE "K 0;数组空间是0的标记
MAKE "Z 0;总共有多少组数字的标记
MAKE "WS 0;累加总共有多少位的计数器
TYPE :N TYPE[!=];从高位到低位显示计算结果
FOR "M 1 859[XXS 860-:M]
PR[ ] TYPE[这是一个]TYPE :WS TYPE[位数]PR[]
END
TO JC :I;计算阶乘的过程
FOR "J 1 858[CF :I :J] ;对所有数组空间逐一计算乘法
FOR "J 1 858[CLJW :J];处理乘法过程中的进位
END
TO CF :I :J ;计算乘法的过程
MAKE "ZJ AGET :A :J
MAKE "ZJ :ZJ :I;I是阶乘中需要累乘的数
ASET :A :J :ZJ
END
TO CLJW :J;处理进位的过程
MAKE "X AGET :A :J
IF :X<1000 THEN GO "XXX;处理没有进位的数组
MAKE "JINWEI INT (:X / 1000);截取小于1000的尾数
MAKE "WEISHU :X - :JINWEI 1000 ;截取进位的数字
ASET :A :J :WEISHU;存储尾数
MAKE "Y AGET :A :J+1
MAKE "Y :Y + :JINWEI
ASET :A :J+1 :Y;向上进位
LABEL "XXX
END
TO XXS :P;显示计算结果的过程
MAKE "NN AGET :A :P
IF (AND :NN=0 :K=0) THEN[GO "END_]ELSE[MAKE "K 1 MAKE "Z :Z+1] ;避开无效数组
IF :Z=1 THEN MAKE "WS :WS+(COUNT :NN) GO "UP;计算头一个有效数组的位数
IF :Z>1 THEN MAKE "WS :WS+3;累计数值的总位数
IF :NN < 10 THEN TYPE [0];填充空位0
IF :NN < 100 THEN TYPE [0]
LABEL "UP
TYPE :NN
LABEL "END_ ;越过开头的空数组
END
TO JC :N ;求解任意数的阶乘是多少位数
MAKE "S 0;先赋值位数为0
FOR "I 1 :N[MAKE "S :S+LOG10 :I]
TYPE[:S=]PR :S
END
Common Lisp 语言
在 Common Lisp 中, 可以很方便的使用更为简洁的使用递归实现阶乘
(defun factorial (n)  (cond      ((> n 0) ( (factorial (- n 1)) n))      ((= n 0) 1)      (t (error "N is smaller than 0."))))
注意因为百度不提供任何Lisp语言的代码框,此处使用的是Python的代码框,所以关键字可能无法高亮显示
Python 语言
在 Python 中, 同样可以使用这种简洁方式实现阶乘的计算
def factorial(n)    if(n<=1):        return 1    else:        return factorial(n-1)  n
C语言
在 C 语言中,使用循环语句可以很方便的求出阶乘的值,下面介绍一个很简单的阶乘例子。(因为网上多数是比较麻烦的方法)
【计算出“ 1!+ 2!+ 3!+ …… + 10!”的值是多少?】
#include<stdio.h>int main(){    int n, x;        for(n = x = 1; n <= 10; ++n) {        x = n;        printf("%d\t%d\n", n, x);    }                return 0;}
/10!3628800/
Pascal中
program test;varn:longint;function jc(n:longint):qword;begin if n=0 then jc:=1 else jc:=njc(n-1)end;begin readln (n); writeln (jc(n))end.
C++ 中
#include<iostream>using namespace std;long long f(int n){    long long e=1;    if(n>0)    e=nf(n-1);    cout<<n<<"!="<<e<<endl;    return e;}int main(){    int m=20;    f(m);    return 0;}
以上使用 C++ 11 标準
也可以利用积分求浮点数阶乘
#include<cstdio>#include<cmath>double s;const double e=exp(1.0);double F(double t){    return pow(t,s)pow(e,-t);}double simpson(double a,double b){    double c=a+(b-a)/2;    return (F(a)+4F(c)+F(b))(b-a)/6;}double asr(double a,double b,double eps,double A){    double c=a+(b-a)/2;    double L=simpson(a,c),R=simpson(c,b);    if(fabs(L+R-A)<=15eps) return L+R+(L+R-A)/15.0;    return asr(a,c,eps/2,L)+asr(c,b,eps/2,R);}double asr(double a,double b,double eps){    return asr(a,b,eps,simpson(a,b));}int main(){    scanf("%lf",&s);    printf("%lf\n",asr(0,1e2,1e-10));    return 0;} 
JAVA 中
public class Main{ final static int MAX=20;// 可以替换 MAX 的值。    public static void main(String[] args)    {        int i=1;        long result=1;        long[] n=new long[MAX];        do{            result=(i+1);            System.out.println(i+1+"!="+result);            n[i]=result;            i++;        }while(i<MAX);        n[0]=1;//阶乘end    }}
使用for循环更简单易懂:
public static void main(String[] args) {    long result = 1;    for (int j = 0; j < 10; j++) {        result = (j + 1);        System.out.println(j + 1 + "!=" + result);    }}
php中
function jc($n){if($n>1){return $njc($n-1);}else{return 1;}}echo jc(3);
JavaScript 阶乘
function factorial(num){    if (num <= 1)        return 1;    else        return num  arguments.callee(num - 1);};
彙编中的阶乘算法
.286.model small,stdcallFactorial PROTO :WORD.stack 200h.data    f_n WORD 6    result WORD ?.CODESTART:    mov ax,@data    mov ds,ax    mov es,ax    invoke Factorial,f_n    mov result,ax    mov ah,4ch    int 21h    ;计算阶乘    ;输入参数:[ebp+8] = n, 需要计算的数字    ;返回参数:eax = n的阶乘    Factorial PROC near uses bx,fn    mov ax,fn    cmp ax,0    ja L1    mov ax,1    jmp L2    L1:    dec ax    invoke Factorial,ax    ReturnFact:    mov bx,fn    mul bx    L2:    retFactorial ENDPEND START

高精度求阶乘

Visual Basic NET
'本代码使用了斯特林(Stirling)逼近的方式计算较大数值的阶乘和有小数位数的数值的阶乘。详细过程请参见《用Stirling逼近近似计算阶乘的探讨与套用》一书。
'根据验算,结果与Windows 7的计算器结果有出入,还能忍受。特别是可以计算较大数值的阶乘,例如 36000,Windows计算器溢出。无法考证我的结果与真实结果的差距,没有比较……^.^
Private Sub 计算按钮_Click(sender As Object, e As EventArgs) Handles 计算按钮.Click
Dim 阶乘数 As Decimal = CDec(InputBox("请输入一个数值,将得到它的阶乘结果。"))
' 因为要计算比较大的数值,将较小的整数值按照阶乘的定义来计算得到準确值,但 VBULong整数的最大範围能计算到 20的阶乘,21将溢出;较大的数值或者小数按照 Stirling 逼近的方式计算。
If 阶乘数 >= 0 And 阶乘数 <= 20 And Fix(阶乘数) = 阶乘数 Then
MsgBox(阶乘数 & " 的阶乘结果是" & 小阶乘(CInt(阶乘数)))
Else
MsgBox(阶乘数 & " 的阶乘结果是" & 大数阶乘(CDbl(阶乘数)) & " E " & 大数阶乘指数(阶乘数))
End If
End Sub
'这是小数值阶乘的过程,标準计算方式,结果精确。
Private Function 小阶乘(阶乘数 As Integer) As ULong
If 阶乘数 = 0 Or 阶乘数 = 1 Then'如果数值是 0或者 1,直接返回 1。
Return 1
Else
小阶乘 = 1
For 阶乘次数 As Integer = 1 To CInt(阶乘数)
小阶乘 = CULng(阶乘次数)
Next
Return 小阶乘
End If
End Function
''' <summary>
''' 这个过程用 Stirling 逼近的方式计算较大数值和有小数点的值的阶乘结果的前 15 位。详细过程见《用 Stirling 逼近近似计算阶乘的探讨与套用》一书。
''' </summary>
''' <param name="阶乘数"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function 大数阶乘(阶乘数 As Double) As Double
Dim X As Double = 0.5 Math.Log(2 阶乘数 Math.PI) / Math.Log(10) + 阶乘数 Math.Log(阶乘数 / Math.Exp(1)) / Math.Log(10)
Dim XDouble As Double = X - Math.Truncate(X)
Dim Y As Double = Math.Exp(XDouble Math.Log(10))
Dim Z As Double = Math.Exp(1 / 12 / 阶乘数 - 1 / 360 / 阶乘数 / 阶乘数)
Return Y Z
End Function
''' <summary>
''' 这个过程用 Stirling 逼近的方式计算较大数值和有小数点的值的阶乘结果的小数点位数。详细过程见《用 Stirling 逼近近似计算阶乘的探讨与套用》一书。
''' </summary>
''' <param name="阶乘数"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function 大数阶乘指数(阶乘数 As Double) As ULong
Dim X As Double = 2 Math.PI 阶乘数
Dim Y As Double = 0.5 Math.Log10(X)
Dim A As Double = 阶乘数 Math.Log10(阶乘数 / Math.E)
Return CULng(Math.Truncate(Y + A))
End Function
C语言
#include<stdio.h>#define N 5000//modify it to hold larger number//to avoid stack overflow, define it as global varibleint f[N];//do not need to be assigned as 0 because it has already been assigned 0 by the systemint main(){    int n,i,j,s,up;    scanf("%d",&n);    for(i=2,f[0]=1;i<=n;i++)    {        for(j=up=0;j<N;j++)        {            s=f[j]i+up;            f[j]=s%10;            up=s/10;        }    }    for(i=N-1;f[i]==0;i--);    for(;i>=0;i--)    printf("%d",f[i]);    printf("\n");    return 0;}
Pascal 语言(可计算到3251!)
var a:array[1..10000] of integer;    b,c,d,t,x:integer;begin     readln (x);     if (x<0) then  begin writeln ('error!'); readln; halt; end;     for  t:=1 to 10000 do a[t]:=0;     d:=1; a[1]:=1;     for c:=1 to x do                     {一直乘到x}     begin          t:=1; b:=0;                     {t:第几位数 b:进位 d:总位数}          repeat               a[t]:=a[t]c+b;           {数组每位均乘上c,加上进位}               b:=a[t] div 10;           {分离出进位}                if a[t]>=10 then if (t=d) then d:=d+1;  {假如一位乘时有}                                                         {进位,则总位数加1}                 a[t]:=a[t] mod 10;                inc (t);                  {数组下一位}          until (t>d);                    {直到乘完数组的每一位数字}     end;     for t:=d downto 1 do write (a[t]);    {输出}end.
上一篇:kkbox 下一篇:MGCP协定

Copyright@2015-2025 www.aizhengw.cn 癌症网版板所有