65.9K
CodeProject 正在变化。 阅读更多。
Home

通过基础数论理解 Java 中的递归

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (10投票s)

2015 年 6 月 26 日

CPOL

7分钟阅读

viewsIcon

20322

在本文中,我们将了解如何在 Java 程序中使用递归。

引言

我们知道,方法是一个命名可执行指令块,可以从程序的多个位置调用。通常,它们有两种类型:迭代递归

迭代方法在其主体中包含循环结构,并被程序员频繁使用。另一方面,递归方法更复杂,使用它们需要更多的工作。

在本文中,我们将通过基础数论介绍递归背后的背景概念。所以,如果你准备好看到Java-数学的友好关系,那就开始吧!

背景

递归是一种技术,可以为使用简单循环难以编程的问题提供优雅的解决方案。

这意味着在适当的情况下强烈建议使用递归方法,因此您应该熟悉这些技术,以提升您在 Java 编程中的经验。

必备组件

Java

递归

除了调用其他方法外,方法也可以调用自身。这称为递归。如果你问我“为什么递归在 Java 中有效?”,我会告诉你因为它在数学中有效。事实上,递归与数学中一个著名的主题——归纳法相关

在接下来的部分中,我将介绍这个想法背后的数学原理。目前,递归的定义对我们来说就足够了。更多细节将在示例中展示。

数学

数学归纳法

数学归纳法是一种数学证明方法,通常用于证明给定语句适用于所有自然数。它是一种直接证明形式,分为两步完成。

简单地说,它的意思是如果命题 P 对于 1 为真,并且我们可以得出结论,如果 n 满足 P,那么 n+1 也满足 P;那么所有自然数都满足 P。参见 https://en.wikipedia.org/wiki/Mathematical_induction

自然数

自然数是正整数。所有自然数的集合用N表示。所以 1、2、545 是自然数,但 0、-6 和 12.5 不是。参见 https://en.wikipedia.org/wiki/Natural_number

整数

整数是自然数、0 或自然数的负数。所有整数的集合用I(或有时用Z,德语“zahlen”意为数字)表示。所以 1、-9 和 0 是整数,但 0.5、-6.7 和 Pi 不是。参见 https://en.wikipedia.org/wiki/Integer

除法算法

设 m 和 n 为两个任意整数。如果存在一个整数 q 使得 n = m*q,则称 m 整除 n 或 n 是 m 的倍数。在这种情况下,q 称为商,m 称为除数,n 称为被除数。有一个基本定理称为除法算法,它指出对于任意正整数 m 和 n,m 不等于 0,存在唯一一对整数,即 r 和 s,使得 n = m*q + r,其中 r余数。因此,如果 m 整除 n,那么在除法算法中 r 必须为 0。参见 https://en.wikipedia.org/wiki/Division_algorithm

G.C.D.:最大公约数

设 m 和 n 为两个整数,且不同时为 0。如果 d 同时整除 m 和 n,则称 d 是 m 和 n 的公约数。很容易证明,对于我们的 m 和 n,存在一个最大公约数,称为最大公约数(G.C.D.)

我们使用 gcd(m,n) 来表示 m 和 n 的最大公约数。它有许多性质,但这里使用其中一个,即 如果 m 大于或等于 n,则 gcd(m,n) = gcd(m-n,n)。参见 https://en.wikipedia.org/wiki/Greatest_common_divisor

阶乘函数

设 n 为一个自然数。1*2*3*...*n-2*n-1*n 记作 n! 并读作“n 的阶乘”。所以阶乘只是乘法,尽管它们在数论中扮演着非常重要的角色,尤其是在组合学中。我们需要这个重要的等式:n! = n * (n-1)!。参见 https://en.wikipedia.org/wiki/Factorial

斐波那契数列

序列就是一串数字。例如,1、2、3、... 是自然数序列。序列中的每一项都由一个称为索引的数值决定。所以我们示例序列中的 2 具有索引 2,因为它是序列中的第二项。

