Iteration vs. Recursion in Java

本文深入探讨了使用递归和迭代两种不同的方法来计算阶乘。通过对比这两种方法的工作原理、运行过程及优缺点,揭示了它们在计算机科学领域的应用差异。递归方法构建了一个链式的乘法操作,而迭代方法则通过循环逐步计算。同时,文章还介绍了阶乘在实际应用中的复杂情况,如树形递归,以及如何用迭代方法解决这类问题。最后,通过Fibonacci数列的递归和迭代计算,进一步阐述了两种方法在处理递归定义序列时的效率差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Recursion

Consider the factorial function: n!=n*(n-1)*(n-2)*...*1

There are many ways to compute factorials. One way is that n! is equal to n*(n-1)!. Therefore the program can be directly written as:

Program 1:

int factorial (int n) {
    if (n == 1) {
        return 1;
    } else {
        return n*factorial(n-1);
    }
}

In order to run this program, the computer needs to build up a chain of multiplications: factorial(n) → factorial(n-1) → factorial(n-2) → … → factorial(1). Therefore, the computer has to keep track of the multiplications to be performed later on. This type of program, characterized by a chain of operations, is called recursion. Recursion can be further categorized into linear and tree recursion. When the amount of information needed to keep track of the chain of operations grows linearly with the input, the recursion is called linear recursion. The computation of n! is such a case, because the time required grows linearly with n. Another type of recursion, tree recursion, happens when the amount of information grows exponentially with the input. But we will leave it undiscussed here and go back shortly afterwards.

2. Iteration

A different perspective on computing factorials is by first multiplying 1 by 2, then multiplying the result by 3, then by 4, and so on until n. More formally, the program can use a counter that counts from 1 up to n and compute the product simultaneously until the counter exceeds n. Therefore the program can be written as:

Program 2:

int factorial (int n) {
    int product = 1;
    for(int i=2; i<n; i++) {
        product *= i;
    }
    return product;
}

This program, by contrast to program 2, does not build a chain of multiplication. At each step, the computer only need to keep track of the current values of the product and i. This type of program is called iteration, whose state can be summarized by a fixed number of variables, a fixed rule that describes how the variables should be updated, and an end test that specifies conditions under which the process should terminate. Same as recursion, when the time required grows linearly with the input, we call the iteration linear recursion.

3. Recursion vs Iteration

Compared the two processes, we can find that they seem almost same, especially in term of mathematical function. They both require a number of steps proportional to n to compute n!. On the other hand, when we consider the running processes of the two programs, they evolve quite differently.

In the iterative case, the program variables provide a complete description of the state. If we stopped the computation in the middle, to resume it only need to supply the computer with all variables. However, in the recursive process, information is maintained by the computer, therefore “hidden” to the program. This makes it almost impossible to resume the program after stopping it.

4. Tree recursion

As described above, tree recursion happens when the amount of information grows exponentially with the input. For instance, consider the sequence of Fibonacci numbers defined as follows:

recursion-iteration-java

By the definition, Fibonacci numbers have the following sequence, where each number is the sum of the previous two: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...

A recursive program can be immediately written as:

Program 3:

int fib (int n) {
    if (n == 0) {
        return 0;
    } else if (n == 1) {
        return 1;
    } else {
        return fib(n-1) + fib(n-2);
    }
}

Therefore, to compute fib(5), the program computes fib(4) and fib(3). To computer fib(4), it computes fib(3) and fib(2). Notice that the fib procedure calls itself twice at the last line. Two observations can be obtained from the definition and the program:

  1. The ith Fibonacci number Fib(i) is equal to Φi/√5 rounded to the nearest integer, which indicates that Fibonacci numbers grow exponentially.
  2. This is a bad way to compute Fibonacci numbers because it does redundant computation. Computing the running time of this procedure is beyond the scope of this article, but one can easily find that in books of algorithms, which is O(Φn). Thus, the program takes an amount of time that grows exponentially with the input.

On the other hand, we can also write the program in an iterative way for computing the Fibonacci numbers. Program 4 is a linear iteration. The difference in time required by Program 3 and 4 is enormous, even for small inputs.

Program 4:

int fib (int n) {
    int fib = 0;
    int a = 1;
    for(int i=0; i<n; i++) {
       fib = fib + a;
       a = fib;
    }
    return fib;
}

However, one should not think tree-recursive programs are useless. When we consider programs that operate on hierarchically data structures rather than numbers, tree-recursion is a natural and powerful tool. It can help us understand and design programs. Compared with Program 3 and 4, we can easily tell Program 3 is more straightforward, even if less efficient. After that, we can most likely reformulate the program into an iterative way.

Reference:
1. Cornell cs211 lecture

Simulation setting file, output folder and iteration number are not provided! Using default ones... java.io.FileNotFoundException: scripts\DeepEdge\config\default_config.properties (系统找不到指定的路径。) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:213) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106) at edu.boun.edgecloudsim.core.SimSettings.initialize(SimSettings.java:131) at edu.boun.edgecloudsim.applications.deepLearning.TrainingEdge.main(TrainingEdge.java:58) Edge Devices XML cannot be parsed! Terminating simulation... java.io.FileNotFoundException: F:\code\java_code\EdgeCloudSim-DeepEdge\scripts\DeepEdge\config\applications.xml (系统找不到指定的路径。) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:213) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106) at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:84) at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:180) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:654) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:150) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:861) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:825) at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:247) at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.pa
最新发布
03-24
### 解决Java中因缺少配置文件和XML文件引发的FileNotFoundException 在Java应用程序运行过程中,如果某些必要的配置文件或XML文件缺失,则可能会抛出`FileNotFoundException`异常。针对这种情况,可以通过以下方法进行处理: #### 1. 文件路径检查与验证 确保程序能够正确访问所需的文件路径。可以使用绝对路径代替相对路径以减少错误的可能性[^1]。例如,在Spring Boot项目中,通常会将配置文件放置于`src/main/resources`目录下。对于外部文件,建议将其路径硬编码或将路径作为参数传递给程序。 ```java File configFile = new File("path/to/default_config.properties"); if (!configFile.exists()) { throw new FileNotFoundException("The file 'default_config.properties' does not exist."); } ``` #### 2. 使用默认值或备份机制 当主要配置文件丢失时,提供一个备用方案是非常重要的。可以在代码中定义一组默认设置,或者加载另一个预置的配置文件[^2]。 ```java Properties properties = new Properties(); try (InputStream input = Files.newInputStream(Paths.get("path/to/default_config.properties"))) { properties.load(input); } catch (IOException e) { System.out.println("Failed to load the main configuration, using defaults..."); // Load a backup or use hardcoded values. properties.setProperty("key", "defaultValue"); } ``` #### 3. 动态资源管理 利用动态资源配置工具(如Spring Cloud Config),可以从远程服务器获取最新的配置数据,从而降低本地文件依赖的风险[^3]。这种方式特别适合分布式系统中的边缘计算场景(如EdgeCloudSim)。 ```yaml spring: cloud: config: uri: http://localhost:8888 # Remote config server URL ``` #### 4. 日志记录与通知 增强日志功能以便快速定位问题所在。每当尝试读取某个不存在的文件时,都应该生成详细的警告消息并发送邮件或其他形式的通知给管理员[^4]。 ```java Logger logger = LoggerFactory.getLogger(MyApplication.class); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new File("applications.xml")); } catch (SAXException | IOException ex) { logger.error("Error while parsing XML file.", ex); notifyAdmin(ex.getMessage()); } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值