埃氏筛法和欧拉筛法的区别
来源:互联网 发布:linux 删除文件第一行 编辑:程序博客网 时间:2024/06/05 23:54
转自:
点击打开链接
Eratosthenes筛法(Sieve of Eratosthenes)
由于思想非常简单,故只给出实现。
12345678910111213141516171819
void eratosthenes_sieve(int n){ totPrimes = 0; memset(flag, 0, sizeof(flag)); int sqrtn = sqrt(n + 0.5); for (int i = 2; i <= sqrtn; i++) { if (!flag[i]) { primes[totPrimes++] = i; for (int j = i * i; j <= n; j += i) { flag[j] = true; } } } for (int i = sqrtn + 1; i <= n; i++) { if (!flag[i]) primes[++totPrimes] = i; }}
时间复杂度
Euler筛法(Sieve of Euler)
欧拉筛是一种线性算法,并且同时可以计算出每个数的
做法
回顾经典的Eratosthenes筛法,它可能对同一个质数筛去多次。那么如果用某种方法使得每个合数只被筛去一次就变成是线性的了。
不妨规定每个合数只用其最小的一个质因数去筛,这便是欧拉筛了。
不妨先看代码:
123456789101112131415
void euler_sieve(int n){ totPrimes = 0; memset(flag, 0, sizeof(flag)); for (int i = 2; i <= n; i++) { if (!flag[i]) primes[totPrimes++] = i; for (int j = 0; i * primes[j] <= n; j++) { flag[i*primes[j]] = true; if (i % primes[j] == 0) break; } }}
请仔细体会i % primes[j] == 0
的含义。
时间复杂度
简单证明
这个看似很简单,其实还是要注意一下细节的。搞清了证明其他的问题也就清楚了。
证明分两部分。首先证每个合数都会被筛到(正确性),其次证每个合数只会被筛到一次(复杂度)。
每个合数都会被筛到
设有一合数
则
每个合数都只会被筛到一次
与上面一样,还是设有一合数
倘若存在一个质因子
i>px i>px,此时在内层循环中已经早早地break
掉了,因为p1∣i p1∣i。i<px i<px,此时px px还没加进质数表QwQ(顺便一提:这种情况只有可能在x=m x=m 时发生)
难以理解的地方
i % primes[j] == 0
为何不放在前面?
你可以去试试……实践出真知。
放前面的话,所有的“某个质因子的次数不为1”的合数便会被当成质数。至于为什么,请看证明。
j < totPrimes
为何不加?
实践才是检验真理的唯一标准。
- 当
i i 为质数时,内层循环会在最后一个质数(也就是i i 自己)终止。 - 当
i i 为合数时,内层循环会在它的第一个质因数终止。
当然加了也没有问题。
顺便把 φ φ 算出来?
其实这是极简单的。
主要基于以下事实:(很容易通过定义推出来,不妨自己试试)
1.n为质数时,phi(n) = n - 1
2.p为质数且p整除n时,phi(n*p) = p* phi(n)
3.p为质数且p不整除n时,phi(n*p) =(p - 1) * phi(n)
有没有发现简直就是为Euler筛法量身定做的!
代码:
12345678910111213141516171819202122
void euler_sieve_with_phi(int n){ totPrimes = 0; phi[1] = 1; memset(flag, 0, sizeof(flag)); for (int i = 2; i <= n; i++) { if (!flag[i]) { primes[totPrimes++] = i; phi[i] = i - 1; } for (int j = 0; i * primes[j] <= n; j++) { flag[i*primes[j]] = true; if (i % primes[j]) phi[i*primes[j]] = phi[i] * (primes[j] - 1); else { phi[i*primes[j]] = phi[i] * primes[j]; break; } } }}
速度比较
你可能会觉得
实测结果:(精确到微秒,编译时不打开优化开关)
123456789101112131415161718
when n = 10000 eratosthenes_sieve(1229): 0(us) euler_sieve(1229): 0(us)when n = 100000 eratosthenes_sieve(9592): 999(us) euler_sieve(9592): 0(us)when n = 1000000 eratosthenes_sieve(78498): 13004(us) euler_sieve(78498): 7004(us)when n = 10000000 eratosthenes_sieve(664579): 185130(us) euler_sieve(664579): 79067(us)when n = 100000000 eratosthenes_sieve(5761455): 2363692(us) euler_sieve(5761455): 842592(us)when n = 1000000000 eratosthenes_sieve(50847534): 25535159(us) euler_sieve(50847534): 8987385(us)
差距还是蛮大的呢。
- 埃氏筛法和欧拉筛法的区别
- 和和的区别
- & 和 &&、|和||的区别:
- &和&&、|和||的区别
- &&和&,||和|的区别
- &和&&、|和||的区别
- &和&&、|和||的区别
- ../和./和/的区别
- &和&&,|和||的区别
- "&"和"&&"、"|"和"||"的区别
- &和&& |和||的区别
- &和&&的区别 |和||的区别
- &&和&的区别,||和|的区别
- &和&&的区别以及|和||的区别
- &&和&的区别? | | 和|的区别?
- &和&&的区别,|和||的区别
- &和&&的区别及!和~区别
- '&'和'*' (C++)的区别
- 初识Spark 1.6.0
- JavaScript第四天
- happens-before俗解
- android应用开发架构概述
- CoordinatorLayout 之 AppBarLayout
- 埃氏筛法和欧拉筛法的区别
- android 简单写了一个缓冲图片 json json数组,对象的工具类
- 欢迎使用CSDN-markdown编辑器
- 测试Node.js 应用程序
- Android开发中,变色状态栏
- @PathVariable和@RequestParam的区别,@SessionAttributes
- varchar与char的区别
- 某些车载蓝牙无法通过PBAP获取手机SIM卡联系人
- css-selectors