快速幂实现pow函数(从二分和二进制两种角度理解快速幂)

文章目录

  • 迭代实现快速幂
    • 思路
      • int的取值范围
      • 快速幂
        • 从二进制的角度来理解
        • 从二分法的角度来理解
    • 代码
    • 复杂度分析
  • 进阶——超级次方
    • 思路
      • 倒序+快速幂
      • 正序+快速幂
    • 代码
    • 复杂度分析


迭代实现快速幂

实现 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


思路

想用累乘处理幂路子就走窄了,超时是板上钉钉的。

用快速幂的思想才符合题目考察的意图,在详说快速幂之前说一个很刁钻的测试用例 —— n=-2147483648

int的取值范围

我们知道:

  • int的取值范围是-2147483648-231)到2147483647231 - 1
  • -2147483648有一位符号位可用,因此2147483647是32位操作系统中最大的符号型整型常量。
  • 当出现这个刁钻的测试用例时,如果对n粗暴的取绝对值的话,int是容纳不下的,因此当 n<0 时, 需要定义一个long long类型变量 m 来存储 n 的绝对值。

快速幂

从二进制的角度来理解

  • 对于任何十进制正整数 n ,设其二进制为在这里插入图片描述
    则有:

在这里插入图片描述

  • 根据以上推导,可把计算 xn 转化为解决以下两个问题:

在这里插入图片描述

  • 因此,应用以上操作,可在循环中依次计算在这里插入图片描述
    的值,并将所有在这里插入图片描述累计相乘即可。

在这里插入图片描述
在这里插入图片描述


从二分法的角度来理解

