线性筛求约数个数

写欧拉计划第十二题的时候,求约数个数,暴力实在太慢,顺便写篇博客记录一下高效方法

改编自: https://blog.csdn.net/ControlBear/article/details/77527115

d(i) 表示 i 的约数个数

num[i] 表示 i 的最小素因子的个数

prim[i] 表示 第 i 个素数

素数

根据基本算数定理,每一个大于等于2的正整数,都可以被分解成
N = p 1 a 1 p 2 a 2 . . . p n a n N = p_1^{a_1}p_2^{a_2}...p_n^{a_n} N=p1a1p2a2...pnan
其中,p为素数。

线性筛就是每一次把最小素因子筛出。

mark = [0] * N
prim = [0] * N
def initial():cnt = 0for i in range(2, N):if not mark[i]:prim[cnt] = icnt += 1for j in range(0, cnt):if i * prim[j] < N:mark[i * prim[j]] = 1if not i % prim[j]:break

其中,mark,为已知质数的倍数。

## 约数

算数基本定理中,根据拆分后的素因子的指数,我们可以求出每个N的约数的个数
d ( N ) = ( 1 + a 1 ) ( 1 + a 2 ) . . . ( 1 + a n ) d(N) = (1+a_1)(1+a_2)...(1+a_n) d(N)=(1+a1)(1+a2)...(1+an)
根据这个式子,可以用线性筛去筛去当前N的约数个数。

筛的过程中,我们需要保存最下素因子的个数。

①当前数是素数

当前 d ( N ) = ( 1 + 1 ) = 2 d(N)=(1+1)=2 d(N)=(1+1)=2, 因为素数只有一个素因子(它本身),并且指数为1,

最小素因子个数num[N] = 1

i % p r i m [ j ] ! = 0 i \% prim[j] !=0 i%prim[j]!=0

i中不包含 prim[j]这个素因子,然而在i*prim[j]中,包含了一个prim[j]

可以从前面得到i的所有约数个数 ( 1 + a 1 ) ( 1 + a 2 ) . . . ( 1 + a n ) (1+a_1)(1+a_2)...(1+a_n) (1+a1)(1+a2)...(1+an)

然后补上 prim[j]的个数 ( 1 + a 1 ) ( a + a 2 ) . . . ( 1 + a n ) ∗ ( 1 + 1 ) (1+a_1)(a+a_2)...(1+a_n)*(1+1) (1+a1)(a+a2)...(1+an)(1+1)

所以最后得到 d ( i ∗ p r i m ( j ) ) = d ( i ) ∗ d ( p r i m [ j ] ) d(i*prim(j))=d(i)*d(prim[j]) d(iprim(j))=d(i)d(prim[j])

而且由于当前的 prim[j] 必然是 i * prim[j]的最小素因子,要记录下这个最小素因子个数

所以 num[i * prim[j]] = 1

③i % prim[j] == 0

i中必然包含了至少一个prim[j], 而且prim[j]也必然是i的最小素因子。

而i * prim[j]比起i则是多了一个最小素因子个数,即 1 + a 1 1+a_1 1+a1

那么 i * prim[j]的约数个数应该是 ( 1 + a 1 + 1 ) ( 1 + a 2 ) . . . ( 1 + a n ) (1+a_1+1)(1+a_2)...(1+a_n) (1+a1+1)(1+a2)...(1+an)

之后,i的最小素因子个数为num[i], 而d(i)中已经包含了 ( 1 + a 1 ) ( 1 + a 2 ) . . . ( 1 + a n ) (1+a_1)(1+a_2)...(1+a_n) (1+a1)(1+a2)...(1+an)

这时可以除去第一项 1 + a 1 1+a_1 1+a1,然后乘以 1 + a 1 + 1 1+a_1+1 1+a1+1,就可得到d(i * prim[j])的约数的个数

d ( i ∗ p r i m [ j ] ) = d ( i ) / ( n u m [ i ] + 1 ) ∗ ( n u m [ i ] + 2 ) d(i * prim[j]) = d(i) / (num[i]+1) * (num[i]+2) d(iprim[j])=d(i)/(num[i]+1)(num[i]+2)

当前 num[i * prim[j]] = num[i] +1, 继续保存当前最小素因子个数

N = 20
d = [0] * N
prim = [0] * N
num = [0] * N
mark = [0] * N
def initial():cnt = 0d[1] = 1for i in range(2, N):if not mark[i]:prim[cnt] = icnt += 1num[i] = 1d[i] = 2for j in range(0, cnt):if i * prim[j] < N:mark[i * prim[j]] = 1if not i % prim[j]:num[i * prim[j]] = num[i] + 1d[i * prim[j]] = d[i] / (num[i] + 1) * (num[i*prim[j]]+ 1)breakd[i * prim[j]] = d[i] * d[prim[j]]num[i * prim[j]] = 1


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部