用Java计算阶乘 - 迭代和递归

397 阅读6分钟

简介

计算一个数字的阶乘是一项简单的任务。一个数字的阶乘是该数字(正整数)与小于该数字的所有正整数的乘积。换句话说,就是用一个数字乘以从该数字到1的所有整数。

0!也等于1,因为你不可能完全从0降到1。

这只是一个协议,即0!等于1,对此,一个常见的解释(可悲的是没有归于一个人)是。'因为正好有一种方法可以什么都不做'。

阶乘是由整数表示的,后面有一个感叹号。

5!表示一个 阶乘.换句话说,你可以直接说五阶乘

为了计算这个阶乘,我们把这个数字与比它小的每个正整数相乘。

5!=5\*4\*3\*2\*15!=1205! = 5 \* 4 \* 3 \* 2 \* 1 5! = 120

在本教程中,我们将学习如何用Java计算一个整数的阶乘。这可以用循环递归来完成--尽管递归可以说是一种更自然的方法。当然,你应该实现你更喜欢的那一种。

使用循环计算阶乘

让我们开始使用循环来计算阶乘--whilefor 。我们也可以使用do-while 循环,但是最初的do 块在这里对我们没有什么作用,而且会引入一个潜在的错误的边缘案例,所以我们将跳过它。

两种循环类型的一般过程都很相似--我们只需要一个参数作为输入和一个计数器来迭代数字。

让我们从for 循环开始。

public static int getFactorialForLoop(int n) {
    int result = 1;
    if (n > 1) {
        for (int i = 1; i <= n; i++) {
            result = result * i;
        }
        return result;
    }
    else {
        System.out.println("n has to be positive");
        return result;
    }
}

实际上,我们在这里有点偏离了原来的定义--我们是从1数到n ,而阶乘的定义是从给定的数字到1

不过,当你把它写在纸上时,在数学上。

1\*2\*3\*4...\*n=n\*(n1)\*(n2)\*(n3)\*(n4)...\*(n(n1))1 \* 2 \* 3 \* 4 ...\* n = n \* (n-1) \* (n-2) \* (n-3) \* (n-4) ...\* (n - (n-1))

这些都是相等的语句,你真的可以从1n ,或者反过来。

为了简化,(n - (n-1)) 将永远等于1

这意味着我们在哪个方向上迭代并不重要。它可以从1开始,向n ,也可以从n ,向1减少。

为什么呢?

好吧,如果你把循环反过来,这个方法并没有变得更复杂,但它只是有点不干净。

public static int getFactorialForLoop(int n) {
    int result = n;
    if (n >= 1) {
        for (int i = n-1; i >= 1; i--) {
            result = result * i;
        }
        return result;
    }
    else {
        System.out.println("n has to be positive");
        return 1;
    }
}

现在,这一点已经明确了,让我们开始分解这个方法。

它需要一个参数,n ,它表示我们要计算的阶乘的数字。首先,我们定义一个名为result 的变量,并将1 赋值给它。

为什么指定1而不是0

如果我们给它赋值为0,那么接下来的所有乘法都将包含这个0,自然,它将使整个操作崩溃,变成一个巨大的0

然后我们开始我们的for 循环,将i 定义为从1 开始的计数器。注意,条件语句是i <= n; ,以便将n 本身也包括在内。

for 循环中,我们将result 的当前值与我们的索引i 的当前值相乘。

最后,我们返回result 的最终值。为了获得用户的输入,记得要导入java.util.Scanner

如果你想阅读更多关于在Java中获取用户输入的信息--请阅读我们的《扫描器类指南》

让我们测试一下我们的方法并打印结果。

Scanner scanner = new Scanner(System.in);
int inp;
	    
System.out.println("Enter a number: "); 
inp = Integer.parseInt(scanner.nextLine());   
	       
