[算法复习] 素数判定:欧拉筛 Miller_Rabin

摘取了以前的笔记并做了一些补充说明

9,素数判定

   O(n) + O(1): 线性筛.
    又叫欧拉筛。
    先考虑一种朴素的想法。
    对于每一个数\(k\), 我们枚举\(i\),将\(ik\)标记为合数。
    因为每个数都可以分解成若干个素数的积,我们只需枚举每个素数作为\(k\)即可。
    但是复杂度依然很高,我们考虑优化。
    对于每个数,仅用它最小的质因数来筛它。
    我们用一个数组存储质数,枚举\(i\)作为它的倍数,并且每次都判断一下如果\(i\)是素数,则将其加入素数数组。
    然后我们从小到大枚举\(pri[j]\)用其筛除\(i * pri[j]\).
    特别的,如果\(!(i \ mod \ pri[j])\) 那么我们筛完就break.
    
    下面我们来证明这个算法的正确性和复杂度。
    
    正确性:
    接下来我们证,如果\(!(i \ mod \ pri[j])\),那么对于后面的\(i \cdot pri[j+k]\)来说,用\(pri[j+k]\)来筛一定是不优的,也就是这个剪枝肯定不会导致漏筛。
    因为\(!(i \ mod \ pri[j])\)所以我们可以设\(i = t \cdot pri[j]\)
    那么\(i \cdot pri[j+k] = t \cdot pri[j] \cdot pri[j+k]\)
    又知\(pri[j+k] > pri[j]\),也就是说,用\(pri[j+k]\)来筛,不如等到\(i = t \cdot pri[j+k]\)的时候用\(pri[j]\)来筛。
    所以这个剪枝肯定是合法的。
    
    复杂度:
    那么为什么每个数仅被最小的质因数筛除了呢?
    接下来我们证,这个剪枝可以导致每个数仅被最小的质因数筛除.
    \(pm[m]\)\(m\)的质因数个数,对于一个数\(m\)\(m = \prod_{i = 1}^{pm[m]}p_i\),其中\(p\)序列为升序排列,若\(m\)不是被\(p_1\)筛除了,我们假设这个数是\(p_t\)
    我们先考虑\(m\)质因数大于\(2\)个的情况:
    那么筛掉\(m\)时我们枚举到的\(i = \prod_{i = 1}^{pm[m]}(p_i) / p_t = b \cdot p_1\),\(b\)为某个不为\(1\)正整数。
    此时,\(i\)会在枚举到\(p_1\)的时候停下,也就是筛完\(b \cdot p_1 \cdot p_1\)就不筛了,也就不会筛\(m = b \cdot p_1 \cdot p_t\)了。与假设矛盾。
    再考虑\(m\)刚好有两个质因数的情况,此时\(m = p_1 \cdot p_2\),那么根据假设,\(m\)\(p_2\)筛掉了。
    也就是在\(i = p_1\)的时候\(m\)被筛了。
    这显然是不合法的,因为此时\(p_2\)这个质数还没被加入素数数组。
    所以\(m\)只可能在\(i = p_2\)时被\(p_1\)筛掉。与假设矛盾。
    综上,假设情况是不可能的,\(m\)必然会被其最小质因数筛除.
    又因为在第二个循环中,每循环一次必筛除一个数,而每个数只会被筛走一次,因此第二个循环的总次数为被筛除的素数个数。所以复杂度是\(O(n)\)
    
    代码:

void get()
{
	z[1] = 1;
	for(R i = 2; i <= n; i ++)
	{
		if(!z[i]) pri[++ tot] = i;
		//printf("%d\n", tot);
		for(R j = 1; j <= tot && 1LL * i * pri[j] <= n; j ++)
		{
			z[i * pri[j]] = 1;
			if(!(i % pri[j])) break;
		}
	}
}

  
  O(\(\sqrt{n}\)): 暴力枚举\(\sqrt{n}\)以内的因子
  Miller_Rabin算法:(这部分写的好烂,哪天我重学Miller_Rabin再来更新吧)
    根据费马小定理有\(a^{p - 1} \equiv 1 (mod \quad p)\),要求p是质数.
    那么如果对于等式\(a^{p - 1} \equiv 1 (mod \quad p)\),枚举多个a,均满足等式,那么可以认为当前的p有很大概率是素数。
    但是依然有很大概率不是,,,因此这个时候采取二次探测来减小错误概率。
    首先有定理:若\(a^2 \equiv 1 (mod \quad p)\)且p为质数,那么a = 1 或 p - 1.
    证明如下:若等式成立,则\(a^{2} - 1 \equiv 0 (mod \quad p)\).
    即\((a + 1)(a - 1) \equiv 0 (mod \quad p)\).
    因此要么\(a + 1 = p\) ---> \(a = p - 1\).
    或者\(a - 1 = 0\) ---> \(a = 1\).
    因为这个探测是在满足费马测试的情况下才进行的,因此对于某个已经被枚举过的a,我们已经有\(a^{p - 1} \equiv 1 (mod \quad p)\).我们设当前指数\(x = p - 1\)
    当p不为2且为素数时,\(p\)一定为奇数,即\(p - 1\)一定为偶数.
    因此我们可以把原式看做\(a^{\frac{x}{2}} \cdot a^{\frac{x}{2}} \equiv 1 (mod \quad p)\),其中可以设\(t = a^{\frac{x}{2}}\)
    那么原式就是\(t \cdot t \equiv 1(mod \quad p)\)
    那么因为这个是直接根据原式化过来的,因此我们已经有它同余1了,因此只需要再判断t是否等于1 或 p - 1即可。
    如果t = 1 且 x为偶数,那么我们就又得到了一个新的式子\(a^{\frac{x}{2}} \equiv 1 (mod \quad p)\)
    此时因为x为偶数,所以我们依然这个把这个新式子当做上面的式子重新做探测,直到不满足t = 1且x为偶数为止。

posted @ 2021-04-12 23:33  ww3113306  阅读(98)  评论(1编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。