使用递归的幂函数

2023-11-28

我必须用Java 编写一个power 方法。它接收两个整数,它们是正数还是负数并不重要。它的复杂度应该是O(logN)。它还必须使用递归。我当前的代码得到两个数字,但我一直输出的结果为零,我不明白为什么。

import java.util.Scanner;

public class Powers {

    public static void main(String[] args) {
        float a;
        float n;
        float res;

        Scanner in = new Scanner(System.in);
        System.out.print("Enter int a ");
        a = in.nextFloat();

        System.out.print("Enter int n ");
        n = in.nextFloat();

        res = powers.pow(a, n);

        System.out.print(res);
    }

    public static float pow(float a, float n) {
        float result = 0;

        if (n == 0) {
            return 1;
        } else if (n < 0) {
            result = result * pow(a, n + 1);
        } else if (n > 0) {
            result = result * pow(a, n - 1);
        }

        return result;
    }
}

让我们从一些数学事实开始:

  • 对于正 n,aⁿ = a⨯a⨯…⨯a n 次
  • 对于负数 n,aⁿ = ⅟a⁻ⁿ = ⅟(a⨯a⨯…⨯a)。这意味着a不能为零。
  • 对于 n = 0,aⁿ = 1,即使a为零或负数。

因此,让我们从积极的 n 情况开始,并从那里开始工作。

由于我们希望我们的解决方案是递归的,因此我们必须找到一种基于较小的 n 来定义 aⁿ 的方法,并从那里开始工作。人们思考递归的通常方式是尝试找到 n-1 的解决方案,并从那里开始工作。

事实上,由于 aⁿ = a⨯(aⁿ⁻¹) 在数学上是正确的,因此简单的方法将与您创建的方法非常相似:

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    return ( a * pow(a,n-1));
}

然而,其复杂度为 O(n)。为什么?因为对于 n=0 它不执行任何乘法。当 n=1 时,它执行一次乘法。对于n=2,它调用pow(a,1),我们知道这是一次乘法,并将其乘一次,所以我们有两次乘法。递归每一步有一次乘法,总共n步。所以它是 O(n)。

In order to make this O(log n), we need every step to be applied to a fraction of n rather than just n-1. Here again, there is a math fact that can help us: an₁+n₂ = an₁⨯an₂.

This means that we can calculate aⁿ as an/2⨯an/2.

But what happens if n is odd? something like a⁹ will be a4.5⨯a4.5. But we are talking about integer powers here. Handling fractions is a whole different thing. Luckily, we can just formulate that as a⨯a⁴⨯a⁴.

So, for an even number use an/2⨯an/2, and for an odd number, use a⨯ an/2⨯an/2 (integer division, giving us 9/2 = 4).

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    if ( n % 2 == 1 ) {
        // Odd n
        return a * pow( a, n/2 ) * pow(a, n/2 );
    } else {
        // Even n
        return pow( a, n/2 ) * pow( a, n/2 );
    }
}

这实际上给了我们正确的结果(即 n 为正)。但事实上,这里的复杂度再次是 O(n) 而不是 O(log n)。为什么?因为我们计算了两次幂。这意味着我们实际上在下一个级别调用它 4 次,在下一个级别调用它 8 次,依此类推。递归步骤的数量是指数级的,因此这抵消了我们通过将 n 除以 2 所实现的假设节省。

但事实上,只需要一个小小的修正:

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    int powerOfHalfN = pow( a, n/2 );
    if ( n % 2 == 1 ) {
        // Odd n
        return a * powerOfHalfN * powerOfHalfN;
    } else {
        // Even n
        return powerOfHalfN * powerOfHalfN;
    }
}

在此版本中,我们仅调用递归一次。因此,我们可以从 64 的幂快速得到 32、16、8、4、2、1,然后就完成了。每一步只有一到两次乘法,而且只有六步。这是 O(log n)。

所有这一切的结论是:

  1. 为了获得 O(log n),我们需要在每一步都对 n 的一小部分进行递归,而不仅仅是 n - 1 或 n - 任何东西。
  2. 但这个分数只是故事的一部分。我们需要小心,不要多次调用递归,因为在一个步骤中使用多次递归调用会产生指数复杂性,而使用 n 的一小部分就可以抵消这种复杂性。

