Java数据结构之快速幂的实现

目录
  • 引入
  • 具体方法
  • 代码实现
  • 题目
  • 矩阵快速幂
    • 斐波那契数列
    • 第 N 个泰波那契数
    • 统计元音字母序列的数目

引入

快速幂是用来解决求幂运算的高效方式。

例如我们要求 x 的 90 次方,一般的方法可以通过一个循环,每次乘一个 x,循环 90 次之后就可以得到答案,时间复杂度为 O(n),效率较低。而通过快速幂,我们可以在 O(log(n)) 的时间复杂度内完成该运算。

具体方法

我们可以通过二进制的视角来看待幂运算。

要计算的是 xn,把 n 以二进制的形式展开。

所以,只需要使用一个循环求 n 的二进制的每一位,每次一循环中,如果该二进制位为 0,则不需要乘;如果该二进制位为 1,则需要乘 x。且每一次循环中都执行 x *= x,可以一次获取 x 的不同幂次。

代码实现

public static double getPower(double x, int n) {
      if(x == 0) return 0;
      if(n < 0) {     // x^(-a) = (1/x)^a
          x = 1/x;
          n = -n;
      }
      double res = 1.0;
      while(n > 0) {
          if((n & 1) == 1) {
              res *= x;
          }
          x *= x;
          n >>= 1;
      }
      return res;
}

题目

Pow(x, n)题目内容如下

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn )。

示例 1:

输入:x = 2.00000, n = 10

输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3

输出:9.26100

示例 3:

输入:x = 2.00000, n = -2

输出:0.25000

解释:2-2 = 1/22 = 1/4 = 0.25

提示:

-100.0 < x < 100.0

-231 <= n <= 231-1

-104 <= xn <= 104

实现代码

class Solution {
    public double myPow(double x, int n) {
        long exp = n;              // 特殊处理:补码表示的负数最小值的相反数超过 Integer 表示范围,故提高数据表示范围
        if(x == 0.0) return 0.0;
        if(n < 0) {
            x = 1/x;
            exp = -exp;
        }
        double res = 1.0;
        while(exp > 0) {
            if((exp & 1) == 1) res *= x;
            x *= x;
            exp >>= 1;
        }
        return res;
    }
}

矩阵快速幂

斐波那契数列

解:找到一种递推关系,满足矩阵乘法。

f(n) = f(n - 1) + f(n - 2),将其依赖的状态存成列向量

目标值 f(n) 所在矩阵为:

下面关键就是找到这两个矩阵直接满足的一个关系,知道系数矩阵 mat

则令

我们就成功找到了系数矩阵。

下面可以求得递推关系式:

对于 mat 可以通过快速幂求得结果。

class Solution {
    int mod = (int)1e9+7;
    public int fib(int n) {
        if(n <= 1) return n;
        long[][] mat = new long[][]{
            {1, 1},
            {1, 0}
        };
        long[][] ans = new long[][]{
            {1},
            {0}
        };
        int count =  n - 1;
        while(count > 0) {
            if((count & 1) == 1) ans = mul(mat, ans); // 注意矩阵乘法顺序,不满足交换律
            mat = mul(mat, mat);
            count >>= 1;
        }
        return (int)(ans[0][0] % mod);
    }
    public long[][] mul(long[][] a, long[][] b) {
        // 矩阵乘法,新矩阵的行数 = a的行数rowa,列数 = b的列数colb
        // a矩阵的列数 = b矩阵的行数 = common
        int rowa = a.length, colb = b[0].length, common = b.length;
        long[][] ans = new long[rowa][colb];
        for (int i = 0; i < rowa; i++) {
            for (int j = 0; j < colb; j++) {
                for (int k = 0; k < common; k++) {
                    ans[i][j] += a[i][k] * b[k][j];
                    ans[i][j] %= mod;
                }
            }
        }
        return ans;
    }
}

第 N 个泰波那契数

解:

对于 mat 的幂运算可以使用快速幂

class Solution {
    public int tribonacci(int n) {
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        int[][] mat = new int[][]{
            {1, 1, 1},
            {1, 0, 0},
            {0, 1, 0}
        };
        int[][] ans = new int[][]{
            {1},
            {1},
            {0}
        };
        int count = n - 2;
        while(count > 0) {
            if((count & 1) == 1) ans = mul(mat, ans);
            mat = mul(mat, mat);
            count >>= 1;
        }
        return ans[0][0];
    }
    public int[][] mul(int[][] a, int[][] b) {
        int rowa = a.length;
        int colb = b[0].length;
        int common = b.length;
        int[][] ans = new int[rowa][colb];
        for(int i = 0; i < rowa; i++) {
            for(int j = 0; j < colb; j++) {
                for(int k = 0; k < common; k++) {
                    ans[i][j] += a[i][k] * b[k][j];
                }
            }
        }
        return ans;
    }
}

统计元音字母序列的数目

提示:1 <= n <= 2 * 10^4

解:题目中给定的字符的下一个字符的规则如下:

