快速幂算法+leetcode原题讲解

快速幂算法+leetcode原题讲解

  • 1.快速幂算法的介绍
    • 1.1 定义
    • 1.2 原理
  • 2.leetcode原题解析
    • 2.1 Pow(x, n)
    • 2.2 统计好数字的数目
    • 2.3 超级次方

1.快速幂算法的介绍

1.1 定义

顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。

1.2 原理

快速幂算法的核心思想就是每一步都把指数分成两半(类似于二分思想),而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。

让我们来思考一下:310一般我们是怎么计算的?
最简单的想法就是将一个个3全部进行累积相乘:3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,这样的话对于3^10就进行了9次乘法

对于数字相对较小时这样做可能还问题不大,那要是算的是3100 ,31000,310000……呢?

如果还是按照之前的一步一步算无疑就会造成非常大的一个时间开销,那么这个时候快速幂算法就由此引出了:
我们还是以310进行举例:
将10转化为二进制的形式 : 1010,那么310就可以写成:38 * 32, 以下为简单的快速幂算法实现:

def quickPow(x:int, n:int):
	ans=1
	while n:
		if n&1>0:
			ans*=x
		x*=x
		n//=2
	return ans

小知识:
1.n&1>0其实就等价于n&1 == 1,也可以写成n%2 == 1,这三者在逻辑上是一样的,只不过在表达形式上有所不同而已.
2.n //= 2也可以写成n >>= 1.

2.leetcode原题解析

2.1 Pow(x, n)

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn
真题点击此处:50.pow(x, n)

思路:
本题采用快速幂的方法计算 xn 的值,需要额外注意的是当n为负数时,计算的结果为1/x-n
以下为代码实现:

    def myPow(self, x: float, n: int) -> float:
        mark=0
        if n<0:
            n=-n
            mark=1
        ans=1
        while n:
            if n&1>0:
                ans*=x
            x*=x
            n//=2
        return ans if mark==0 else 1/ans

封装函数代码实现:

    def myPow(self, x: float, n: int) -> float:
        def quickPow(x,n):
            ans=1
            while n:
                if n&1>0:
                    ans*=x
                x*=x
                n//=2
            return ans
        return quickPow(x,n) if n>0 else 1/quickPow(x,-n)

2.2 统计好数字的数目

我们称一个数字字符串是 好数字 当它满足(下标从 0 开始)偶数 下标处的数字为 偶数奇数 下标处的数字为 质数 (2,3,5 或 7)。

比方说,“2582” 是好数字,因为偶数下标处的数字(2 和 8)是偶数且奇数下标处的数字(5 和 2)为质数。但 “3245” 不是 好数字,因为 3 在偶数下标处但不是偶数。
给你一个整数 n ,请你返回长度为 n 且为好数字的数字字符串 总数 。由于答案可能会很大,请你将它对 109 + 7 取余后返回 。

一个 数字字符串 是每一位都由 0 到 9 组成的字符串,且可能包含前导 0 。

真题点击此处:1922.统计好数字的数目

思路:
1.有题意可知,对于偶数处小标的数字,有2,4,6,8,10五种,对于奇数处下标的数字,有2,3,5,7四种

2.由于下标是从0开始计算的,对于长度为n的数字字符串来说,它有(n+1)// 2个偶数下标,有n // 2个奇数下标
举个例子:
当n为3时:下标为0,1,2 偶数的下标有2个,奇数下标有1个
当n为4时:下标为0,1,2,3 偶数的下标有2个,奇数下标有2个
当n为5时:下标为0,1,2,3,4偶数的下标有3个,奇数下标有2个
当n为6时:……
当n为7时:……
当n为8时:……
因此长度为n的字符串的好数字的总数为:5(n+1)//2 * 4n//2
以下为代码实现:

    def countGoodNumbers(self, n: int) -> int:
        mod=10**9+7
        def quickMul(x,n):
            ans,mul=1,x
            while n:
                if n&1>0:
                    ans*=mul%mod
                mul*=mul%mod
                n>>=1
            return ans

        return quickMul(5,(n+1)//2)*quickMul(4,n//2)%mod

2.3 超级次方

计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出
真题点击此处:超级次方

思路:
同样的,这题也是采用快速幂的方法,关于快速幂的讲解前面已经说过了,这里就不在过多赘述了,我们这里主要来分析一下当给出的b是一个数组形式的整数时具体该怎么求解:

我们以a = 2,b = [1,1,1]来举例说明一下:
这里其实就是求2111的结果,貌似好像可以用前面的快速幂直接求解,但在这里是不行的,因为b的数值可能会非常非常大,只是这里举的例子可以而已,这个例子单纯只是为了方便大家理解,接着正题:

2111可以分解为:2100 * 210 * 21, 这里我们需要对2100转换一下:
2100 = 210*10 = (210)10 ,由此我们可以推出axy可以变成(ax)y的形式,因此我们就可以得出这题的做法:
我们对b进行逆序遍历,每遍历一次就将a = a10,这样做的意义是为了可以在每一次遍历的时候可以直接乘ab[i](b[i]为当前下标为i的数组的值)。
以下为代码实现:

    def superPow(self, a: int, b: List[int]) -> int:
        mod=1337
        def quickPow(x,n):
            ans=1
            while n:
                if n&1>0:
                    ans=(ans*x)%mod  # 对 ans 取模
                x=(x*x)%mod          # 先对 x 进行取模,再进行乘法运算
                n>>=1
            return ans

        res=1
        for num in reversed(b):
            res=(res*quickPow(a,num))%mod  # 对每一步的乘积进行取模
            a=quickPow(a,10)%mod
        return res

注意:在这段代码的ans和res的计算中我们是先乘再取模,而不是先乘再取模。否则的话直接ans*=x%mod的话就会使得ans没有被取模,从而导致ans的结果错误。

你可能感兴趣的:(算法,算法,leetcode)