斐波那契数列是一个特殊的数列,其形式如下:1, 1, 2, 3, 5, 8, 13, ...

它有许多有趣的性质。其中之一是它的定义,它是以递归方式进行的:序列的第 n 项是前两项的和。参见 https://en.wikipedia.org/wiki/Fibonacci_number

Using the Code

现在我们准备好处理递归了。为了演示概念,我们开发了一个名为 Recursion 的类,用于计算乘法、阶乘、斐波那契数列、最大公约数等等。Recursion 类同时包含迭代和递归方法,因此我们可以轻松地比较它们。

理论已经足够了。让我们开始编码吧。

package Main;
/**

* @作者 MrSH http:www.thevelop.ir

*/

/** This class has static methods to describe Recursion in Java */
public class Recursion {
    
    /** finds x*y in an iterative fashion
     * @param x first number
     * @param y second number
     * @return returns the production of two numbers using iterative approach
     */
    public static long iterativeMultiplication(long x, long y){
        long production = 0;
        for (int i = 0; i < y; i++)
            production+=x;
        return production;
    }
    
    /** finds x*y in a recursive fashion
     * @param x first number
     * @param y second number
     * @return returns the production of two numbers using recursive approach
     */
    public static long recursiveMultiplication(long x, long y){
        if(y == 1)
            return x;
        return x + recursiveMultiplication(x, y-1);
    }
    
    /** finds n! in an iterative fashion
     * @param n input for factorial calculation
     * @return returns n! using iterative approach
     */
    public static long iterativeFactorial(long n){
        long factorial = 1;
        for (int i = 1; i <= n ; i++) {
            factorial *= i;
        }
        return factorial;
    }
    
    /** finds n! in a recursive fashion
     * @param n input for factorial calculation
     * @return returns n! using recursive approach
     */
    public static long recursiveFactorial(long n){
        if (n==1 || n== 0)
            return n;
        return n * recursiveFactorial(n-1);
    }
    
    /** finds nth term of Fibonacci sequence in an iterative fashion
     * @param n input for calculation
     * @return returns nth term of Fibonacci sequence using iterative approach
     */
    public static long iterativeFibonacci(long n){
        long x=1, y=1, z=1;
        for (int i = 3; i <= n; i++) {
            z=x+y;
            x=y;
            y=z;
        }
        return z;
    }
    
    /** finds nth term of Fibonacci sequence in a recursive fashion
     * @param n input for calculation
     * @return returns nth term of Fibonacci sequence using recursive approach
     */
    public static long recursiveFibonacci(long n){
        if (n==1 || n==2)
            return 1;
        return recursiveFibonacci(n-1)+recursiveFibonacci(n-2);
    }
    
    /** finds G.C.D(x,y) in an iterative fashion
     * @param x first input
     * @param y second input
     * @return returns G.C.D(x,y) using iterative approach
     */
    public static long iterativeGCD(long x, long y){
        long GCD = 1;
        long minimum = Math.min(x, y);
        for (int i = 1; i <= minimum; i++) {
            if (x%i == 0 && y%i == 0) {
                GCD = i;
            }
        }
        return GCD;
    }
    
     /** finds G.C.D(x,y) in a recursive fashion
     * @param x first input
     * @param y second input
     * @return returns G.C.D(x,y) using recursive approach
     */
    public static long recursiveGCD(long x, long y){
        if(x==1 || y == 1)
            return 1;
        if (x==0)
            return y;
        if(y==0)
            return x;
        if (x>=y)
            return recursiveGCD(y, x-y);
        return recursiveGCD(x, y-x);
    }
}

代码分析

iterativeMultiplication:它是如何工作的?

从上学时起我们就知道 x * y 意味着 x * ( 1 + 1 + 1 + ... + 1 ),其中括号中有 y 个 1;我们也知道 x * ( 1 + 1 + 1 + ... + 1 ) = x + x + x + ... + x。所以在我们的 for 循环中,我们将 x 自身相加 y 次以得到 x * y。