最后,我们准备好处理负数了。我们只需要得到倒数⅟a⁻ⁿ。有两点需要注意:

  • 不允许除以零。也就是说,如果 a=0,则不应执行计算。在 Java 中,在这种情况下我们会抛出异常。最合适的现成异常是 IllegalArgumentException。这是一个 RuntimeException,所以你不需要添加throws您的方法的子句。如果你能在自己的能力范围内发现或阻止这种情况的发生,那就太好了。main方法,当你读入参数时。
  • 你不能再返回一个整数(事实上,我们应该使用long,因为我们遇到了相当低的幂的整数溢出int) - 因为结果可能是小数。

所以我们定义该方法,使其返回 double。这意味着我们还必须修复powerOfHalfN。这是结果:

public static double pow(int a, int n) {
    if (n == 0) {
        return 1.0;
    }
    if (n < 0) {
        // Negative power.
        if (a == 0) {
            throw new IllegalArgumentException(
                    "It's impossible to raise 0 to the power of a negative number");
        }
        return 1 / pow(a, -n);
    } else {
        // Positive power

        double powerOfHalfN = pow(a, n / 2);
        if (n % 2 == 1) {
            // Odd n
            return a * powerOfHalfN * powerOfHalfN;
        } else {
            // Even n
            return powerOfHalfN * powerOfHalfN;
        }
    }
}

请注意,处理负 n 的部分仅在递归的顶层使用。一旦我们打电话pow()递归地,它始终为正数,并且符号在达到 0 之前不会改变。

这应该是您锻炼的充分解决方案。不过,我个人并不喜欢if在最后,所以这里是另一个版本。你能说出为什么会做同样的事情吗?

