[python刷题模板] 矩阵快速幂(手写/numpy)

news/2023/12/9 16:24:57

[python刷题模板] 矩阵快速幂 (手写/numpy

    • 一、 算法&数据结构
      • 1. 描述
      • 2. 复杂度分析
      • 3. 常见应用
      • 4. 常用优化
    • 利用numpy库省去手写矩阵乘法的过程.
    • 二、 模板代码
      • 1. 斐波那契数列(错位写矩阵,手写矩阵乘法)
      • 2. 1137. 第 N 个泰波那契数(错位写矩阵,手写矩阵乘法)
      • 3. 1220. 统计元音字母序列的数目(状态机DP,用numpy)
      • 4. 552. 学生出勤记录 II(2维状态机DP展开成1维,用numpy)
      • 5. 2851. 字符串转换(KMP+矩阵快速幂)
    • 三、其他
    • 四、更多例题
    • 五、参考链接

一、 算法&数据结构

1. 描述

矩阵快速幂是一种采用数学办法降低复杂度的操作。(其实就是把递推公式变成通项公式)
  • 如果一个递推公式可以写成诸如正方形矩阵的形式,且每个相邻递推公式系数不变,那么可以提出系数变成指数写法.
    • 记为: f[i] = m*f[i-1],且m不随i变化。
    • 那么可以推出f[n] = mn * f[0]。
  • 我们可以用快速幂计算m^n,这样本需要递推计算n次的操作,就降低到了log(n)次。

  • 若f[i]每一项只有一项,那么就是上述计算过程,非常好理解,一下就推出了通项公式。
  • 但若f[i][0/1/2…]这种二维矩阵怎么办呢?其实是一样的.
  • 可以将f[i]展开写成一列的矩阵,形如:
    • f[i][0] = c00 * f[i-1][0] + c01 * f[i-1][1] …
    • f[i][1] = c10 * f[i-1][0] + c11 * f[i-1][1] …
    • 即:
    • [ f [ i ] [ 0 ] f [ i ] [ 1 ] . . ] = [ c 00 c 01 . . c 10 c 11 . . . . ] ∗ [ f [ i − 1 ] [ 0 ] f [ i − 1 ] [ 1 ] . . ] \left[\begin {array}{c} f[i][0] \\ f[i][1] \\ .. \\ \end{array}\right] = \left[\begin {array}{c} c00 &c01 &.. \\ c10 &c11 &.. \\ .. \\ \end{array}\right] *\left[\begin {array}{c} f[i-1][0] \\ f[i-1][1] \\ .. \\ \end{array}\right] f[i][0]f[i][1].. = c00c10..c01c11.... f[i1][0]f[i1][1]..
    • 可推出:
    • [ f [ n ] [ 0 ] f [ n ] [ 1 ] . . ] = [ c 00 c 01 . . c 10 c 11 . . . . ] n ∗ [ f [ 0 ] [ 0 ] f [ 0 ] [ 1 ] . . ] \left[\begin {array}{c} f[n][0] \\ f[n][1] \\ .. \\ \end{array}\right] = \left[\begin {array}{c} c00 &c01 &.. \\ c10 &c11 &.. \\ .. \\ \end{array}\right]^n * \left[\begin {array}{c} f[0][0] \\ f[0][1] \\ .. \\ \end{array}\right] f[n][0]f[n][1].. = c00c10..c01c11.... n f[0][0]f[0][1]..
  • 于是,我们只需要知道f[0]和矩阵m,就相当于知道了通项公式。

  • 要注意的是,这需要保证一件事,就是上边提到的相邻递推公式系数不变
  • 换句话说,在递推过程中,每层的转移方法是固定的,不随着当前层的/下标等因素发生if等特殊改动(这通常是状态机)。
  • 这才能保证可以提出系数,而且需要矩阵是正方形,才能做平方操作。
    • 因此这种题可能首先要写普通dp,列出转移方程,再优化。
  • 注意,手写快速幂矩阵时,np.eye()(对角线是1)的矩阵,相当于数字里的1。
  • 另外,推完f[n],通常要考虑最终答案是什么,可能是fn[i],也可能是sum(fn),等。

2. 复杂度分析

  1. 把本来 O(n) 的递推操作,优化成了 O(logn) 的数学计算。

3. 常见应用

  1. 状态转移只依赖上层的线性DP(通常是状态机)。
  2. 状态转移只依赖上2/3…层的线性DP,采用错位写法。

4. 常用优化

  1. 注意很有可能需要特判n比较小的情况,可以避免很多wa,尤其是错位写法。
  2. 由于系数矩阵需要是正方形,要注意补0。
  3. fib这种计算,注意错位补0技巧。
  4. 利用numpy库省去手写矩阵乘法的过程.

import numpy as np
if n == 1:return 5
m = np.mat([[0, 1, 1, 0, 1],[1, 0, 1, 0, 0],[0, 1, 0, 1, 0],[0, 0, 1, 0, 0],[0, 0, 1, 1, 0]
])
f0 = np.mat([[1],[1],[1],[1],[1],
])
n -= 1
while n:if n & 1:f0 = m * f0 % MODm = m * m % MODn >>= 1
return int(f0.sum()) % MOD

二、 模板代码

1. 斐波那契数列(错位写矩阵,手写矩阵乘法)

例题: 509. 斐波那契数

def matrix_multiply(a, b, MOD=10 ** 9 + 7):m, n, p = len(a), len(a[0]), len(b[0])ans = [[0] * p for _ in range(m)]for i in range(m):for j in range(n):for k in range(p):ans[i][k] = (ans[i][k] + a[i][j] * b[j][k])return ansdef matrix_pow_mod(a, b, MOD=10 ** 9 + 7):n = len(a)ans = [[0] * n for _ in range(n)]for i in range(n):ans[i][i] = 1while b:if b & 1:ans = matrix_multiply(ans, a, MOD)a = matrix_multiply(a, a, MOD)b >>= 1return ansclass Solution:def fib(self, n: int) -> int:if n == 0:return 0m = [[1, 1], [1, 0]]return matrix_pow_mod(m, n - 1)[0][0]

2. 1137. 第 N 个泰波那契数(错位写矩阵,手写矩阵乘法)

链接: 1137. 第 N 个泰波那契数

def matrix_multiply(a, b, MOD=10 ** 9 + 7):m, n, p = len(a), len(a[0]), len(b[0])ans = [[0] * p for _ in range(m)]for i in range(m):for j in range(n):for k in range(p):ans[i][k] = (ans[i][k] + a[i][j] * b[j][k])return ansdef matrix_pow_mod(a, b, MOD=10 ** 9 + 7):n = len(a)ans = [[0] * n for _ in range(n)]for i in range(n):ans[i][i] = 1while b:if b & 1:ans = matrix_multiply(ans, a, MOD)a = matrix_multiply(a, a, MOD)b >>= 1return ansclass Solution:def fib(self, n: int) -> int:if n == 0:return 0m = [[1, 1], [1, 0]]return matrix_pow_mod(m, n - 1)[0][0]

3. 1220. 统计元音字母序列的数目(状态机DP,用numpy)

链接: 1220. 统计元音字母序列的数目

MOD = 10**9+7import numpy  as np
class Solution:def countVowelPermutation(self, n: int) -> int:"""定义f[i][0~4]表示长为i+1的字符串,最后结尾是aeiou的种类数显然f[0][0:5] = [1,1,1,1,1]下边g = f[i-1]f[i][0] = 0g[0] + 1g[1] + 1g[2] + 0g[3] + 1g[4]f[i][1] = 1g[0] + 0g[1] + 1g[2] + 0g[3] + 0g[4]f[i][2] = 0g[0] + 1g[1] + 0g[2] + 1g[3] + 0g[4]f[i][3] = 0g[0] + 0g[1] + 1g[2] + 0g[3] + 0g[4]f[i][4] = 0g[0] + 0g[1] + 1g[2] + 1g[3] + 0g[4]"""if n == 1:return 5m = np.mat([[0,1,1,0,1],[1,0,1,0,0],[0,1,0,1,0],[0,0,1,0,0],[0,0,1,1,0]])f0 = np.mat([[1],[1],[1],[1],[1],])n -= 1while n:if n &1:f0 = m*f0 %MOD m = m*m%MOD n >>= 1return int(f0.sum()) %MOD

4. 552. 学生出勤记录 II(2维状态机DP展开成1维,用numpy)

链接: 552. 学生出勤记录 II

  • 这题状态维度多一层,还好是2*3,可以直接展开。
MOD = 10 ** 9 + 7
import numpy as np
class Solution:def checkRecord(self, n: int) -> int:'''f[i][0/1][0/1/2]表示i天,A=0,1,最近连续L为0/1/2时的情况'''# f = [[0]*3 for _ in range(2)]# f[0][0] = 1# for _ in range(n):#     g = [[0]*3 for _ in range(2)]#     g[0][0] = f[0][0] + f[0][1] + f[0][2]#     g[0][1] = f[0][0] #     g[0][2] = f[0][1]#     g[1][0] = f[0][0] + f[0][1] + f[0][2] + f[1][0] + f[1][1] + f[1][2]#     g[1][1] = f[1][0]#     g[1][2] = f[1][1]#     for i in range(2):#         for j in range(3):#             g[i][j] %= MOD #     f = g # return sum(sum(row) for row in f) %MOD'''0 0:00 1:10 2:21 0:31 1:41 2:5f[i][0] = [1, 1, 1, 0, 0, 0]f[i][1] = [1, 0, 0, 0, 0, 0]f[i][2] = [0, 1, 0, 0, 0, 0]f[i][3] = [1, 1, 1, 1, 1, 1]f[i][4] = [0, 0, 0, 1, 0, 0]f[i][5] = [0, 0, 0, 0, 1, 0]f[n] = m^n * [[1],[0],[0],[0],[0],[0]]'''f0 = np.mat([[1],[0],[0],[0],[0],[0]])m = np.mat([[1, 1, 1, 0, 0, 0],[1, 0, 0, 0, 0, 0],[0, 1, 0, 0, 0, 0],[1, 1, 1, 1, 1, 1],[0, 0, 0, 1, 0, 0],[0, 0, 0, 0, 1, 0],])while n:if n & 1:f0 = m * f0 %MOD m = m * m %MOD n >>= 1return int(f0.sum()%MOD)       

5. 2851. 字符串转换(KMP+矩阵快速幂)

链接: 2851. 字符串转换

  • 难点在于想到kmp,以及状态转移。
MOD = 10 ** 9 + 7
class Kmp:"""kmp算法,计算前缀函数pi,根据pi转移,复杂度O(m+n)"""def __init__(self, t):"""传入模式串,计算前缀函数"""self.t = tn = len(t)self.pi = pi = [0] * nj = 0for i in range(1, n):while j and t[i] != t[j]:j = pi[j - 1]  # 失配后缩短期望匹配长度if t[i] == t[j]:j += 1  # 多配一个pi[i] = jdef find_all_yield(self, s):"""查找t在s中的所有位置,注意可能为空"""n, t, pi, j = len(self.t), self.t, self.pi, 0        for i, v in enumerate(s):while j and v != t[j]:j = pi[j - 1]if v == t[j]:j += 1if j == n:yield i - j + 1j = pi[j - 1]def find_one(self, s):"""查找t在s中的第一个位置,如果不存在就返回-1"""for ans in self.find_all_yield(s):return ansreturn -1
def matrix_multiply(a, b, MOD=10**9+7):m, n, p = len(a), len(a[0]), len(b[0])ans = [[0]*p for _ in range(m)]for i in range(m):for j in range(n):for k in range(p):ans[i][k] = (ans[i][k]+a[i][j] * b[j][k]) %MODreturn ans 
def matrix_pow_mod(a, b, MOD=10**9+7):n = len(a)ans = [[0]*n for _ in range(n)]for i in range(n):ans[i][i] = 1 while b:if b & 1:ans = matrix_multiply(ans, a, MOD)a = matrix_multiply(a, a, MOD)b >>= 1return ansclass Solution:def numberOfWays(self, s: str, t: str, k: int) -> int:if t not in s+s:return 0n = len(s)c = len(list(Kmp(t).find_all_yield(s+s[:-1])))        m = [[c-1, c],[n-c,n-1-c]]m = matrix_pow_mod(m, k)return m[0][s != t]    

三、其他

  1. 一定别忘了取模。
  2. 通常小数据特判。

四、更多例题

  • 790. 多米诺和托米诺平铺

五、参考链接

  • 链接: KMP + 矩阵快速幂优化 DP(附题单)Python/Java/C++/Go/JS

http://www.ppmy.cn/news/1123087.html

相关文章

Echarts散点图筛选新玩法dataZoom

目录 前言 一、引入Echarts5.4.3 二、新建index.html 三、绑定Echarts展示元素 四、初始数据绑定 五、option设置 六、效果展示 七、参数说明 总结 前言 如果您在日常的工作当中也会遇到如下场景,需要在线对已经展示出来的图表进行进一步的筛选&#xff0c…

从零学习开发一个RISC-V操作系统(二)丨GCC编译器和ELF格式

本篇文章的内容 一、GCC(GUN Compiler Collection)1.1 GCC的命令格式1.2 GCC的主要执行步骤1.3 GCC涉及的文件类型 二、ELF简介2.1 ELF文件格式图2.2 ELF文件处理的相关工具2.3 练习 本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习笔记&…

RASP hook插桩原理解析

javaagent技术,实现提前加载类字节码实现hook,插桩技术 javassist技术ASM字节码技术 像加载jar,有两种方式 premain启动前加载:每次变动jar包内容,都需要进行重启服务器利用java的动态attch加载原理,采用pr…

华为云Stack的学习(七)

八、华为云Stack存储服务介绍 1.云硬盘EVS 云硬盘(Elastic Volume Service,EVS),又名磁盘,是一种虚拟块存储服务,主要为ECS(Elastic Cloud Server)和BMS(Bare Metal Se…

vuereact质检工具(eslint)安装使用总结

1、ESLint ESLint工具主要类似java中的checkStyle和findbugs,是检查代码样式和逻辑规范的工具。 1.1、ESLint安装流程 打开VSCode软件,打开扩展中心,下载ESLint插件 图1.1 点击后面的install按进行安装,如图1.2所示&#xff1…

如何安全传输存储用户密码?(程序员必备)

前言 我们开发网站或者APP的时候,首先要解决的问题,就是「如何安全传输和存储用户的密码」。一些大公司的用户数据库泄露事件也时有发生,带来非常大的负面影响。因此,如何安全传输存储用户密码,是每位程序员必备的基础…

uniapp 微信小程序之隐私协议开发

uniapp 微信小程序之隐私协议开发 官网通知:https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/PrivacyAuthorize.html 1、配置 __usePrivacyCheck__: true;位置 manifest.json : "mp-weixin":{"__usePrivacyCh…

暴力递归转动态规划(七)

题目 LeetCode原题-最长回文子序列 给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 示例 1: 输入&a…

Unity用相机实现的镜子效果

首先登场 场景中的元素 mirror是镜子,挂着我们的脚本,Quad是一个面片。Camera是用来生成RenderTexture给面片的。里面的test1是我用来调试位置的球。 镜子size是大小,x是-2,为了反转一下贴图 相机直接可以禁用掉,用…

深入理解JVM虚拟机第十篇:两个Class对象是否来源于一个类文件的标准以及类的主动使用与被动使用

文章目录 前言 一:两个Class对象是否是一个类的标准 1:JVM对于类加载器信息的记录

Linux---su:鉴定故障

问题来源:在使用xshell操作Linux命令时,切换root权限报错 可能是未设置密码:输入 sudo password 重新设置一下密码即可 本人犯的错: 因为在Linux下输入密码是没有显示的,然后我的键盘num键没开!!!(也就是输入数字开关的键盘),导致我认为我的密码输进去了,给我整懵逼了&#x…

Jenkins学习笔记4

配置构建流程: Jenkins任务创建: 1)创建新任务: 把这个Accept first connection改成 No Validation。问题得到解决。 说明下,要确认下主分支的名称是master还是main。 构建触发器这块暂时没有需要配置的。 传输文件…

全国职业技能大赛云计算--高职组赛题卷①(私有云)

全国职业技能大赛云计算--高职组赛题卷①(私有云) 第一场次题目:OpenStack平台部署与运维任务1 基础运维任务(5分)任务2 OpenStack搭建任务(15分)任务3 OpenStack云平台运维(15分&am…

Django之初入门

一)Django简介 1.简介 Django是一个开源的Python Web框架,它以简洁高效的方式帮助开发者构建复杂的Web应用程序。Django采用了MVC(Model-View-Controller)的架构模式,通过强大的工具和功能,提供了一套完整…

物联网边缘网关

物联网边缘网关 边缘网关的定义边缘网关的分类边缘计算网关平台相关产品有哪些 百度边缘计算平台(BIE)华为边缘计算平台(IEF)产品应用拓扑图产品价格区间

MySQL安全问题

MySQL安全性是确保数据库系统不受未经授权的访问、数据泄露和其他恶意活动的重要方面。以下是一些保护MySQL数据库的安全措施和最佳实践: 更新和维护MySQL: 始终使用最新版本的MySQL,因为新版本通常包含安全性修复和改进。此外,定…

linux、windows的pip一键永久换源[清华源、中科大、豆瓣、阿里云]

前言 本文概述:linux、windows操作系统一键将pip下载源永久设置为国内下载源,避免了使用临时源需要到处找镜像地址的麻烦。 作者介绍:作者本人是一名人工智能炼丹师,目前在实验室主要研究的方向为生成式模型,对其它方向…

Unity 3D 简易对象池

依赖于UniTask(访问Github)依赖于Addressable资源管理(通过UPM安装) using Cysharp.Threading.Tasks; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceMana…

在Linux中安装nginx-1.20.1+php-7.4.28(增加扩展)

NginxPHP安装在公网IP为x.x.x.x的服务器上 需要下载安装的软件版本:nginx-1.20.1php-7.4.28 需要增加的PHP扩展如下: 在编译安装php-7.4.28时加上的--enable-pcntl; 单独下载安装的Wxwork_finance_sdk;(在编译安装…

linux离线安装make

一、下载rpm包 https://pkgs.org/search/?qmake 二、拷贝至服务器 三、安装make rpm -ivh make-3.82-24.el7.x86_64.rpm四、查看是否安装成功 make -v
最新文章