第四章 数学知识——数论
1、质数
判断一个数是质数:当这个数大于1,且只包含1和它本身这两个约数。
1)质数的判定——试除法:i从2到sqrt (n)就可判断,但是不推荐直接用sqrt,因为它很慢,也不推荐i*i<=n,因为可能会有溢出风险。
#include<iostream>
#include<algorithm>
using namespace std;
bool isprime(int n)
{
if(n<2) return false;
for(int i=2;i<=n/i;i++)
{
if(n%i == 0) return false;
}
return true;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
if(isprime(x)) puts("Yes");
else puts("No");
}
return 0;
}
2)分解质因数——短除法
#include<iostream>
#include<algorithm>
using namespace std;
void divide(int n)
{
for(int i=2;i<=n/i;i++)
{
if(n%i == 0)
{
int s=0;
while(n%i==0)
{
n/=i;
s++;
}
cout<<i<<" "<<s<<endl;
}
}
if(n>1)
cout<<n<<" 1"<<endl;
puts("");
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
divide(x);
}
return 0;
}
3)筛质数
朴素:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int prime[N], cnt;
bool st[N];
int cntprime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prime[cnt++] = i;
}
for(int j=i+i;j<=n;j+=i) st[j] = true;
}
return cnt;
}
int main()
{
int n;
cin>>n;
cout<<cntprime(n)<<endl;
return 0;
}
时间复杂度计算: O(nlnn)
优化:埃式筛,只需要把质数的倍数晒去即可,因为质数的倍数是合数。
int cntprime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prime[cnt++] = i;
for(int j=i+i;j<=n;j+=i) st[j] = true;
}
}
return cnt;
}
时间复杂度优化到 约等于o(n)实际上是O(nloglogn)
线性筛:合数一定会被筛掉,核心思路:每个数n只会被它的最小质因子筛掉。因为合数一定存在最小质因子
int cntprime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) prime[cnt++] = i;
for(int j=0;prime[j]<=n/i;j++)
{
st[prime[j]*i] = true;
if(i%prime[j] == 0) break;//prime[j]一定是i的最小质因子
}
}
return cnt;
}
n小时与埃式筛相近,n大时比埃式筛快很多
2、约数
1)求约数——试除法,约数也成对出现,如果当前数不是n的开根号,就把i和n/i都加入变长数组res,复杂度是根号n。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> getdivision(int n)
{
vector<int> res;
for(int i=1;i<=n/i;i++)
{
if(n%i == 0)
{
res.push_back(i);
if(i*i!=n) res.push_back(n/i);
}
}
sort(res.begin(), res.end());
return res;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
vector<int> res=getdivision(x);
for(auto t:res) cout<<t<<" ";
cout<<endl;
}
return 0;
}
2)求约数的个数,每个数n可以看成p1的a1次方乘上p2的a2次方......,所以约数个数就是(a1+1)*(a2+1)...... ,因为第一项里面有p1的0次方,1次方,一直到a1次方。eg:99=3的平方乘以11.约数就位6个,分别是1x1=1,1x11=11,3x1=3,3x11=33,9x1=9,9x11=99;这六个
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
unordered_map<int, int> primes;
const int mod=1e9+7;
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
{
while(x%i==0)
{
x/=i;
primes[i]++;
}
}
if(x>1) primes[x]++;
}
long long int res=1;
for(auto primei:primes) res=res *(primei.second+1) % mod;
cout<<res<<endl;
return 0;
}
3)约数之和
思路上图图解
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
unordered_map<int, int> primes;
const int mod=1e9+7;
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
{
while(x%i==0)
{
x/=i;
primes[i]++;
}
}
if(x>1) primes[x]++;
}
long long int res=1;
for(auto primei:primes)
{
int p = primei.first, a = primei.second;
long long int t = 1;
while(a--)
{
t = (p * t + 1) % mod;
}
res = res * t % mod;
}
cout<<res<<endl;
return 0;
}
4)欧几里得算法——辗转相除法
a,b的最大公约数等于b和a mod b的最大公约数,只需要特判 b = 0的情况就好了。
#include<iostream>
using namespace std;
int gcd(int a, int b)
{
return b ? gcd(b, a%b) : a;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a, b;
cin>>a>>b;
cout<<gcd(a, b)<<endl;
}
return 0;
}