public static double pow(int a, int n) {
    if (n == 0) {
        return 1.0;
    }
    if (n < 0) {
        // Negative power.
        if (a == 0) {
            throw new IllegalArgumentException(
                    "It's impossible to raise 0 to the power of a negative number");
        }
        return 1 / pow(a, -n);
    } else {
        // Positive power
        double powerOfHalfN = pow(a, n / 2);
        double[] factor = { 1, a };
        return factor[n % 2] * powerOfHalfN * powerOfHalfN;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用递归的幂函数 的相关文章

  • cucumber.json 报告被重新运行场景报告覆盖

    我有一个具有相同技术堆栈 JAVA1 8 Cucumber JVM JUnit Maven 的 UI 测试项目和一个 API 测试项目 这两个项目都向我展示了这个问题 可能是因为两者都存在相同的依赖关系集 我使用了使用 maven sure
  • Mockito 匹配器和基元数组

    有了 Mockito 我想verify 方法调用byte 在它的参数列表中 但我没有找到如何写这个 myMethod byte 我只想要类似的东西anyByteArray 如何使用 Mockito 做到这一点 我会尝试any byte cl
  • 使用 Firebase Java API 检索/格式化数据的最佳方式

    我在用着Firebase用于数据存储Android项目 并使用Firebase Java API来处理数据 不过 我不确定我是否尽可能高效地完成此操作 并且我希望获得一些有关检索和格式化数据的最佳实践的建议 我的Firebase存储库看起来
  • 如何在 Twig 中渲染树

    我想渲染一棵深度不确定的树 孩子的孩子的孩子等 我需要递归地循环遍历数组 我怎样才能在 Twig 中做到这一点 我玩过domi27的想法 https stackoverflow com questions 8326482 how to re
  • 用Java截取网页的屏幕截图[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有没有一个免费的工具可以读取给定的网页并截取它的屏幕截图 我使用 VirtualFramebuffer 和 Firefox Binary
  • firebase android 基于类的更新不尊重字段名称的大小写

    我声明了以下类 注意大小写选择 public class User private String DisplayName private Boolean Proxy false SuppressWarnings unused public
  • 为什么需要添加工件 JSR305 才能使用 Guava 14+?

    在stackoverflow上查找信息时 我看到了一个与我类似的问题 但没有真正的答案here https stackoverflow com questions 3800033 guava r07 gwt and javax annota
  • 如何获取JavaFX的版本号?

    如何在运行时找出我正在使用哪个版本的 JavaFX 简单的方法之一就是简单地阅读javafx properties文件位于您的 JAVA HOME jre lib目录 我现在安装了 Java 1 7 u9 与之捆绑的 JavaFX 是 v2
  • AJAX(原型/java)在执行期间获取部分状态更新

    这部分模仿了AJAX 原型 php 在脚本执行期间获取部分状态更新 https stackoverflow com questions 800997 ajax prototype php getting partial status upd
  • 如何使用 Java2D 创建硬件加速图像?

    我正在尝试创建一个快速图像生成器 它可以执行大量 2d 转换和形状渲染 因此我尝试使用 BufferedImage 然后获取 Graphics2D 对象来执行所有绘图 我现在主要关心的是 make 速度非常快 所以我创建一个像这样的 Buf
  • 如何从 Unix 命令行递归解压目录及其子目录中的档案?

    The unzip命令没有递归解压缩档案的选项 如果我有以下目录结构和档案 Mother Loving zip Scurvy Sea Dogs zip Scurvy Cures Limes zip 我想将所有档案解压缩到与每个档案同名的目录
  • 在java中迭代日期

    我需要遍历一系列日期 不确定如何在 for 循环中获取第二天 我在用java util Date So plusDays 1 不能在 for 循环中用于获取下一个日期 Used date1 new Date date1 getTime 10
  • 如何统计lucene索引中每个文档的term数?

    我想知道 lucene 索引中每个文档的术语数量 我一直在 API 和互联网上搜索 但没有结果 你能帮助我吗 Lucene 的构建是为了回答相反的问题 即哪些文档包含给定术语 因此 为了获取文档的术语数量 您必须进行一些修改 第一种方法是存
  • Java 线程 JavaDoc

    我编写了一个只能在特定线程上调用的方法 是否应该将标准注释或注释添加到方法的 javadoc 中来表示这一点 不知道有任何这样的标准注释 Java 并发实践 http www javaconcurrencyinpractice com 在第
  • 错误:libXext.so.6:无法打开共享对象文件:没有这样的文件或目录[重复]

    这个问题在这里已经有答案了 运行尝试打开 ods 文件的 java 文件时出现以下错误 线程 main 中出现异常 java lang UnsatisfiedLinkError opt software jdk1 6 0 45 jre li
  • 如何在 Jersey RESTful Web 服务中放置 cookie?

    我想通过 Jersey API 将 cookie 从 PUT webservice result 放置到 POST webservice 这是我的代码 WebResource service1 client resource http te
  • android 中的 lang.NumberFormatException

    我有以下代码 除了在后台线程中从数据库读取一些值并使用这些值之外什么也不做 我使用 jar 绘制折线图 对于我用于每个数组值的折线图 问题是第三个我传递给绘制 LineChart 的构造函数的参数是 float float viteza S
  • 如何使用二叉树中的递归来完成回溯

    我正在尝试插入一个二进制节点 我的代码很复杂 没有希望挽救它 所以我计划重写它 基本上我没有考虑回溯 也没有仔细考虑算法 我正在尝试使用顺序遍历插入二进制节点 但我不明白应该如何回溯 D B E A C F 我如何搜索根 D 的左子树 然后
  • AWS Java SDK 中 DynamoDB v2 的迁移详细信息?

    有没有人对新的命名空间进行了更改 com amazonaws services dynamodbv2 以及 AWS Java SDK 1 4 2 及更高版本 中 DynamoDB 的接口 本地二级指数的发布显然需要根据1 4 2 发行说明
  • 术语“可序列化”是什么意思? [复制]

    这个问题在这里已经有答案了 不太确定我读过的定义可序列化实际上做了什么 import java io Serializable import java text StringCharacterIterator import java uti

随机推荐

  • 如何将 console.log 写入文件

    现在我使用以下方式显示信息 console log kraken id markets 但是 我想将所有发送到控制台的信息写入文件中 如何通过完成以下代码来完成此操作 use strict var ccxt require ccxt asy
  • 我可以限制通用堆栈的深度吗?

    是否有内置方法来限制 System Collection Generics Stack 的深度 那么 如果您处于最大容量 推入新元素会删除堆栈的底部吗 我知道我可以通过转换为数组并重建堆栈来做到这一点 但我认为可能已经有一个方法了 编辑 我
  • 使用 (Core)Foundation 折叠/规范化连字(例如 Æ 到 ae)

    我正在编写一个助手 它对输入字符串执行多次转换 以便创建该字符串的搜索友好表示 考虑以下场景 德语或法语文本全文搜索 The entries in your datastore contain M ller Gro mann inglet
  • 什么是三法则?

    什么是复制对象 mean 什么是复制构造函数和复制赋值运算符 我什么时候需要自己申报 如何防止我的对象被复制 介绍 C 处理用户定义类型的变量值语义 这意味着对象在各种上下文中隐式复制 我们应该理解 复制对象 的实际含义 让我们考虑一个简单
  • 为什么Git源代码中一些声明为extern和头文件的函数没有包含在source中?

    我想查看真实应用程序的源代码以了解良好的编程实践等 因此我选择了 Git 并下载了 1 8 4 版本的源代码 随机浏览各种文件后 这两个文件中的一些内容引起了我的注意 strbuf h strbuf c 这两个文件显然定义了一个 API本文
  • 使用双指针作为参数

    请找到如下所示的代码片段 include
  • 如何通过 mmap 映射内存指针进行写入以立即刷新?

    在双 ARM 处理器系统 确切地说是 Xilinx Zynq 上使用 dev mem 和 mmap 时 我遇到了似乎是缓存的问题 我的配置是不对称的 一个处理器运行 Linux 另一个处理器运行裸机应用程序 它们通过不在 Linux 虚拟内
  • Windows 8 fat 二进制文件(适用于 x86 和 ARM 的 exe)

    有谁 这里 知道 Windows 8 是否会有一种可以用 Visual Studio 2012 编译的胖 exe 并且在 ARM 和 x86 机器上都支持 我猜不会 因为据我所知 您无法创建执行 32 或 64 位代码的胖二进制文件 据我所
  • 从原始位到 jpeg,无需写入文件

    我有一个实时应用程序 它接收以 base64 编码的 jpg 图像 我不知道如何在 matlab 中显示图像 而不必将图像保存在磁盘中并随后打开它 这是我到目前为止的代码 它在显示图像之前将图像保存在磁盘中 raw base64decode
  • 如何在ash shell中保持程序在后台运行

    我需要通过 SSH 连接到嵌入式设备 启动后台程序 然后断开连接并保持后台进程运行 问题是嵌入式设备正在使用 ash shell 不是 bash 或其他任何东西 因此 nohup 和 screen 不可用 我还没有找到任何方法来断开灰烬中的
  • Android Edittext 中输入类型为数字的多行是否可能?

    当android edittext输入类型为数字时 是否可以制作多行 我已经在 xml 文件中尝试过以下内容 android inputType number textMultiLine 但这没有用 当输入类型为数字时 是否无法制作多行 请
  • Jersey 2.0 的依赖注入

    在没有任何 Jersey 1 x 知识的情况下从头开始 我很难理解如何在 Jersey 2 0 项目中设置依赖项注入 我还了解到 HK2 在 Jersey 2 0 中可用 但我似乎找不到有助于 Jersey 2 0 集成的文档 Manage
  • 在 Android 中使用网络服务发现出现内部错误

    在第一次使用示例和 NSDManager 实现期间开发者页面上的教程 应用程序成功启动发现并找到设备 不过现在好像已经坏掉了 程序启动时 经过一番初始化后 代码进入如下方法并成功运行 public void discoverServices
  • 如何向 Outlook 发送富文本格式的电子邮件?

    通过分配 text html 内容类型字符串以 HTML 格式发送电子邮件 到 Outlook 非常有效 如下所示 using MailMessage message new MailMessage message From new Mai
  • 对数组字段执行更新时,无法使用字符串字段名称 [$] 附加到数组

    rowsI 尝试对记录数组中的每个字段执行 mongodb 更新 示例架构如下 id ObjectId 508710f16dc636ec07000022 summary uid ABCDEF username bigcheese name
  • 如何有选择地导入 ES2015 模块函数,但具有命名空间?

    我正在开始使用 Rollup 和 D3 版本 4 它是用 ES2015 模块编写的 我使用传统的 D3 命名空间 d3 编写了一些代码 现在我想使用 Rollup 创建自定义捆绑包 我想使用 tree shaking 因为我可能只使用 d3
  • 如何确保 Python while 循环需要特定的时间来运行?

    我正在用 while 循环读取串行数据 但是 我无法控制采样率 代码本身似乎需要 0 2 秒才能运行 所以我知道我无法比这更快了 但我希望能够精确控制采样速度 我觉得我可以使用 睡眠 来做到这一点 但问题是 在不同的点 循环本身可能需要更长
  • 检查 Firebase 中是否存在文档并根据图标返回

    我想检查 Firebase 集合中是否存在特定文档 据此 我的应用程序应该显示一个彩色图标或灰色图标 我试图用一个返回布尔值的方法来解决这个问题 在我的构建小部件中 我调用该方法并分配正确的图标 这是我检查文档是否存在的方法 checkIf
  • 设置自定义字体的自定义字体内存泄漏

    以下用于设置自定义字体的代码会减慢我的整个应用程序的速度 我该如何修改它以避免内存泄漏并提高速度并很好地管理内存 public class FontTextView extends TextView private static final
  • 使用递归的幂函数

    我必须用Java 编写一个power 方法 它接收两个整数 它们是正数还是负数并不重要 它的复杂度应该是O logN 它还必须使用递归 我当前的代码得到两个数字 但我一直输出的结果为零 我不明白为什么 import java util Sc