文章目录
- (一)埃氏筛法
- 1. 原理
- 2. 代码
- 3. 特点
- (二)欧拉筛法
- 1. 原理
- 2. 代码
- 3. 特点
- (三)分解质因数
- 1. 原理
- 2. 代码
- (四)斐波那契数列
- 1. 递推式
- 2. 代码
- (1) 方法1
- (2) 方法2
经过12天的“魔鬼集训”,我又回来更新了。这篇文章是关于算法的
(一)埃氏筛法
1. 原理
它是用来筛选质数的,从2开始看(因为1不是质数也不是合数),2是质数,筛选掉所有是2的倍数的数,它们是合数,不知道什么是质数合数点我。随后看下一个没有被筛掉的数:3,再来筛掉,然后看数字5,把数字五的倍数筛掉…
2. 代码
#include <bits/stdc++.h>
using namespace std;
int a[1001]; //用于记录质数
bool prime[1001]; //质数筛
int cnt = 0; //质数的个数
void eldri() {
fill(prime+2, prime+1001, true); //除1以外,其他的都可能是质数
for(int i=2; i<=1000; i++) {
if(prime[i]) {//判断i有没有被筛掉
a[++cnt] = i;
for(int j=2*i; j<=1000; j+=i) //筛掉i的倍数
prime[j] = false;
}
}
}
int main() {
ios::sync_with_stdio(false); //加速代码
cin.tie(0);
cout.tie(0);
eldri(); //运行函数
cout << "1~1000中的质数有:";
for(int i=1; i<=cnt; i++)
cout << a[i] << " ";
cout << endl << "1~1000中质数的个数:" << cnt;
return 0;
}
3. 特点
优点: 比一个个筛质数更好
缺点: 会重复看某个合数
时间复杂度:
O
(
n
l
o
g
l
o
g
n
)
O(nloglogn)
O(nloglogn)
注意事项: 质数表和质数筛都得放在main()
函数外,一是无法访问,二才是段错误
段错误(segmentation fault)可以理解为函数内的变量占用字节太大了,也可能是访问了野指针(指向nullptr
或者NULL
)或者数组访问越界
毕竟main()
也是函数
(二)欧拉筛法
1. 原理
欧拉筛法的思想是每个合数只被它最小的质数筛掉,比如在看数字 2 2 2时,可以先不考虑筛掉 30 30 30,等到 15 15 15时再筛掉
2. 代码
#include <bits/stdc++.h>
using namespace std;
bool isp[1001]; //质数表
int prime[1001]; //质数
int cnt=0; //质数个数
void euler() {
fill(isp+2, isp+1001, true); //除1以外,其他的都可能是质数
for(int i=2; i<=1000; i++) {
if(isp[i])
prime[++cnt] = i;
for(int j=1; j<=cnt&&i*prime[j]<=1000; j++) { //质数合数都来筛
isp[i*prime[j]] = false; //筛掉
if(i%prime[j]==0) break; //超出有效范围,退出循环,也是欧拉筛法的关键
}
}
}
int main() {
ios::sync_with_stdio(false); //加速代码
cin.tie(0);
cout.tie(0);
euler(); //运行代码
cout << "1~1000间的质数:";
for(int i=1; i<=cnt; i++)
cout << prime[i] << ' ';
cout << endl << "1~1000间质数的个数:" << cnt;
return 0;
}
3. 特点
优点: 不会重复筛数
时间复杂度:
O
(
n
)
O(n)
O(n)
注意事项: 分清楚里面的i
和j
(三)分解质因数
1. 原理
以一个数 84 84 84为例,假如被 2 2 2筛到不能筛时,就不可能被 4 , 6 , 8 4, 6, 8 4,6,8 整除了
2. 代码
#include <iostream>
using namespace std;
void split(int n) {
cout << n << '=';
int t = 2; //从2开始筛
bool flag = false; //是否输出'*'
while(n>1) {
if(n%t==0) { //n还能整除t
n /= t;
if(flag++) cout << '*';
cout << t;
} else { //不可以的话找下一个质数
t ++;
}
}
}
int main() {
int n;
cin >> n;
split(n); //调用函数
return 0;
}
(四)斐波那契数列
1. 递推式
F
i
b
n
=
F
i
b
n
−
1
+
F
i
b
n
−
2
Fib_{n}=Fib_{n-1}+Fib_{n-2}
Fibn=Fibn−1+Fibn−2
边界条件:
F
i
b
1
=
1
,
F
i
b
2
=
1
Fib_{1}=1, Fib_{2}=1
Fib1=1,Fib2=1
2. 代码
(1) 方法1
#include <iostream>
#define int unsigned long long //宏定义
using namespace std;
int fib(int n) {
if(n==1||n==2) return 1; //边界条件
else return fib(n-1)+fib(n-2); //递归式
}
signed main() { //signed代替int
int n;
cin >> n;
cout << fib(n); //调用函数
return 0;
}
但是这样会重复算一些数字
f
i
b
4
=
f
i
b
3
+
f
i
b
2
=
f
i
b
2
+
f
i
b
1
+
f
i
b
2
=
1
+
1
+
1
=
3
fib_4=fib_3+fib_2 = fib_2+fib_1+fib_2=1+1+1=3
fib4=fib3+fib2=fib2+fib1+fib2=1+1+1=3
这个式子重复计算了
f
i
b
2
fib_2
fib2
我们可以仿造埃氏筛,做一个用于存储数值的数组
(2) 方法2
#include <iostream>
#define int unsigned long long //宏定义
using namespace std;
int a[51] = {};
int fib(int n) {
if(n==1||n==2) return 1; //边界条件
else if(a[n]>0) return a[n]; //寄存了a[n]
else return a[n]=fib(n-1)+fib(n-2); //二合一,表示返回值并赋值
}
signed main() { //signed代替int
int n;
cin >> n;
cout << fib(n); //调用函数
return 0;
}
这样就能解决这个问题了!
预览:
- 十九:指针与迭代器
- 二十:位运算与进制
- 二十一:联合体(union)
- 二十二:类(class)
- 二十三:高精度运算
- 二十四:算法进阶
- 二十五:递归
- 二十六:
vector
容器 - 二十七:递推
- 二十八:
set
容器 - 二十九:
map
容器
…