[python刷题模板] 一些数论工具
- 一、 算法&数据结构
- 1. 描述
- 2. 复杂度分析
- 3. 常见应用
- 4. 常用优化
- 二、 模板代码
- 1. 不用浮点数求向上取整公式 ceil(a/b) = (a+b-1)//b
- 2. 快速幂pow(m,x,MOD)
- 3. 组合数C(m,r) = comb(m,r)
- 4. 排列数P(m,R) = perm(m,r)
- 4.1 含重复元素的全排列种类数p = C(n,c1) * C(n-c1,c2) * C(n-c1-c2,c3) * ... * C(cx,cx)
- 5. 最大公约数gcd = math.gcd(a,b)
- 6. 最小公倍数lcm = math.lcm(a,b)
- 7. 质数筛法:埃氏筛O(nlgn)、线性筛O(n)(欧拉筛)
- 8. 求一个数所有质因子以及个数。
- 三、其他
- 四、更多例题
- 五、参考链接
一、 算法&数据结构
1. 描述
复制代码
1
2数论太难了,有的思路第二次写又要重新想,这里记录一些数学相关常用方法,备查。
2. 复杂度分析
- 挨个分析。
3. 常见应用
- 组合数学、取模…
- 计算几何
4. 常用优化
- 记忆化。
二、 模板代码
1. 不用浮点数求向上取整公式 ceil(a/b) = (a+b-1)//b
涉及到取整,如果直接ceil就有浮点数精度问题,问就是wa过。
复制代码
1
2
3# ceil(a/b) = (a+b-1)//b s = (a+b-1)//b
2. 快速幂pow(m,x,MOD)
这个我写过一篇[python刷题模板] 取模技巧/组合数取模模板/快速幂取模模板
但是py其实pow函数第三个参数支持模。。。
或者由于py支持大数,其实可以直接m**x%MOD
复制代码
1
2
3ans = pow(m,x,MOD) ans = m**x%MOD
3. 组合数C(m,r) = comb(m,r)
py自带组合数方法,支持大数运算,别忘了外边取模
复制代码
1
2c = comb(m,r)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class ModComb: def __init__(self, n, p): """ 初始化,为了防止模不一样,因此不写默认值,强制要求调用者明示 :param n:最大值,通常是2*(10**5)+50 :param p: 模,通常是10**9+7 """ self.p = p self.inv_f, self.fact = [1] * (n + 1), [1] * (n + 1) # 阶乘的逆元、阶乘 inv_f, fact = self.inv_f, self.fact for i in range(2, n + 1): fact[i] = i * fact[i - 1] % p inv_f[-1] = pow(fact[-1], p - 2, p) for i in range(n, 0, -1): inv_f[i - 1] = i * inv_f[i] % p def comb(self, m, r): if m < r or r < 0: return 0 return self.fact[m] * self.inv_f[r] % self.p * self.inv_f[m - r] % self.p def perm_count_with_duplicate(self, a): """含重复元素的列表a,全排列的种类。 假设长度n,含x种元素,分别计数为[c1,c2,c3..cx] 则答案是C(n,c1)*C(n-c1,c2)*C(n-c1-c2,c3)*...*C(cx,cx) 或:n!/c1!/c2!/c3!/../cn! """ ans = self.fact[len(a)] for c in Counter(a).values(): ans = ans * self.inv_f[c] % self.p return ans # 下边这种也可以 # s = len(a) # ans = 1 # for c in Counter(a).values(): # ans = ans * self.comb(s,c) % MOD # s -= c # return ans
4. 排列数P(m,R) = perm(m,r)
perm如果不传入第二个参数,就是阶乘
复制代码
1
2
3p = perm(m,r) perm(n) == factorial(n)
4.1 含重复元素的全排列种类数p = C(n,c1) * C(n-c1,c2) * C(n-c1-c2,c3) * … * C(cx,cx)
6276. 统计同位异构字符串数目
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22MOD = 10 ** 9 + 7 class Solution: def countAnagrams(self, s: str) -> int: ret = 1 def perm_count_with_duplicate(a): """含重复元素的列表a,全排列的种类。 假设长度n,含x种元素,分别计数为[c1,c2,c3..cx] 则答案是C(n,c1)*C(n-c1,c2)*C(n-c1-c2,c3)*...*C(cx,cx) """ s = len(a) ans = 1 for c in Counter(a).values(): ans = ans * comb(s,c) % MOD s -= c return ans for w in s.split(): ret = ret * perm_count_with_duplicate(w) %MOD return (ret)%MOD
5. 最大公约数gcd = math.gcd(a,b)
注意这个gcd支持传入多参数,有两种写法,建议用星号,因为reduce如果a是空数组会报错。
注意gcd(a,0)=a,即任意数和0的gcd都是自己,参照循环相减法。
复制代码
1
2
3
4
5ans = gcd(a,b) a = [3,5,10] print(gcd(*a)) print(reduce(gcd,a))
6. 最小公倍数lcm = math.lcm(a,b)
写法和gcd类似。
复制代码
1
2
3
4
5ans = lcm(a,b) a = [3,5,10] print(lcm(*a)) print(reduce(lcm,a))
7. 质数筛法:埃氏筛O(nlgn)、线性筛O(n)(欧拉筛)
- 由于py切片优化的原因,埃氏筛的表现超过线性筛
欧拉筛
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17def tag_primes_euler(n): # 返回一个长度n+1的数组p,如果i是质数则p[i]=1否则p[i]=0 primes = [1]*(n+1) primes[0] = primes[1] = 0 # 0和1不是质数 ps = [] # 记质数 for i in range(2,n+1): if primes[i]: ps.append(i) for j in ps: if j*i>n: break primes[j*i] = 0 if i%j == 0:break # print(ps) return primes primes = tag_primes_euler(10**5+5)
埃氏筛
给所有素数标记1
复制代码
1
2
3
4
5
6
7
8
9def tag_primes_eratosthenes(n): # 返回一个长度n的数组p,如果i是质数则p[i]=1否则p[i]=0 primes = [1]*n primes[0] = primes[1] = 0 # 0和1不是质数 for i in range(2,int(n**0.5)+1): if primes[i]: primes[i * i::i] = [0] * ((n - 1 - i * i) // i + 1) return primes primes = tag_primes_eratosthenes(5*10**5+5)
8. 求一个数所有质因子以及个数。
- 如果是需要暴力的分解质因数,不需要多次使用,可以直接两重循环从小枚举
- 2507. 使用质因数之和替换后可以取到的最小值
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25def get_prime_reasons(x): # 获取x的所有质因数,虽然是两层循环且没有判断合数,但复杂度依然是O(sqrt(x)) # 由于i是从2开始增加,每次都除完,因此所有合数的因数会提前除完,合数不会被x整除的 if x == 1: return Counter() ans = Counter() i = 2 while i*i<=x: while x%i==0: ans[i] += 1 x //= i i += 1 if x > 1: ans[x] += 1 return ans class Solution: def smallestValue(self, n: int) -> int: while n: cnt = get_prime_reasons(n) s = 0 for k,v in cnt.items(): s += k*v if s == n:return n n = s
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13class Solution: def smallestValue(self, n: int) -> int: while True: x, s, i = n, 0, 2 while i * i <= x: while x % i == 0: s += i x //= i i += 1 if x > 1: s += x if s == n: return n n = s
多次使用,先搞个质数筛加快运行;然后记忆化。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29def tag_primes_euler(n): # 返回一个长度n+1的数组p,如果i是质数则p[i]=1否则p[i]=0 primes = [1]*(n+1) primes[0] = primes[1] = 0 # 0和1不是质数 ps = [] # 记质数 for i in range(2,n+1): if primes[i]: ps.append(i) for j in ps: if j*i>n: break primes[j*i] = 0 if i%j == 0:break # print(ps) return primes primes = tag_primes_euler(10**5+5) @cache def get_prime_reasons(x): if x == 1: return Counter() if primes[x]: return Counter([x]) for i in range(2,int(x**0.5)+1): if x % i == 0: return get_prime_reasons(i) + get_prime_reasons(x//i)
三、其他
四、更多例题
五、参考链接
最后
以上就是沉静短靴最近收集整理的关于[python刷题模板] 一些数论工具的全部内容,更多相关[python刷题模板]内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复