使用递归的幂函数

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(使用前将#替换为@)

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

随机推荐

  • 如何将 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