System.out.println("The result is: " + getFactorialForLoop(inp));        

	
public static int getFactorialForLoop(int n) {
    int result = 1;
	if (n >= 1) {
	    for (int i = 1; i <= n; i++) {
	        result = result * i;
	    }
	    return result;
	}
	else {
	  System.out.println("n has to be positive");
	  return result;
	}

它将提示用户给予输入。我们将用4

Enter a number: 4
The result is: 24

你可以用计算器来验证这个结果。

4!4 * 3 * 2 * 1 ,结果是24

现在让我们看看如何用while 循环计算阶乘。下面是我们修改后的方法。

public static int getFactorialWhileLoop(int n){
    int result = 1;
    while (n > 1) {
        result = result * n;
        n -= 1;
    }
    return result;
}

这与for 循环很相似。除了,这次我们从n1移动,更接近于数学定义。让我们测试一下我们的方法。

System.out.println("Enter a number: "); 
inp = Integer.parseInt(scanner.nextLine());   
    
System.out.println("The result is: " + getFactorialWhileLoop(inp));   

我们再一次输入4作为输入。

Enter a number: 4
The result is: 24

虽然计算的结果是4*3*2*1 ,但最后的结果和之前的一样。

现在让我们来看看如何用递归的方法计算阶乘。

使用递归计算阶乘

递归方法是一种调用自身的方法,并在某些条件下终止调用。

一般来说,每个递归方法都有两个主要组成部分:一个基例和一个递归步骤

基准案例是问题的最小的实例。另外,它们必须有一个断点,即一个将返回一个值并脱离递归的案例。就阶乘方法而言,基例就是我们返回阶乘的最后一个元素,也就是1

如果没有基数或基数不正确,你的递归方法可以无限地运行,导致溢出。

递归步骤--顾名思义,是方法的递归部分,整个问题被转化为更小的东西。如果递归步骤未能将问题缩小,那么再次递归可以无限地运行。

考虑一下阶乘的递归部分。

  • **5!**是5 * 4 * 3 * 2 * 1

但我们也知道,。

  • **4!**是4 * 3 * 2 * 1

换句话说,**5!5 * 4! ,而4!**是4 * 3! ,以此类推。

所以我们可以说,n! = n * (n-1)! 。这将是我们的阶乘的递归步骤!

阶乘的递归在达到1时结束。这将是我们的基本情况。如果n1 或更少,我们将返回1 ,涵盖零输入。

让我们来看看我们的递归阶乘方法。

public static int getFactorialRecursively(int n){
    if (n <= 1){
        return 1;
    }
    else {
        return n * getFactorialRecursively(n-1);
    }
}

正如你所看到的,if 块体现了我们的基本情况,而else 块涵盖了递归的步骤

让我们测试一下我们的方法。

System.out.println("Enter a number: "); 
inp = Integer.parseInt(scanner.nextLine());   
    
System.out.println("The result is: " + getFactorialRecursively(inp)); 

这次我们将输入3作为输入。

Enter a number:3
The result is: 6

我们得到同样的结果。但是这一次,引擎盖下的情况相当有趣。

你看,当我们输入时,该方法将检查if 块,由于3大于1,它将跳到else 块。在这个块中,我们看到return n * getFactorialRecursively(n-1);

我们暂时知道了n 的当前值,是3 ,但getFactorialRecursively(n-1) 仍需计算。

然后程序再次调用同一个方法,这次我们的方法以2作为参数。它检查了if 块,然后跳到else 块,再次遇到最后一行。现在,n 的当前值是2 ,但是程序仍然必须计算getFactorialRecursively(n-1)

所以它再次调用该方法,但这次if 块,或者说,基类成功地返回了1,并从递归中脱离出来。

按照同样的模式往上走,它返回每个方法的结果,将当前的结果与之前的n ,并为之前的方法调用返回。换句话说,我们的程序首先到达阶乘的底部(也就是1),然后一路向上,同时在每一步上进行乘法。

同时从调用堆栈中逐一删除方法,直到返回n * (n-1) 的最终结果。

这通常是递归方法的工作方式。一些更复杂的问题可能需要更深的递归,有一个以上的基例或一个以上的递归步骤。但是现在,这个简单的递归足以解决我们的阶乘问题了

计算大数字的阶乘

阶乘很快就会变大。每个人都知道指数是如何在少量的步骤中变得巨大的

26=642^6 = 64
6!=7206! = 720

事实上,20的阶乘就等于。

20!=2,432,902,008,176,640,00020! = 2,432,902,008,176,640,000

这就是2.4五亿。下一个阶乘是51五亿,这甚至超出了Java中longs的范围,它是~9五亿。整数仅有24亿,所以它们很快就会被淘汰。

这就是BigInteger 的作用--JVM不会为数字预先分配已知的空间,而是动态地更新其大小。你可以用数字填满整个RAMBigInteger ,只有这样你才会遇到限制。

public static BigInteger getFactorialRecursively(int n) {
    BigInteger value = BigInteger.valueOf(n);
    if (value == BigInteger.ZERO) {
        return BigInteger.ONE;
    } else {
        return value.multiply(getFactorialRecursively(n - 1));
    }
}

21 扔进这个方法中会导致。

51090942171709440000

结论

在这篇文章中,我们介绍了如何使用forwhile 循环来计算阶乘。我们还学习了什么是递归,以及如何使用递归计算阶乘。