leetcode刷题日记- 超级次方

262 阅读3分钟
  • 题目描述:
  • 你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出

  • 示例
  • 输入:a = 2, b = [3]
    输出:8
    输入:a = 2147483647, b = [2,0,0]
    输出:1198

  • 解析:这是一个看起来很简单,实质上很复杂的题目,前提是如果你知道余数定理和快速幂的话,如果不知道这就是一个很难的题目了。再将余数定理之前,先说一下快速幂,这题就是快速幂+余数定理的结合。
  • 先说说快速幂的问题,讲快速幂之前先说一说分治的思想。比如我们要算5234的值,如果我们直接算234个5相乘,计算次数是很多的,是235次,但是我们可以采用分治的思想,比如234可以拆解成2100+310+4,那么5234=52*100+3*10+4=52*100*53*10*54,这样幂的次数就降低了很多,关于原来的式子还可以进一步分治,将每次的幂分解为10以内的式子,如52*100= ((52)10)10,以此类推,从而将每个幂将为10以内的数字,来减少运算次数,分治之后我们的计算次数为3+2+1=6次,可见运算次数大大减少。实际中的快速幂为了方便运算,采用了更为简单的二分思路。即如果我还是要算5234,计算过程如下
  • 5234=5234/2*5234/2=5117*5117
    5117=5117-1*5 (117为基数,因此要拆解为an=an-1*a)
    5116=5116/2* 5116/2
    …以此类推

  • 到这里就很明显了,实际中的快速幂大多采用递归的方式进行运算,将O(n)的时间复杂度降低为了O(log n)的时间复杂度。
  • 余数定理主要有三个
  • 1-余数的加法定理:a与b的和除以c的余数,等于a,b分别除以c的余数之和,或这个和除以c的余数(如果余数之和大于c),即(a+b)%c = (a%c+b%c)%c。

  • 2-余数的乘法定理:a与b的乘积除以c的余数,等于a,b分别除以c的余数的积,或者这个积除以c所得的余数(如果余数之积大于c),即(a*b)%c = (a%c*b%c)%c,这里有一个特例,如果a=b,那么(a*b)%c =((a%c)2)%c,假设a%b=c那么,就有(am)%b=(cm)%b,这个公式就是解决这道题的关键

  • 若两个数a,b除以同一个数m得到的余数相同,则a,b的差一定能被m整除。这个就简单提一下,本题用不到。

  • 代码如下:
class Solution:
    def superPow(self, a: int, b: List[int]) -> int:
        """
        计算a的b次幂对1337取模,b以数组的形式给出
        >>>self.superPow(2, [1, 0])
        >>>1024
        """
        res = 1
        for i in b:
            res = res%1337
            res = (res**10)*(a**i)
        return res%1337
  • 代码解读,本例用了快速幂的思想和余数定理的第二条,再每一次对res进行整体幂运算的时候对res进行取余,因为res%1337等价于(res10)%1337。