字符串中的每个字符都应当是小写元音字母 (‘a’,‘e’,‘i’,‘o’,‘u’);

  • 每个元音 ‘a’ 后面都只能跟着 ‘e’;
  • 每个元音 ‘e’ 后面只能跟着 ‘a’ 或者是 ‘a’;
  • 每个元音 ‘i’ 后面不能再跟着另一个 ‘i’;
  • 每个元音 ‘o’ 后面只能跟着 ‘i’ 或者是 ‘u’;
  • 每个元音 ‘u’ 后面只能跟着 ‘a’;

以上等价于每个字符的前一个字符的规则如下:

  • 元音字母 ‘a’ 前面只能跟着 ‘e’,‘i’,‘u’;
  • 元音字母 ‘e’ 前面只能跟着 ‘a’,‘i’;
  • 每个元音 ‘i’ 前面只能跟着 ‘e’,‘o’;
  • 每个元音 ‘o’ 前面只能跟着 ‘i’;
  • 每个元音 ‘u’ 前面只能跟着 ‘o’,‘i’;

我们设 f[i][j] 代表当前长度为 i 且以字符 j 为结尾的字符串的数目,其中在此 j=0,1,2,3,4 分别代表元音字母 ‘a’,‘e’,‘i’,‘o’,‘u’

class Solution {
    long mod = 1_000_000_007;
    public int countVowelPermutation(int n) {

        long[][] mat =
        {
            {0, 1, 0, 0, 0},
            {1, 0, 1, 0, 0},
            {1, 1, 0, 1, 1},
            {0, 0, 1, 0, 1},
            {1, 0, 0, 0, 0}
        };
        long[][] ans = {
            {1},{1},{1},{1},{1}
        };
        int count = n - 1;

        while(count > 0) {
            if((count & 1) == 1) ans = mul(mat, ans);
            mat = mul(mat, mat);
            count >>= 1;
        }
        long res = 0;
        for(int i = 0; i < 5; i++) {
            res += ans[i][0];
        }
        return (int)(res % mod);
    }
    public long[][] mul(long[][] a, long[][] b) {
        int rowa = a.length;
        int colb = b[0].length;
        int common = b.length;
        long[][] ans = new long[rowa][colb];
        for(int i = 0; i < rowa; i++) {
            for(int j = 0; j < colb; j++) {
                for(int k = 0; k < common; k++) {
                    ans[i][j] += a[i][k] * b[k][j];
                    ans[i][j] %= mod;
                }
            }
        }
        return ans;
    }
}