如您所见,此方法背后的思想非常简单、直接和基础

recursiveMultiplication:它是如何工作的?

从高中时代 :) 我们记得 x * y = x * ( 1 + y - 1 ),所以 x * y = x + x * ( y - 1 ),并且记住 1乘法的单位元,这意味着 x * 1 = x。虽然我认为这个说明已经足够了,但让我们更详细地研究这个方法,因为递归是本文的主题。

考虑以下代码片段

public static void main(String[] args) {
       System.out.println(Recursion.recursiveMultiplication(5, 3));
    }

执行过程如下

!( 3 == 1 ) => return  5 + recursiveMultiplication(5, 2);
!( 2 == 1 ) => return  5 + [ 5 + recursiveMultiplication(5, 1); ]
( 1 == 1 ) => return 5 + [ 5 + [ 5 ] ] == 15 
System.out.println(15);

iterativeFactorial:它是如何工作的?

如前所述,n! = 1 * 2 * 3 * ... * n。所以在 for 循环中,我们声明一个阶乘变量,初始化为 1。然后将其乘以 1、2、...、n 以获得 n!。不再赘述!

recursiveFactorial:它是如何工作的?

利用关系 n! = n * ( n -1 )!,我们每一步都减少 n,直到达到 1。在数学中,0! 定义为 1,显然 1! = 1

考虑以下代码片段

public static void main(String[] args) {
       System.out.println(Recursion.recursiveFactorial(6));
    }
!( 6 ==1 ) => return 6 * recursiveFactorial(5);
!( 5 ==1 ) => return 6 * [ 5 * recursiveFactorial(4); ]
!( 4 ==1 ) => return 6 * [ 5 * [ 4 * recursiveFactorial(3); ] ] 
!( 3 ==1 ) => return 6 * [ 5 * [ 4 * [ 3 * recursiveFactorial(2); ] ] ]
!( 2 ==1 ) => return 6 * [ 5 * [ 4 * [ 3 * [ 2 * recursiveFactorial(1); ] ] ] ]
( 1 ==1 ) => return 6 * [ 5 * [ 4 * [ 3 * [ 2 * 1 ] ] ] ] == 720
System.out.println(720);

iterativeFibonacci:它是如何工作的?

根据斐波那契数列的定义,此方法的实现非常简单。

recursiveFibonacci:它是如何工作的?

为了说明这一点,例如,考虑 recursiveFibonacci(4);请看下图以理解评估过程。

Click to enlarge image

iterativeGCD:它是如何工作的?

在此方法中,我们首先将 GCD 设置为 1。由于我们知道当 m 整除 n 时,m 小于或等于 n,因此我们获取输入的最小值。在 for 循环中,我们从 1 数到最小值,当我们找到更大的除数时,我们将 GCD 更改为该值。

recursiveGCD:它是如何工作的?

在这种情况下,我们只使用最大公约数的性质,例如 gcd(m,n) = gcd(n,m),gcd(m,1) = 1,gcd(m,0) = m,以及我们之前提到的关系:如果 m 大于或等于 n,则 gcd(m,n) = gcd(m-n,n)。

例如,考虑以下代码片段

public static void main(String[] args) {
       System.out.println(Recursion.recursiveGCD(24, 12));
    }
( 24 >= 12 ) => return recursiveGCD(12,12);
( 12 >= 12 ) => return recursiveGCD(12,0);
( 0 == 0 ) => return 12;
System.out.println(12);

延伸阅读

我鼓励你搜索以下主题以扩展你的知识,从而递归地思考:)

  • Java 中的汉诺塔递归算法
  • Java 中使用递归反转数字
  • Java 中使用递归选择排序
  • Java 中使用递归二分查找
  • Java 中使用递归生成分形

结束 --- 欢迎自由发展 :)

通过基础数论理解 Java 中的递归 - CodeProject - 代码之家
© . All rights reserved.