信奥日记——数论(快速幂、埃氏筛、欧拉筛)
文章目录
- 前情提要
- 正文
- 1.快速幂
- 思路
- 代码
- 完整代码(包含取模)
- 2.埃拉托斯特尼筛法(埃氏筛)
- 原理
- 思路
- 代码
- 完整代码(求 2 到 n 之间的素数)
- 3.线性筛
- 思路
- 代码
- 完整代码
前情提要
本人蒟蒻,这是我的第一篇blog,同时也是这个信奥日记系列的一篇文章,如有错误请神犇们多多包涵
我的个人blog
正文
1.快速幂
//朴素的求幂方法
for(int i = 1; i <= n; i++){ans *= x;
}
思路
根据我们小学二年级学过的知识可以知道
如果(a = b + c)
则 xa = ab + ac
通过这种方法可以把每一步的指数都分成两半,而相应的底数做平方运算。这样不仅能把大的指数变小,而且可以减少循环次数,达到减小时间复杂度的目的。
所以我们要想求 x^n 时只要 n 能转化为 2^i 次方即可得到 x^n = ((x2)2)^2…。
那么我们只要把 n 转换为二进制即可达成这一条件
举个例子
求3的14次方
14 的二进制为 1110
则 3^14 = 3^8 * 3^4 * 3^2
代码
int powd(int a,int k){//传值 a 为底数 k 为指数int ans = 1; //定义一个 ans 变量并将初值赋为 1 用来记录答案while(k){//k 不等于 0 if(k & 1) ans = ans * a; //计算k >>= 1; a = a * a; }return ans;//返回结果
}
如此一来快速幂的时间复杂度就能由O(N)变为O(log2N),与朴素的求法相比极大的节省了时间。
完整代码(包含取模)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>using namespace std;inline int read(){//快读int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;
}typedef long long ll;//重定义 long long 为 ll 以方便后面使用int powd(int a,int k,int mod){int res = 1;while(k){if(k & 1) res = (ll) res * a % mod;//运算时强制转换为 long long ,防止res 和 a 均为一个临近 int 边缘的值,导致 res * a 超过 int 上限,出现错误k >>= 1;a = (ll)a * a % mod; }return res % mod;
}int main(){int a,k,mod;a = read();k = read();mod = read();int ans;ans = powd(a,k,mod);printf("%d^%d mod %d=%d",a,k,mod,ans);return 0;
}
2.埃拉托斯特尼筛法(埃氏筛)
埃氏筛是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。
原理
要得到自然数n以内的全部素数,必须把不大于 √n 的所有素数的倍数剔除,剩下的就是素数。
思路
求 2 到 25 之间的素数
step1 : 列出 2 以后所有的数
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
step2 : 标出目前的第一个素数 2
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
step3 : 划掉 2 的倍数
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
step4 : 如果这个序列中最大数小于最后一个划掉的素数的平方,那么剩下的序列中所有的数都是素数,否则回到step2
25 大于 2 的平方,所以返回step2
step5 : 标出目前的第一个素数 3
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
step6 : 划掉 3 的倍数
……
最终我们在划掉 5 的倍数后
23 小于 5 的平方,跳出循环
目前序列中的每个元素为
2 3 5 7 11 13 17 19 23
全部是素数
代码
void get_primes(int n){//传值 nfor(int i = 2; i <= n; i++){//从 2 开始循环if(!prime[i]){//prime[i] 非 0cout << i << ",";//输出目前的第一个素数for(int j = i; j <= n/i; j++){//循环 √n 次, n/i等于√n,但不用调用sqrt函数可以节约资源prime[j * i] = 1;//打标记}}}
}
完整代码(求 2 到 n 之间的素数)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>using namespace std;inline int read(){//快读int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;
}const int N = 1e7;bool prime[N];void get_primes(int n){for(int i = 2; i <= n; i++){if(!prime[i]){cout << i << ",";for(int j = i; j <= n/i; j++){prime[j * i] = 1;}}}
}int main(){int n;n = read();get_primes(n);return 0;
}
时间复杂度为
O ( N ∗ l o n g ( l o n g n ) ) O(N * long(longn)) O(N∗long(longn))
证明见此 (反正我是没看懂 逃……
3.线性筛
思路
顾名思义,一种针对埃氏筛的优化算法,可以达到O(N)的复杂度
看刚才的埃氏筛不难发现,这里面似乎会对某些数标记了很多次其为合数,那么有没有方法能够省掉这些无意义的步骤呢?
追求时间有限的OIer当然要回答你:有!
只要能让每个合数都只被标记一次,那么时间复杂度就可以降到O(N)了。
所以我们只需要在 i % primes[j] == 0 时跳出循环即可
代码
void get_primes(int n){for(int i = 2; i <= n; i++){if(!prime[i])primes[cnt++] = i;for(int j = 0; primes[j] <= n/i; j++){prime[i * primes[j]] = 1;if(i % primes[j] == 0) break;//关键语句}}
}
完整代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>using namespace std;inline int read(){//快读int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;
}const int N = 1e8+5;bool prime[N];
int primes[N],cnt = 0;
int ans[N]; void get_primes(int n){for(int i = 2; i <= n; i++){if(!prime[i])primes[cnt++] = i;for(int j = 0; primes[j] <= n/i; j++){prime[i * primes[j]] = 1;if(i % primes[j] == 0)break;}}
}int main(){int n = read();int q = read(); get_primes(n);for(int i = 1;i <= q; i++){cin >> n;ans[i] = primes[n-1];}for(int i = 1;i <= q; i++){printf("%d\n",ans[i]);}return 0;
}
如此一来埃氏筛的时间复杂度就可以降到 O ( N ) 了 如此一来埃氏筛的时间复杂度就可以降到O(N)了 如此一来埃氏筛的时间复杂度就可以降到O(N)了
信奥日记——数论(快速幂、埃氏筛、欧拉筛)相关推荐
- 埃氏筛 线性筛(欧拉筛) 算法解析
埃氏晒 埃拉托斯特尼筛法,简称埃氏晒,是一种用来求自然数n以内的全部素数. 他的基本原理是,如果我们要获得小于n的所有素数,那就把不大于根号n的所有素数的倍数剔除. 埃氏晒的原理很容易理解,一个合数, ...
- 素数的线性筛法java,埃氏筛 线性筛(欧拉筛) 算法解析
埃氏晒 埃拉托斯特尼筛法,简称埃氏晒,是一种用来求自然数n以内的全部素数. 他的基本原理是,如果我们要获得小于n的所有素数,那就把不大于根号n的所有素数的倍数剔除. 埃氏晒的原理很容易理解,一个合数, ...
- 素数筛选法(埃氏筛 欧拉筛)
质数筛选法 文章目录 质数筛选法 前言 一.埃氏筛 O(nloglogn)O(nloglogn)O(nloglogn) 二.欧拉筛O(n)O(n)O(n) 总结 前言 当需要大范围内的素数时,例如1e ...
- 素数筛法详解:埃氏筛和欧拉筛
文章目录 摘要 埃式筛 欧拉筛 超级详细的基础算法和数据结构合集: https://blog.csdn.net/GD_ONE/article/details/104061907 摘要 本文主要介绍埃氏 ...
- 算法笔记--素数筛(朴素筛,埃式筛,欧拉筛)
素数 素数也叫质数,是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数.如2 , 3 , 5 , 7 , 11等. 素数筛 素数筛即筛选出1~n内的素数的方法,这里介绍三种 常见的求素数 ...
- 莫比乌斯函数的两种求法(基于欧拉筛、埃氏筛)
给出莫比乌斯函数的定义: 这里的n即u(i)中的i,即i=1时,u(1)=1:i大于1且i中某个质因子的幂超过1,则u(i)=0:否则,u(i)取决于其质因子个数的奇偶性. 这里给出两个莫比乌斯函数的 ...
- 埃氏筛与欧拉筛(线性筛)
目录 一.前言 二.埃氏筛与欧拉筛(线性筛) 1.问题描述 2.基本思路 (1)埃氏筛法 (2)欧拉筛法 三.题例 1.上链接 2.简单思路 3.代码 (1)埃氏筛python版 (2)欧拉筛pyth ...
- 筛选质数,埃氏筛和欧拉筛(线性筛)
求len之内的所有的素数 除了比较常用的开根号的求法,还有两种更好的方法,埃氏筛和线性筛.其中埃氏筛更好理解,而线性筛(欧拉筛)不好理解但是更快. 埃氏筛 #include <bits/stdc ...
- 牛客 华华给月月出题 (积性函数+欧拉筛+快速幂)
题目描述 华华刚刚帮月月完成了作业.为了展示自己的学习水平之高超,华华还给月月出了一道类似的题: ⊕符号表示异或和,详见样例解释. 虽然月月写了个程序暴力的算出了答案,但是为了确保自己的答案没有错,希 ...
最新文章
- 网站性能优化的常用方法
- 厉害!84 行 JavaScript 代码实现塔式堆叠游戏
- 网页图表Highcharts实践教程之图表区
- 方案一TCP 完成聊天室的编写
- 最小生成树 Kruskal算法 Prim算法
- byte转换字符串(string)+字符串转换byte
- EF 数据库连接约定(Connection String Conventions in Code First)
- cc2530设计性实验代码三
- kaggle数据集下载
- nginx启用reuseport
- 如何解决注塑成型中的毛边问题?
- matlab 矩阵分行标准化,matlab如何将矩阵标准化 命令是什么?
- 2013年2月工作小结 -- 最美工作月
- 生鲜电商之百果园的未来
- 到底什么是非线性优化?
- 《被讨厌的勇气》摘抄
- 软件测试自学毛笔字纹身,横眉冷对千夫指 俯首甘为孺子牛的毛笔楷书和行书...
- T-SQL Recipes之Database Backups
- 用批处理文件检测u盘并进行复制
- Linux用户标识符UID与GID和用户账号