以上就是Java数据结构之快速幂的实现的详细内容,更多关于Java快速幂的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java语言实现快速幂取模算法详解

    快速幂取模算法的引入是从大数的小数取模的朴素算法的局限性所提出的,在朴素的方法中我们计算一个数比如5^1003%31是非常消耗我们的计算资源的,在整个计算过程中最麻烦的就是我们的5^1003这个过程 缺点1:在我们在之后计算指数的过程中,计算的数字不都拿得增大,非常的占用我们的计算资源(主要是时间,还有空间) 缺点2:我们计算的中间过程数字大的恐怖,我们现有的计算机是没有办法记录这么长的数据的,所以说我们必须要想一个更加高效的方法来解决这个问题 当我们计算AB%C的时候,最便捷的方法就是调用Ma

  • C语言快速幂取模算法小结

    本文实例汇总了C语言实现的快速幂取模算法,是比较常见的算法.分享给大家供大家参考之用.具体如下: 首先,所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余).在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快.计算范围更大的算法,产生了快速幂取模算法.我们先从简单的例子入手:求abmodc 算法1.直接设计这个算法: int ans = 1; for(int i = 1;i<=b;i++) { ans = ans * a; } ans = ans %

  • C++快速幂与大数取模算法示例

    一.快速幂 其实就是求(a^b)% p ,(其中a,b,p都比较大在int范围内)这类问题. 首先要知道取余的公式: (a*b)%p=(a%p*b%p)%p . 那么幂不就是乘机的累积吗,由此给出代码: int fast(int a,int b,int p) { long long a1=a,t=1; while(b>0) { if(b&1) /如果幂b是奇数多乘一次,因为后边会除2变偶数,(7/2=3) t=(t%p)*(a1%p)%p; a1=(a1%p)*(a1%p)%p; b/=2;

  • Python实现求解斐波那契第n项的解法(包括矩阵乘法+快速幂)

    斐波那契数列 首先我们来定义一下斐波那契数列: 即数列的第0项: 算法一:递归 递归计算的节点个数是O(2ⁿ)的级别的,效率很低,存在大量的重复计算. 比如: f(10) = f(9) + f(8) f(9) = f(8) + f(7) 重复 8 f(8) = f(7) + f(6) 重复 7 时间复杂度是O(2ⁿ),极慢 def F1(n): if n <= 1: return max(n, 0) # 前两项 return F1(n-1)+F1(n-2) # 递归 算法二:记忆化搜索 开一个大

  • Java数据结构之快速幂的实现

    目录 引入 具体方法 代码实现 题目 矩阵快速幂 斐波那契数列 第 N 个泰波那契数 统计元音字母序列的数目 引入 快速幂是用来解决求幂运算的高效方式. 例如我们要求 x 的 90 次方,一般的方法可以通过一个循环,每次乘一个 x,循环 90 次之后就可以得到答案,时间复杂度为 O(n),效率较低.而通过快速幂,我们可以在 O(log(n)) 的时间复杂度内完成该运算. 具体方法 我们可以通过二进制的视角来看待幂运算. 要计算的是 xn,把 n 以二进制的形式展开. 所以,只需要使用一个循环求 

  • Java实现的快速查找算法示例

    本文实例讲述了Java实现的快速查找算法.分享给大家供大家参考,具体如下: 快速查找算法,可以根据想要找的是第几个大的数,每次循环都能固定下来一个数在数组完整排完序之后的位置,每次循环都能定一个数的位置,如果当前固定的数的位置和用户要找的第几个数匹配,则就直接返回.例如我要找第二大的数,如果循环一次固定的数的下标是1,那就是当前需要找的数. 代码如下: // 快速查找算法 public static int quickSelect(int[] arr, int selectIndex) { in

  • java数据结构排序算法之归并排序详解

    本文实例讲述了java数据结构排序算法之归并排序.分享给大家供大家参考,具体如下: 在前面说的那几种排序都是将一组记录按关键字大小排成一个有序的序列,而归并排序的思想是:基于合并,将两个或两个以上有序表合并成一个新的有序表 归并排序算法:假设初始序列含有n个记录,首先将这n个记录看成n个有序的子序列,每个子序列长度为1,然后两两归并,得到n/2个长度为2(n为奇数的时候,最后一个序列的长度为1)的有序子序列.在此基础上,再对长度为2的有序子序列进行亮亮归并,得到若干个长度为4的有序子序列.如此重

  • Java数据结构-HashMap详解

    Java数据结构-HashMap 1. HashMap数据结构 没有哈希冲突时,为数组,支持动态扩容 哈希冲突时,分为两种情况: 1.当冲突长度小于8或数组长度小于64(MIN_TREEIFY_CAPACITY默认值为64)时,为数组+链表(Node) 2.当冲突长度大于8时,为数组+红黑树/链表(TreeNode). 红黑树用于快速查找,链表用于遍历. 2. 红黑树 HashMap中的TreeNode是红黑树的实现. TreeNode几个方法 1. 左旋转 static <K,V> Tree

  • Java数据结构学习之二叉树

    1 背景知识:树(Tree) 在之前的笔记中,我们介绍的链表.栈.队列.数组和字符串都是以线性结构来组织数据的.本篇笔记要介绍的树采用的是树状结构,这是一种非线性的数据组织形式. 树结构由节点和边构成,且不存在环.我们曾在线性表型的数据结构中介绍过循环链表和循环队列,这两种数据结构使得存储容器中的元素形成一个闭环,具体可参看"数据结构学习笔记"系列的相关博文,链接贴在下面: 链表:https://www.jb51.net/article/215278.htm 队列:https://ww

  • java数据结构-堆实现优先队列

    目录 一.二叉树的顺序存储 1.堆的存储方式 2.下标关系 二.堆(heap) 1.概念 2.大/小 根堆 2.1小根堆 2.2大根堆 3.建堆操作 3.1向下调整 4.入队操作 4.1向上调整 4.2push 入队的完整代码展示 5.出队操作 5.1pop 出队代码完全展示 6.查看堆顶元素 7.TOK 问题 7.1TOPK 8.堆排序 文章内容介绍大纲 一.二叉树的顺序存储 1.堆的存储方式 使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中. 一般只适合表示完全二叉树,因为非完

  • Java数据结构彻底理解关于KMP算法

    大家好,前面的有一篇文章讲了子序列和全排列问题,今天我们再来看一个比较有难度的问题.那就是大名鼎鼎的KMP算法. 本期文章源码:GitHub源码 简介 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法).KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息.KMP算法

  • java数据结构ArrayList详解

    目录 简介 成员变量 构造函数 无参构造函数 构造一个初始容量大小为 initialCapacity 的 ArrayList 使用指定 Collection 来构造 ArrayList 的构造函数 主要操作方法解析 add 操作 remove 操作 get操作 迭代器 iterator 总结 简介 ArrayList 是 java 集合框架中比较常用的数据结构了.继承自 AbstractList,实现了 List 接口.底层基于数组实现容量大小动态变化.允许 null 的存在.同时还实现了 Ra

  • Java数据结构之顺序表篇

    目录 一.线性表 二.顺序表 1.概念及结构 2.顺序表的实现 打印顺序表 获取顺序表的有效长度 在pos位置新增元素 判断是否包含某个元素 查找某个元素对应的位置 获取/查找pos位置的元素 给pos位置的元素设为value 删除第一次出现的关键字key 清空顺序表 3.顺序表的优.缺点 三.顺序表的实现代码汇总 一.线性表 线性表( linear list ) 是 n 个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见 的线性表:顺序表.链表.栈.队列.字符串

随机推荐