快速幂实际上是二分思想的一种应用。

  • 二分推导: xn = xn/2 × xn/2 = (x2)n/2,令 n/2 为整数,则需要分为奇偶两种情况(设向下取整除法符号为 “//” ):

在这里插入图片描述

  • 幂获取结果:
  1. 根据二分推导,可通过循环 x = x2 操作,每次把幂从 n 降至 n//2 ,直至将幂降为 0
  2. sum=1 ,则初始状态 xn = xn × sum。在循环二分时,每当 n 为奇数时,将多出的一项 x 乘入 sum ,则最终可化至 xn = x0 * sum = sum,返回 sum 即可。

在这里插入图片描述

转自作者:jyd

算法流程:

  1. x = 0 时:直接返回 0 (避免后续 x = 1 / x 操作报错)。
  2. 初始化 sum = 1
  3. n < 0 时:把问题转化至 n≥0 的范围内,即执行 x = 1/xn = - n
  4. 循环计算:当 n = 0 时跳出;
  • n&1=1 时:将当前 x 乘入 sum(即 sum∗=x );
  • 执行 x = x2(即 x∗=x );
  • 执行 n 右移一位(即 n>>=1)。
  1. 返回 sum。

代码

class Solution {
public:double myPow(double x, int n) {if(x == 0) return 0;double sum = 1;long long m = n;if(n < 0){x = 1.0/x;m = -m;}while(m > 0){if(m & 1){sum *= x;}x *= x;m >>= 1;}return sum;}
};

复杂度分析

  • 时间复杂度 O(log_2 n): 二分的时间复杂度为对数级别。
  • 空间复杂度 O(1): sum, b 等变量占用常数大小额外空间。

进阶——超级次方

计算 ab1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。


思路

本题的难点在于对 数组b 的处理,显然无法将其转为 一个具体类型,因此解决方法无非就是 在 倒序 or 正序 遍历数组的同时进行次方处理

倒序+快速幂

本质无非就是把上面提到的二进制思想转为十进制,具体来说:

223 可以看作 2(10 * 2) + (1 * 3) ,也就是 10242 * 23

那么我们只需要在第 k倒序遍历 数组 b 的同时,记录 a 的 10k-1 次方即可。


正序+快速幂

思路源于秦九韶算法,一般地,一元 n 次多项式的求值需要经过 (n+1)∗n2\frac{(n+1)*n}{2}2(n+1)n 次乘法和 nnn 次加法,而秦九韶算法只需要 nnn 次乘法和 nnn 次加法。大大简化了运算过程。

把一个 n 次多项式:
在这里插入图片描述
改写成如下形式:
在这里插入图片描述

举个具体的例子,计算 22342^{234}2234

2234=2200+30+4=2200∗230∗24=(220∗23)10∗24=[(110∗22)10∗23]10∗242^{234} = 2^{200+30+4} = 2^{200} * 2^{30} * 2^4 = (2^{20} * 2^3)^{10} * 2^4 = [(1^{10} * 2^2)^{10} * 2^3]^{10} * 2^42234=2200+30+4=220023024=(22023)1024=[(11022)1023]1024

最后一步推导实际上是对规律的归纳,即:以 1 作为初始值 res,每次有 新项 加入时,对 res 执行 res=res10∗新项res = res^{10} * 新项res=res10 的操作。


代码

class Solution {const int MOD = 1337;int quickmul(int x, int n){ // 快速幂实现pow功能if(x == 0) return 0;int sum = 1;while(n){if(n&1) sum = (long)sum * x % MOD;n >>= 1;x = (long)x * x % MOD;}return sum;}
public:int superPow(int a, vector<int>& b) { // 倒序int res = 1, n = b.size();for(int i=n-1; i>=0; i--){res = (long)res * quickmul(a, b[i]) % MOD;a = quickmul(a, 10); // 记录当前 a 的幂}return res;}int superPow(int a, vector<int>& b) { // 正序int res = 1, n = b.size();for(int i : b){res = (long)quickmul(res, 10) * quickmul(a, i) % MOD;// 加入新项时,对 res 执行 res = res^10 * 新项 的操作}return res;}
};

复杂度分析

  • 时间复杂度O(∑i=0n−1log⁡bi\sum_{i=0}^{n-1}{\log{b_i}}i=0n1logbi): 其中 n 是数组 b 的长度,对每个 bib_ibi 计算快速幂的时间为 O(log⁡bi\log{b_i}logbi)。
  • 空间复杂度O(1): 过程由迭代实现,避免了递归方法对栈空间的占用,只需要常数的空间存放若干变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443799.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

n位数的全排列(需要考虑大数的情况)

文章目录题目思路代码题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1 输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正整数 …

正则表达式匹配(动规)

文章目录题目思路转移方程特征再探 i 和 j代码题目 请实现一个函数用来匹配包含 . 和 * 的正则表达式。模式中的字符 . 表示任意一个字符&#xff0c;而 * 表示它前面的字符可以出现任意次&#xff08;含0次&#xff09;。在本题中&#xff0c;匹配是指字符串的所有字符匹配整…

在循环递增一次的数组中插入元素

文章目录题目思路如何建立左右区间&#xff1f;如何查找最高点&#xff1f;那我们怎么判断 num 到底处于什么样的位置呢&#xff1f;如何确定插入位置&#xff1f;插入元素代码题目 给一个只循环递增一次的数组 res&#xff0c;res 满足首元素大于等于尾元素&#xff0c;形如&…

表示数值的字符串(有限状态自动机与搜索)

文章目录题目思路一代码一思路二代码二题目 思路一 考察有限状态自动机&#xff08;参考jyd&#xff09;&#xff1a; 字符类型&#xff1a; 空格 「 」、数字「 0—9 」 、正负号 「 」 、小数点 「 . 」 、幂符号 「 eE 」 。 状态定义&#xff1a; 按照字符串从左到右的…

树的子结构

文章目录题目深搜深搜代码广搜广搜代码题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 给定的树 B&#xff1a; 返回 true&#xff0c;因为…

写题过程中碰见的小问题

文章目录和--vector二维vector的初始化数组中最大的数max_element()数组所有元素之和accumulate()vector数组去重对pair类型的vector排序对元素都为正整数的vector利用sort默认的升序排列进行降序排列一维数组转二维数组size_t和int如何不用临时变量交换两个数?将类函数的形参…

LeetCode——二叉树序列化与反序列化

文章目录题目思路问题一问题二代码实现题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 设计一个算法来实现二叉树的序列化与反序列化。不限定序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序…

jsp中生成的验证码和存在session里面的验证码不一致的处理

今天在调试项目的时候发现&#xff0c;在提交表单的时候的验证码有问题&#xff0c;问题是这样的&#xff1a;就是通过debug模式查看得知&#xff1a;jsp页面生成的验证码和表单输入的页面输入的一样&#xff0c;但是到后台执行的时候&#xff0c;你会发现他们是不一样的&#…

求1~n这n个整数十进制表示中1出现的次数

文章目录题目思路代码复杂度分析题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;那么1&#xff5e;12这些整数中包含1 的数字有1、10、11和12。可得1一共出现了5次。 思路 将个位、十位……每位…

求数字序列中的第n位对应的数字

文章目录题目思路代码复杂度分析致谢题目 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中&#xff0c;第5位&#xff08;从下标0开始计数&#xff09;是5&#xff0c;第13位是1&#xff0c;第19位是4&#xff0c;等等。 请写一个函数&#xff0c…

一学就废的归并排序

文章目录其他与排序有关的文章原理代码实现复杂度分析其他与排序有关的文章 一学就废的三种简单排序【冒泡、插入、选择】 原理 归并排序&#xff08;Merge sort&#xff09;&#xff1a; 归并排序对元素 递归地 进行 逐层折半分组&#xff0c;然后从最小分组开始&#xff0c…

树状数组的相关知识 及 求逆序对的运用

文章目录树状数组概念前缀和和区间和树状数组原理区间和——单点更新前缀和——区间查询完整代码离散化sort函数unique函数去重erase函数仅保留不重复元素通过树状数组求逆序对树状数组概念 树状数组又名二叉索引树&#xff0c;其查询与插入的复杂度都为 O(logN)&#xff0c;其…

二叉搜索树相关知识及应用操作

文章目录概念查找二叉搜索树的第k大节点概念 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又名&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;——它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的…

二叉树相关知识及求深度的代码实现

文章目录树二叉树满二叉树和完全二叉树二叉树的性质代码实现求二叉树的深度树 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合。 树的相关名词&#xff1a; 根节点&#xff1a;没有前驱结点的结点。父节点&#xff0c;子节点&#xff1a…

大端小端存储模式详解及判断方法

文章目录大小端模式的概念两种模式出现原因两种模式的优劣大小端的应用情景判断机器的字节序大小端模式的概念 当我们查看数据在内存中的存储情况时&#xff0c;我们经常会发现一个很奇怪的现象&#xff0c;什么现象呢&#xff1f; int main() {int i 12;return 0; }数据在内…

Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器

文章目录物理内存物理内存分配外部碎片内部碎片伙伴系统(buddy system)slab分配器物理内存 在Linux中&#xff0c;内核将物理内存划分为三个区域。 在解释DMA内存区域之前解释一下什么是DMA&#xff1a; DMA&#xff08;直接存储器访问&#xff09; 使用物理地址访问内存&am…

Linux 内存管理 | 虚拟内存管理:虚拟内存空间、虚拟内存分配

文章目录虚拟地址空间用户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc虚拟地址空间 在早期的计算机中&#xff0c;程序是直接运行在物理内存上的&#xff0c;而直接使用物理内存&#xff0c;通常都会面临以下几种问题&#xff1a; 内存缺乏访问控制&a…

Linux | 编译原理、gcc的命令参数、自动化构建工具 make/Makefile

文章目录编译原理预处理编译汇编链接gcc的常用命令参数make 和 Makefile 的概念make的运行通配符自动化变量伪目标.PHONE:【命令】编译原理 在解释 makefile 前&#xff0c;首先解释一下 .c 文件变成 .exe 文件要经过的四个步骤——预处理、编译、汇编和链接&#xff08;参考来…

Linux | 进程概念、进程状态(僵尸进程、孤儿进程、守护进程)、进程地址空间

文章目录进程和程序操作系统如何控制和调度程序进程控制块–PCB子进程进程状态僵尸进程孤儿进程守护进程&#xff08;精灵进程&#xff09;进程地址空间引言页表进程和程序 程序&#xff1a; 一系列有序的指令集合&#xff08;就是我们写的代码&#xff09;。进程&#xff1a;…

Linux 进程控制 :进程创建,进程终止,进程等待,程序替换

文章目录进程创建进程等待程序替换进程终止进程创建 fork函数&#xff1a; 操作系统提供的创建新进程的方法&#xff0c;父进程通过调用 fork函数 创建一个子进程&#xff0c;父子进程代码共享&#xff0c;数据独有。 当调用 fork函数 时&#xff0c;通过 写时拷贝技术 来拷贝…