【蓝桥杯】简单数论2——快速幂矩阵快速幂

news2024/11/13 15:36:15

1、快速幂

 

1.1运算模

定义:模运算为a除以m的余数,记为a mod m,a mod m = a % m

  • 模运算是大数运算中的常用操作:如果一个数太大,无法直接输出,或者不需要直接输出,可以把它取模后,缩小数值再输出。
  • Python虽然能直接计算大数,不用担心数据溢出,但是大数乘法太耗时,所以也常用取模来缩小数值
  • 一个简单应用,判断奇偶:a%2==0,a是偶数;a%2==1,a是奇数

例题一:刷题统计  

2022年第十三届省赛,lanqi ao0J题号209

问题描述

小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 a 道题目, 周六和周日每天做 b 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 n 题?

输入格式

输入一行包含三个整数 a,b 和 n.

输出格式

输出一个整数代表天数。

样例输入

10 20 99

样例输出

8

思路

 求余数的简单题,利用求余,把计算复杂度降为0(1)。

a, b, n =map (int, input ().split ())
week = a*5+b*2          # 每周一共做题数
days = (n//week)*7      # 整周的做题天数(不满一周的不计)
n %= week               # 最后一周做题数

if n <= a*5: # 最后一周在前五天完成
    #Python的三目运算
    days += n//a+(1 if n%a>0 else 0)  # 总天数=整周天数+最后一周天数
else:        # 最后一周在后两天完成
    days += 5 # 把前五天先加上
    n -= a*5  # 把最后一周后两天的做题数
    days += n//b+(1 if n%b>0 else 0)  # 加上最后一周后两天做题天数(先整除再取余)
print(days)

1.2快速幂

  •  幂运算a^n,当n很大时,如果一个个地乘,时间是O(n)的,速度很慢,此时可以用快速幂,在O(logn)的时间内算出来。
  • 快速幂的一个解法:分治法,算a^2,然后再算(a^2)^2,..,一直算到a^n,代码也容易写。
  • 标准的快速幂:用位运算实现。基于位运算的快速幂,原理是倍增

1.3快速幂原理

a^{11}为例说明如何用倍增法做快速幂。
(1)幂次与二进制的关系。把a^{11}分解成幂a^8,a^2,a^1的乘积:a^{11}=a^{8+2+1}=8^2*a^2*a^1。其
人中a^1,a^2,a^4,a^8..的幂次都是2的倍数,所有的幂a^i都是倍乘关系,逐级递推,代码: a*=a
(2)幂次用二进制分解。如何把11分解为8+2+1?利用数的二进制的特征,n=11_{10}=1011_2=2^3+2^1+2^0=8+2+1,把n按二进制处理就可以。

(3)如何跳过那些没有的幂次?例如1011需要跳过a^4。做个判断,用二进制的位运算实现:

  • n & 1      取n的最后一位,并且判断这一位是否需要跳过
  • n >>=1   把n右移一位,目的是把刚处理过的n的最后一位去掉
  • 直到全部移除(n=0)才退出

幂运算的结果往往很大,一般会先取模再输出

根据取模的性质有:a^n\ mod\ m = (a\ mod\ m)^n mod\ m

例题二:快速幂(模板题)

lanqiao0J题号1514

题目描述

输入 b,p,k 的值,求 (b^p)\ mod\ k 的值。其中 2≤b,p,k≤10^9 。

输入描述

三个整数 b,p,k。

输出描述

输出(b^p)\ mod\ k=s,s 为运算结果。

输入输出样例

输入

2 10 9

输出

7

代码 

def fastPow(a, n,mod):
    ans = 1  
    while n: # 把n看成二进制,逐个处理它的最后一位
        if n&1: 
            ans = ans * a % mod     # 如果n的最后一位是1,表示这个地方需要乘
        a = a*a % mod                   # 递推: a^2 --> a^4 --> a^8--> a^16...
        n >>= 1                         # n右移一位,把刚处理过的n的最后一位去掉
    return ans
b, p,k = map(int,input ().split())
print(fastPow(b, p,k))

第五六行代码是 取模的性质:a^n\ mod\ m = (a\ mod\ m)^n mod\ m ,先括号里面再外面

例题三:RSA解密

2019年第十届省赛,填空题,lanqiao0J题号603

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

RSA 是一种经典的加密算法。它的基本加密过程如下。

首先生成两个质数 p,q,令 n=p⋅q,设 d 与 (p−1)⋅(q−1) 互质,则可找到 e 使得 d⋅e 除 (p−1)⋅(q−1) 的余数为 11

n,d,e 组成了私钥,n,d 组成了公钥。

当使用公钥加密一个整数 X 时(小于 n),计算 C= X^d mod n,则 C 是加密后的密文。

当收到密文 C 时,可使用私钥解开,计算公式为 X=C^e mod n。

例如,当 p=5,q=11,d=3 时,n=55,e=27。

若加密数字 24,得 24^3 mod 55=19。 解密数字 19,得 19^{27} mod 55=24。

现在你知道公钥中 n=1001733993063167141,d=212353,同时你截获了别人发送的密文 C=20190324,请问,原文是多少?

题解

(1)求p、q        (两个质数p、q,n=pq)
        先求n的素因子p和q。由于n只有这2个因子,没有别的因子,所以p和q必然有一个小于\sqrt{n}找到一个,另一个就知道了。用暴力法求p、q,用i循环从2到\sqrt{n}(不能从1开始,因为1和本身就是一组因子对)一个个试。若n除以i的余数是0,i就是因子。
        循环次数是\sqrt{n}= \sqrt{1001733993063167141} = 1000866621,即十亿次计算。得到: p=891234941、q= 1123984201。RSA算法的p、q是1024位。从n=pq算出p、q,C++代码的执行时间约10秒,Python需要几分钟!

from math import *
n = 1001733993063167141
k = int(sqrt (n))
for i in range(2,k+1):# k+1是因为int是向下取整
    if n%i== 0: print(i, n//i)

(2)求e·        (找到e使得de除(p-1)-(q-1)的余数为1)
下面代码打印出e = 823816093931522017。注意e有很多个,取最小的一个就行了。
(3)求X = C^e mod n
本题考了快速幂

# e,n,C已知,求原文X
def fastPow(a, b, mod):
    ans = 1
    while b:
        if b&1:ans = ans * a % mod
        a = a*a % mod
        b>>=1
    return ans
n = 1001733993063167141
e = 823816093931522017
C = 20190324
print (fastPow(C, e,n))
#打印结果:579706994112328949

2、矩阵快速幂 

2.1矩阵乘法

  • 一个m行n列(记为m×n)的矩阵,用二维数组matrix[ ][ ]来存储,matrix[i][i]是第i行第j列的元素。
  • 两个矩阵A、B相乘,要求A的列数等于B的行数,设A是m×n,B是n×u,那么乘积C=AB的行和列是m×u的。

矩阵乘法C=AB:C[i,j]=\sum _{k=1}^{n}A[i][k]B[k][i] 

for i in range(1,m+1):          #i、j、k的先后顺序没关系,因为对于c[][]来说都一样
    for j in range(1,u+1):
        for k in range(1, n+1) : 
            c[i][j] += a[i][k] * b[k][j])

例题四:矩阵相乘(模板题) 

题目描述

输入两个矩阵,输出两个矩阵相乘的结果。

输入描述

输入的第一行包含三个正整数 N,M,K,表示一个 N×M的矩阵乘以一个的矩阵乘以一个M×K的矩阵。接下来的矩阵。接下来N行,每行M个整数,表示第一个矩阵。再接下来的个整数,表示第一个矩阵。再接下来的M行,每行K 个整数,表示第二个矩阵。

0<N,M,K≤100, 0≤ 矩阵中的每个数 ≤1000。

输出描述

输出有 N 行,每行 K 个整数,表示矩阵乘法的结果。

输入输出样例

输入

2 1 3
1
2
1 2 3

输出

1 2 3
2 4 6

代码 

n, m, k = map (int, input ().split())
A = []
B = []
C= [[0]*k for i in range(n)]  # 用来存相乘后的矩阵
# 读入矩阵
for i in range(n): A.append(list(map(int, input().split())))    # 每次读一行,存到A
for i in range(m): B.append(list(map(int, input().split())))
# 矩阵相乘
for i in range(n):
    for j in range(m):
        for l in range(k):C[i][l] += A[i][j]*B[j][l]
# 输出矩阵
for i in range(n):
    for j in range(k):
        print(C[i][j],end="")
    print()     #换行

2.2矩阵快速幂 

若矩阵A是N×N的方阵,行数和列数都是N,它可以自乘,把n个A相乘记为A^n.

矩阵的幂可以用快速幂来计算,从而极大提高效率,是常见的考题。

矩阵快速幂的复杂度:O(N^3logn)。其中N3是矩阵乘法,logn是快速幂。

出题的时候一般会给一个较小的N和一个较大的n,以考核快速幂的应用。
矩阵快速幂的原理和代码,与普通快速幂几乎一样。

 2.3矩阵快速幂 和 快速幂 代码对比 

def multi(A,B):
    m1,n1 = len(A), len(A[0])    # len(矩阵):行数,len(矩阵[0]):列数
    m2,n2 = len(B),len(B[0])
    if n1 != m2: return None
    C= [[0] *n2 for i in range(m1)]
    for i in range(m1):
        for k in range(n1) :
            for j in range(n2):
                C[i][j] += A[i][k]* B[k][j]
                # 若需要取模运算:C[i][j] = (CLi][j] + A[i][k]* B[k][j])% mod
    return C
def power(A,n): # A是矩阵
    N = len(A)
    ans = [[0]* N for i in range(N)]    # 初始化答案矩阵
    for i in range (N): ans[i][i] = 1   # 单位矩阵(对角线为1,其他为0)
    while n:
        if n % 2: # 若最后一位为1,等价于n&1
            ans = multi(ans,A)
        A = multi(A,A)  # 倍增
        n //= 2 # 右移一位,等价于n >>= 1
    return ans
def fastPow(a, n, mod):
    ans = 1
    while n:
        if n&1: ans = ans * a % mod
        a = a * a % mod
        n >>=1
    return ans

例题五:方阵次幂(模板题)  

lanqiao0J题号1551

题目描述

给定一个 N 阶矩阵 A 和一个常数 M,请你输出 A 的 M 次幂。

输入描述

输入第一行包含两个整数 N,M。

接下来 N 行,每行包含 N 个数,表示矩阵 A。

1≤N≤30,0≤M≤5,0≤0≤ 矩阵中的每个数 ≤5。

输出描述

输出有 N 行,每行 N 个整数,表示 A^M

输入输出样例

输入

2 2
1 2
3 4

输出

7 10
15 22

代码 

def multi(A,B):
    m1,n1 = len(A), len(A[0])
    m2,n2 = len(B),len(B[0])
    if n1 != m2: return None    # 若不是方阵,返回无
    C= [[0] *n2 for i in range(m1)]
    for i in range(m1):
        for k in range(n1) :
            for j in range(n2):
                C[i][j] += A[i][k]* B[k][j]
                # 若需要取模运算:C[i][j] = (CLi][j] + A[i][k]* B[k][j])% mod
    return C
def power(A,n): # A是矩阵
    N = len(A)
    ans = [[0]* N for i in range(N)]    # 初始化答案矩阵
    for i in range (N): ans[i][i] = 1   # 单位矩阵(对角线为1,其他为0)
    while n:
        if n % 2: # 若最后一位为1,等价于n&1
            ans = multi(ans,A)
        A = multi(A,A)  # 倍增
        n //= 2 # 右移一位,等价于n >>= 1
    return ans
s,q =map(int,input().split()) # s行s列,q次幂
A=[]
for i in range(s):
    A.append(list(map(int,input().split())))
res = power(A, q)
# 输出矩阵
for row in res:
    for c in row:
        print(c,end = ' ')
    print()

例题六:垒骰子

2015年第六届省赛,lanqiao0J题号132

题目描述

赌圣 atm 晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!

我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6

假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。

atm 想计算一下有多少种不同的可能的垒骰子方式。

两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。

由于方案数可能过多,请输出模  10^9+7  的结果。

输入描述

输入第一行两个整数 n,m,n 表示骰子数目;

接下来 m 行,每行两个整数 a,b ,表示 a 和 b 数字不能紧贴在一起。

其中,0<n≤10^9,m≤36。

输出描述

输出一行一个数,表示答案模 10^9+7 的结果。

输入输出样例

输入

2 1
1 2

输出

544

思路

本题的n最大是10^9,需要O(logn)的算法。

如何垒骰子?
先不考虑互斥问题,推理一下有多少种方案。
(1)1个骰子的情况。一个骰子有6个面,每个面朝上的时候侧面都可以旋转得到4个不同的摆放结果,共有4×6=24种。
(2)2个骰子的情况。一上一下两个骰子,共有(4×6)×(4×6)=576种。等等。

做法 

从第1个骰子逐个往上垒,是一个递推关系,可以用动态规划来处理。
定义状态dp[i][i]:表示高度为i ,顶面点数为j的方案数dp[i][j]等于i-1高度时所有与j的反面无冲突的方案数累加

状态转移方程:dp[i][j]=\sum_{j}^{}dp[i-1][j]               j表示6个面

最后的总方案数乘以4^i,因为每一个骰子可以4面转。

但是,如果直接这样编码,由于n很大,超时。

dp[i][j]=\sum_{j}^{}dp[i - 1][j],把dp[ ][ ]转换成矩阵。

转换成矩阵乘法。垒n个骰子,等于n-1个转移矩阵相乘,最后再乘以第1个骰子 。

注:上面的的矩阵从前到后依次为从上到下的骰子,转移矩阵的每一列代表骰子朝上的数字1~6,最下面的骰子只有一列,代表骰子朝上的数字1~6

下面朝上的数字为1时,根据矩阵相乘,有4^2+4^2+4^2+4^2+4^2+4^2=96种情况。
互斥:把转移矩阵中互斥的位置置为0

例: 数字1和数字4互斥,把转移矩阵中 第一行第四列第一列第四行 置为0。若数字2和数字4互斥,把转移矩阵中 第二行第四列第二列第四行 置为0。

代码 

MOD = int(1e9+7)    #注意一定要用int转换
def multi(A,B):     #矩阵乘法
    C = [[0]*6 for i in range(6)]
    for i in range(6):
        for j in range(6) :
            for k in range(6) :
                C[i][j] =int((C[i][j] +A[i][k]* B[k][j])% MOD)
    return C
def power(A, n):    #矩阵快速幂
    res = [[0]*6 for i in range(6)]
    for i in range(6): res[i][i] = 1
    while n:
        if n %2:res = multi (res,A)
        A= multi(A,A)
        n >>= 1
    return res

def solve(n,dice) :
    transfer = [[4]*6 for i in range(6)]# 转移矩阵
    # # 去掉互斥的情况
    for i in range(6):
        for j in dice.get ((i+3)%6,[]): # 0对面是3,1对4,2对5
            # get函数:若有键(i+3)%6,输出该键对应的值,没有该键,创建该键,赋值为[]
            transfer[i][j]= 0

    transfer = power(transfer,n-1) # 移矩阵乘n-1次
    temp = [4]*6                    # 表示最下面的骰子
    ans = [0]*6
    # 最后乘最下面的骰子
    for i in range(6) :
        for j in range(6):
            ans[i] += transfer[i][j]* temp[j]
    print(int(sum(ans)% MOD))

n, m= [int (str) for str in input ().split()]
# 用字典记录互相排斥的面
dice = dict()
for i in range(m) :
    x,y = [int (str)-1 for str in input ().split()]
    if x not in dice:   dice[x] = [y]
    else:               dice[x].append(y)
    if y not in dice:   dice[y] =[x]
    else:               dice[y].append(x)

solve(n,dice)

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

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

相关文章

Android 深入系统完全讲解(37)

7.5 源码讲解 dlopen 打开动态库 dlsym 找到符号 (*print_func)(); 调用方法 我们可以看到&#xff0c;要使用一个 so 库的某个方法&#xff0c;就上面三步骤&#xff0c;加载 &#xff0c;查找 &#xff0c;使用 。我 们这里调用了 so 库中的 my_print 方法。 7.6 运行 我们把…

Linux——进程间通信

文章目录前言1. 进程间通信方式的一些标准&#xff1a;2. 管道2.1 什么是管道2.2 管道的原理2.3 匿名管道2.3.1 实例代码1. demo代码2. 总结管道的特点&#xff0c;理解以前的管道 |3. 扩展——进程池2.4 管道读写规则2.5 命名管道2.5.1 创建一个命名管道2.5.2 命名管道的打开规…

Python break用法详解

我们知道&#xff0c;在执行 while 循环或者 for 循环时&#xff0c;只要循环条件满足&#xff0c;程序将会一直执行循环体&#xff0c;不停地转圈。但在某些场景&#xff0c;我们可能希望在循环结束前就强制结束循环&#xff0c;Python 提供了 2 种强制离开当前循环体的办法&a…

路由处理及功能(实现了权限控制vue admin)

界面简化 将 template 改为&#xff1a; <template><div class"login-container"><el-formref"loginForm":model"loginForm":rules"loginRules"class"login-form"autocomplete"on"label-positio…

Mybatis遇到的脑残问题

一、MySQL的版本问题 有的教程mysql是8.0版本使用jar包不一样 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0</version></dependency>然后我查了一下我的mysql版本是5.7版…

分支语句与循环语句

文章目录 什么是语句&#xff1f; 分支语句&#xff08;选择结构&#xff09;循环语句goto语句前言 一、什么是语句&#xff1f; C语句可分为以下五类&#xff1a; 1. 表达式语句 2. 函数调用语句 3. 控制语句 4. 复合语句 5. 空语句 控制语句用于控制程序的执行流程&#xff0…

第九层(1):初识STL

文章目录前情回顾初识STLSTL的诞生STL的基本概念STL六大组件STL中的容器、算法、迭代器容器算法迭代器容器、算法、迭代器的配合使用vector中的嵌套使用石碑倒下...后面还有石碑&#xff1f;本章知识点&#xff08;图片形式&#xff09;&#x1f389;welcome&#x1f389; ✒️…

为什么带NOLOCK的查询语句还会造成阻塞

背景客户反映HIS数据库在11点出现了长时间的阻塞&#xff0c;直到手动KILL掉阻塞的源头。请我们协助分析原因&#xff0c;最终定位到.NET程序中使用的SqlDataReader未正常关闭导致。现象登录SQL专家云&#xff0c;进入趋势分析&#xff0c;在活动会话中回溯11点一个小时内的运行…

【Ajax】防抖和节流

一、防抖什么是防抖防抖策略&#xff08;debounce&#xff09;是当事件被触发后&#xff0c;延迟 n 秒后再执行回调&#xff0c;如果在这 n 秒内事件又被触发&#xff0c;则重新计时。如果事件被频繁触发&#xff0c;防抖能保证只有最有一次触发生效&#xff01;前面 N 多次的触…

【Linux IO】文件描述符、重定向、缓冲区

1.open函数1.1第二个参数的解释&#xff1b;O_RDONLY: 只读打开 O_WRONLY: 只写打开 O_RDWR : 读&#xff0c;写打开上面三个常量&#xff0c;必须指定一个且只能指定一个 O_CREAT : 若文件不存在&#xff0c;则创建它。需要使用mode选项&#xff0c;来指明新文件的访问权限 O_…

MyBatis 连接数据库与增删改查

❤️作者主页&#xff1a;微凉秋意 ✅作者简介&#xff1a;后端领域优质创作者&#x1f3c6;&#xff0c;CSDN内容合伙人&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3c6; ✨精品专栏&#xff1a;数据结构与课程设计 &#x1f525;系列专栏&#xff1a;javaweb 文章目录前…

C++设计模式(8)——命令模式

命令模式 亦称&#xff1a;动作、事务、Action、Transaction、Command 意图 命令模式是一种行为设计模式&#xff0c; 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中&#xff0c; 且能…

linux基本功系列之-lsattr命令实战

文章目录一. lsattr命令实战二. 语法格式及常用选项三. 参考案例3.1 查看指定文件上的隐藏属性&#xff1a;3.2 查看目录的隐藏属性3.3 查看目录中全部文件的隐藏属性总结前言&#x1f680;&#x1f680;&#x1f680; 想要学好Linux&#xff0c;命令是基本功&#xff0c;企业中…

英语学习打卡day4

2023.1.24 1.out of curiosity 出于好奇 out of necessity 出于必要 out of interest 出于利益 out of sympathy 出于同情 out of respect 出于尊敬 out of’ fear 出于害怕 out of desperation 出于不得已/绝望 2.ashore adv.向(或在)岸上;上岸 a在… …的 shore岸- >在…

Java 23种设计模式(5.结构型模式-代理模式)

结构型模式 代理模式 结构型模式描述如何将类或对象按某种布局组成更大的结构。 它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承机制来组织接口和类&#xff0c;后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低&#xff0c;满足“合成…

分享127个ASP源码,总有一款适合您

ASP源码 分享127个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 127个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1JpOFmxkovbScxmz0_MhUWg?pwd758t 提取码&#x…

算法:一维与二维最大连续子序列和(子矩阵和,c++实现 动态规划)

文章目录一维最大连续子序列和代码示例二维最大连续子序列和、代码示例一维最大连续子序列和 给你一个序列 【-1&#xff0c;-2&#xff0c;3&#xff0c;6&#xff0c;4&#xff0c;-9】的最大的连续的子序列和的值。 什么是最大连续子序列和&#xff0c;首先要满足两个条件…

Java基础 Stream流方法引用异常

Stream流 引例 需求&#xff1a;按照下面要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素 1. 把所有以“曹”开头的元素存储到新集合中 2. 把曹开头&#xff0c;长度为3的元素存储到新集合中 List<String> list List.of("曹操", "…

19.2、Javaweb案例_Servlet代码抽取优化分页数据redis缓存优化分页数据展示

优化Servlet 目的 减少Servlet的数量&#xff0c;现在是一个功能一个Servlet&#xff0c;将其优化为一个模块一个Servlet&#xff0c;相当于在数据库中一张表对应一个Servlet&#xff0c;在Servlet中提供不同的方法&#xff0c;完成用户的请求。 Idea控制台中文乱码解决&…

线性代数复习——行列式

文章目录第一章 行列式1.1 克拉默法则1.2 n阶行列式1.3 特殊行列式1.4 行列式的性质和推论1.5 余子式和代数余子式1.6 范德蒙德行列式第一章 行列式 1.1 克拉默法则 举例&#xff1a; 对于三元线性方程组 {a11x1a12x2a13x3b1a21x1a22x2a23x3b2a31x1a32x2a33x3b3(1)\begin{cas…