1.模运算:
定义:模运算为a除以m的余数,记为a mod m,有a mod m = a % m。 模运算是大数运算中的常用操作。 如果一个数太大,无法直接输出,或者不需要直接输出,可以把它取模后,缩小数值再输出。
Python虽然能直接计算大数,不用担心数据溢出,但是大数乘法太耗时,所以也常用取模来缩小数值。
一个简单应用,判断奇偶:a%2==0,a是偶数;a%2==1,a是奇数
题目:刷题统计
问题描述
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 a 道题目, 周六和周日每天做 b 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 n 题?
输入格式
输入一行包含三个整数a,b 和 n.
输出格式
输出一个整数代表天数。
样例输入
10 20 99
样例输出
8
题解:
a, b, n = map(int, input().split())
week = a * 5 + b * 2 #每周做题
days = (n // week) * 7 #天数
n %= week #余数
if n <= a * 5:
days += n // a + (n % a != 0) #在周一到周五内
else: #周六和周日
days += 5
n -= a * 5
days += n // b + (n % b != 0)
print(days)
#n%a!=0 如果余数不为0,则输出True,也就是1
#为0则输出False,即0
2. 快速幂
幂运算a^n,当n很大时,如果一个个地乘,时间是O(n)的,速度很慢,此时可以用快速幂,在O(logn)的时间内算出来。
快速幂的一个解法:分治法,算a^2,然后再算(a^2)^ 2,…,一直算到a^n,代码也容易写。
标准的快速幂:用位运算实现。 基于位运算的快速幂,原理是倍增。
1011 看最后一位是不是1,然后把整个数右移。现在看101,看最后一位是不是1,然后整个数右移,以此类推。。。
题目:快速幂
输入描述
三个整数 b,p,k。
输入输出样例
示例
输入
2 10 9
输出
7
def fast_pow(a, n, mod):
ans = 1
a %= mod #重要,防止下面的ans*a越界,凡是大数都要取模
while n > 0:
if n & 1 == 1:
ans = (ans * a) % mod #取模
a = (a * a) % mod #取模
n >>= 1
return ans
b, p, k = map(int, input().split())
print(fast_pow(b, p, k))
题目:RSA解密
题解:
(1)求p,q
import math
n = 1001733993063167141
k = int(math.sqrt(n))
for i in range(2, k+1):
if n % i == 0:
print(i, n // i)
#p=891234941 q=1123984201
(2)求e
[(d*e-1)]/[(p-1)*(q-1)] = temp
e = [temp*(p-1)(q-1)+1]/d
p=891234941
q=1123984201
tmp = (p -1)*(q - 1)
for j in range(2, n+1):
now = j * tmp + 1 #now = d*e
if now % d == 0:
print(now // d) #e = 823816093931522017
break
(3)求
n = 1001733993063167141
C = 20190324
e = 823816093931522017
def fastpow(b, m, mod):
ans = 1
b %= mod
while m > 0:
if m & 1 == 1:
ans = (ans * b) % mod
b = (b * b) % mod
m >>= 1
return ans
print(fastpow(C, e, n))
3.GCD:定义、性质
最大公约数Greatest Common Divisor(GCD):整数a和b的GCD是指能同时整除a和b的最大整数,记为gcd(a, b)。由于-a的因子和a的因子相同,因此gcd(a, b) = gcd(|a|, |b|)。编码时只关注正整数的最大公约数。
性质:
(1)gcd(a, b) = gcd(a, a+b) = gcd(a, k·a+b)
(2)gcd(ka, kb) = k·gcd(a, b)
(3)定义多个整数的最大公约数:gcd(a, b, c) = gcd(gcd(a, b), c)。
(4)若gcd(a, b) = d,则gcd(a/d, b/d) = 1,即a/d与b/d互素。这个定理很重要。
(5)gcd(a+cb, b) = gcd(a, b)
手写gcd函数,常用欧几里得算法。
辗转相除法, 又名欧几里德算法(Euclidean algorithm),是求最大公约数的一种方法。它的具体做法是:用较小数除较大数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。
a=q*b+r; 都为整数 gcd(a,b)=gcd(b,r);
gcd(a,b)=gcd(b, a mod b );
辗转相除法求gcd:
gcd(a, b) = gcd(b, a mod b) 这是最常用的方法,极为高效。
设a > b,辗转相除法的计算复杂度为O((log2a)^3)
python实现
def gcd(a,b):
if b == 0:
return a
return gcd(b,a%b)
4.LCM:定义、性质
python代码实现
def lcm(a,b):
return a // gcd(a,b) * b
题目:核桃的数量
题目描述
小张是软件项目经理,他带领 3 个开发组。工期紧,今天都在加班呢。为鼓舞士气,小张打算给每个组发一袋核桃(据传言能补脑)。他的要求是:
-
各组的核桃数量必须相同
-
各组内必须能平分核桃(当然是不能打碎的)
-
尽量提供满足 1,2 条件的最小数量(节约闹革命嘛)
输入描述
输入一行 a,b,c,都是正整数,表示每个组正在加班的人数,用空格分开(a,b,c<30)。
输出描述
输出一个正整数,表示每袋核桃的数量。
输入输出样例
示例
输入
2 4 5
输出
20
题解:
a, b, c = map(int, input().split())
def gcd(a,b):
if b == 0:
return a
return gcd(b,a%b)
def lcm(a,b):
return a//gcd(a,b)*b
k = lcm(a,b)
print(lcm(k,c))
题目描述
Hanks 博士是 BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数 c1 和 c2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数 a0,a1,b0,b1,设某未知正整数 x 满足:
-
x 和 a0 的最大公约数是 a1;
-
x 和 b0 的最小公倍数是 b1。
Hankson 的“逆问题”就是求出满足条件的正整数 x。但稍加思索之后,他发现这样的 x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 x 的个数。请你帮助他编程求解这个问题。
输入输出样例
示例 1
输入
2
41 1 96 288
95 1 37 1776
输出
6
2
若x是b1的因子,有 x*y = b1,y也可能是答案。 只需要在范围x<=sqrt(b1)内查询,同时判断y就行了。 但还是超时,因为gcd计算也要花时间,加上一个优化:if(b1%x==0),表示b1是x的公倍数。
题解:
import math
def gcd(a,b):
if b == 0:
return a
return gcd(b, a % b)
def lcm(a,b):
return a//gcd(a, b)*b
n = int(input())
for i in range(n):
a0, a1, b0, b1 = map(int, input().split())
ans = 0
for x in range(1, int(math.sqrt(b1))+1):
if b1 % x == 0:
if gcd(x, a0) == a1 and lcm(x, b0) == b1:
ans += 1
y = b1 // x
if x == y:
continue
if gcd(y, a0) == a1 and lcm(y, b0) == b1:
ans += 1
print(ans)
题目:寻找整数
【题目描述】有一个不超过1017的正整数n,知道这个数除以2至49后的余数如下表所示,求这个正整数最小是多少。
LCM方法
从表格的第一个数2开始,逐个增加后面的数,找满足条件的n。
1)满足第一个条件,除以2余1的数有:3、5、7、9、…此时步长k = 2。
2)继续满足第二个条件,除以3余2的数,只能从上一步骤的3、5、7、9、…中找,有5、11、17、… 此时步长k = 6,为什么k = 6?
实际上是LCM:k = lcm(2, 3) = 6
3)继续满足第三个条件,除以4余1的数,只能从5、11、17、…中找,有5、17、29、…此时步长k = lcm(2, 3, 4) = 12。
4)继续满足第四个条件,….
题解:
from math import *
mod=[0,0,1,2,1,4,5,4,1,2,9,0,5,10,11,14,9,0,11,18,9,11,11,15,17,9,23,20,25,16,29,27,25,11,17,4,29,22,37,23,9,1,11,11,33,29,15,5,41,46]
ans = 2 + mod[2]
k = 2 #从第一个数的步长开始
for i in range(3,50):
while True:
if ans%i == mod[i]: #ans是满足前i个数的解
k = lcm(k,int(i)) #连续做LCM
break
else: ans += k #累加新的步长
print(ans)
5.素数的判断
素数定义:只能被1和自己整除的正整数。注:1不是素数,最小素数是2。
判断一个数n是不是素数:当n ≤ 10^14时,用试除法;n > 10^14时,试除法不够用,需要用高级算法,例如Miller_Rabin算法。
试除法:用[2, n-1]内的所有数去试着除n,如果都不能整除,就是素数。 优化:把[2, n-1]缩小到[2, sqrt(n)]。证明:若n = a×b,其中a≤sqrt(n),b≥ sqrt(n),如果n有个因子是a,说明n不是素数,b不用再试。
题目:笨小猴
【题目描述】
笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案。
【输入描述】
输入文件只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100。
【输出描述】
输出文件共两行,第一行是一个字符串,假设输入的的单词是Lucky Word,那么输出“Lucky Word”,否则输出“No Answer”;第二行是一个整数,如果输入单词是Lucky Word,输出maxn-minn的值,否则输出0。
题解:
from math import sqrt
letter = [0] * 26 # 统计每个字母的个数,是一个hash表
def is_prime(n):
if n <= 1:
return False
for i in range(2, int(sqrt(n)) + 1):
if n % i == 0:
return False
return True
s = input().strip()
for ch in s:
letter[ord(ch) - ord('a')] += 1
maxn, minn = -1, 1000
for i in range(26):
if letter[i] == 0:
continue
if letter[i] > maxn:
maxn = letter[i]
if letter[i] < minn:
minn = letter[i]
if len(s) == maxn:
minn = 0
ans = is_prime(maxn - minn)
if not ans:
print("No Answer\n0")
else:
print("Lucky Word\n%d" % (maxn - minn))
例题:最大最小公倍数
【题目描述】
已知一个正整数n,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。
【输入描述】
一个正整数N。
【输出描述】
一个整数表示答案。
思路:
贪心,从大的数开始选。不过,简单地把N里面最大的三个数相乘,N*(N-1)*(N-2),并不正确,需要分析多种情况。(因为这三个数不一定互质)
最小的公倍数是三个数的质因数相乘,如果有几个质因数相同,则比较两数中哪个数的质因数的个数较多。例如6、7、8的最小公倍数,先分解因子:6=2×3,7=7×1,8=2×2×2,它们的最小公倍数是3×7×2×2×2。
大于1的两个相邻整数互质,它们没有公共的质因数。如果题目是任选二个数,最大的最小公倍数是N*(N-1)。
所以我们利用质数的性质进行思考
对于连续的三个整数,分两种情况:
(1)N是奇数。N、N-1、N-2是奇偶奇,结论是这三个数两两互质,三个数的乘积就是最大的最小公倍数。三个数两两互质,也就是说任意一个质数,只在N、N-1、N-2中出现一次。
逐个分析质数:
质因数2,只在N-1中出现。
质因数3,如果在N中出现(设N = 3a),就不会在N-1中出现(这要求N-1 = 3b,无解),也不会在N-2中出现(这要求N-2 = 3b,无解)。 推广到任何一个质数k,都只会在N、N-1、N-2中出现一次,所以三个数互质。
(2)N是偶数。 如果N为偶数,那么N与N-2最大公约数为2,所以我们要找下一个质数,此时需要考虑N与N-3的关系:
如果N能被3整除,则N-3也能被3整除,此时N与N-3不互质,但是N-1与N-3必然互质(N-1、N-3都为奇数)
所以N-1、 N-2、N-3 如果N不能被3整除,则N-3也不能被3整除,此时N与N-3互质,所以选择N、N-1、N-3
题解:
n = int(input())
if n <= 2:
ans = n
elif n % 2:
ans = n * (n - 1) * (n - 2)
else:
if n % 3:
ans = n * (n - 1) * (n - 3)
else:
ans = (n - 1) * (n - 2) * (n - 3)
print(ans)
6 素数筛
素数的筛选:给定n,求2~n内所有的素数。 一个个地判断很慢,所以用“筛子”筛所有的整数,把非素数筛掉,剩下的就是素数。 常用两种筛法:埃氏筛、欧拉筛。
埃氏筛
初始队列{2、3,4,5,6,7,8,9,10,11,12,13,...,n},操作步骤:
(1)输出最小的素数2,筛掉2的倍数,得{2,3,4,5,6,7,8,9,10,11,12,13,...} (2)输出最小的素数3,筛掉3的倍数,得{2,3,4,5,6,7,8,9,10,11,12,13,...} (3)输出最小的素数5,筛掉5的倍数,得{2,3,4,5,6,7,8,9,10,11,12,13,...} 继续以上步骤,直到队列为空。
python代码实现
primes = [0] * N
cnt = 0
bprime = [False] * N
def getPrimes(n):
global cnt, primes, bprime
bprime[0] = True
bprime[1] = True
for i in range(2, n+1):
if not bprime[i]:
primes[cnt] = i
cnt += 1
for j in range(i*2, n+1, i):
bprime[j] = True
欧拉筛(线性的埃氏筛)
N = 1000005
primes = [0] * N
bprime = [False] * N
cnt = 0
def getPrimes(n: int):
global cnt
for i in range(2, n+1):
if not bprime[i]:
primes[cnt] = i
cnt += 1
j = 0
while j < cnt and i * primes[j] <= n:
bprime[i * primes[j]] = True
if i % primes[j] == 0:
break
j += 1
7 分解质因子
分解质因子也可以用试除法。求n的质因子:
(1)第一步,求最小质因子p1。逐个检查从2到sqrt(n)的所有素数,如果它能整除n,就是最小质因子。然后连续用p1除n,目的是去掉n中的p1,得到n1。
(2)第二步,再找n1 的最小质因子。逐个检查从p1到sqrt(n1)的所有素数。从p1开始试除,是因为n1没有比p1小的素因子,而且n1的因子也是n的因子。
(3)继续以上步骤,直到找到所有质因子。
给定一个区间 [a,b],请你求出区间 [a,b] 中所有整数的质因数分解。
题目:分解质因数
输入描述
输入共一行,包含两个整数 a,b。
2≤a≤b≤10^3。
输出描述
每行输出一个数的分解,形如 k=a1×a2×a3⋯(a1≤a2≤a3⋯,k也是从小到大的)(具体可看样例)
输入输出样例
示例
输入
3 10
输出
3=3
4=2*2
5=5
6=2*3
7=7
8=2*2*2
9=3*3
10=2*5
题解:
import math
p = [0] * 20 # p[] 记录因子,p[1] 是最小因子。一个 int 数的质因子最多有 10 几个
c = [0] * 40 # c[i] 记录第 i 个因子的个数。一个因子的个数最多有 30 几个
def factor(n):
m = 0
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
m += 1
p[m], c[m] = i, 0
while n % i == 0:
n //= i
c[m] += 1
if n > 1:
m += 1
p[m], c[m] = n, 1
return m
a, b = map(int, input().split())
for i in range(a, b+1):
m = factor(i)
print(f'{i}=', end='')
for j in range(1, m+1):
for k in range(1, c[j]+1):
print(p[j], end='')
if k < c[j]:
print('*', end='')
if j < m:
print('*', end='')
print()
MAXN = 110
cnt = [0] * MAXN
for i in range(1, 101):
x = i
# 质因子分解
j = 2
while j * j <= x:
if x % j == 0:
while x % j == 0:
x //= j
cnt[j] += 1
j += 1
if x > 1:
cnt[x] += 1
ans = 1
for i in range(1, 101):
if cnt[i] != 0:
ans *= (cnt[i] + 1)
print(ans)