之前曾好几次学过数论,但是其中各种证明方法以及理论仍然是转眼间就忘。借着这次机会,要把模板以及常用的定理以及推论给掌握。今天讲的高斯素数、梅森素数都是第一次听说,回头要重新写一篇笔记详细学习一下。还有将正整数快速分解成质因数的Pollard_rho算法还没有掌握,也要写一篇笔记学习并整理。
欧几里得算法
找周期(循环节?)
#include
const int M = 1e6+10;
int a[M];
int m,k,tot = 0;
int gcd(int a,int b){
if(b == 0) return a;
return gcd(b,a%b);
}
void solve(){
tot = 0;
for(int i = 1;i <= m;i++)
if(gcd(m,i) == 1) a[tot++] = i;
int tmp = k/tot , r = k%tot-1;
if(r != -1) printf("%d\n",m*tmp+a[r]);
else printf("%d\n",m*(tmp-1)+a[tot-1]);
}
int main(){
while(scanf("%d%d",&m,&k) != EOF){
solve();
}
return 0;
}
求不定方程整数根方法:
用拓展欧几里得算法求得所有的解,其中|x|+|y|最小的为所求答案。
来自老师的代码,用的方法是:先令一个数为正,再求另一个解,然后取最小值。
#include
#include
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int gcd = exgcd(b, a%b, y, x);
y -= a / b * x;
return gcd;
}
int main() {
int a, b, d, x, y, x1, y1, x2, y2;
while (~scanf("%d%d%d", &a, &b, &d) && (a||b||d)) {
int gcd = exgcd(a, b, x, y);
a /= gcd, b /= gcd, d /= gcd, x *= d, y *= d;
x1 = (x%b+b) % b, y1 = (d - a*x1) / b;
y2 = (y%a+a) % a, x2 = (d - b*y2) / a;
if (x1+abs(y1) > abs(x2)+y2) x1 = abs(x2), y1 = y2;
printf("%d %d\n", abs(x1), abs(y1));
}
return 0;
}
拓展欧几里得,然后对xy进行讨论,看能够用c替代a+b。
简单的模运算规则应用。
#include
typedef long long ll;
int z,m,k;
ll qpow(ll a,ll b,ll p){
ll res = 1;
while(b){
if(b&1) res = res*a%p;
a = a*a%p;
b >>= 1;
}
return res;
}
int main(){
scanf("%d",&z);
while(z--){
scanf("%d%d",&m,&k);
ll ans = 0;
for(int i = 1,x,y;i <= k;i++){
scanf("%d%d",&x,&y);
ans = (ans+qpow(x,y,m))%m;
}
printf("%lld\n",ans);
}
return 0;
}
费马小定理的简单应用。
#include
#include
typedef long long ll;
int testnum[] = {2,3,5,61,7,11,13,19};
inline ll fmul(ll a,ll b,ll p){
return a*b%p;
}
ll qpow(ll a,ll b,ll m){ //快速幂算法
ll res = 1;
while(b){
if(b&1) res = fmul(res,a,m);
a = fmul(a,a,m);
b >>= 1;
}
return res;
}
bool isPrime(ll n){
if(n == 2 || n == 3 || n == 5) return true;
if(n < 2 || n%2 == 0) return false;
ll d = n-1 , a, x, y; int t = 0;
while((d&1) == 0) d >>= 1,t++;
//此时b为没有因子2的奇数
for(int i = 0;i < 5;i++){
a = testnum[i];
if(n == a) return true;
x = qpow(a,d,n);
//我们要使得所有的a(^(2^r)*d)都满足二次探测定理
for(int j = 0;j < t;j++){
y = fmul(x,x,n);
if(y == 1 && x != 1 && x != n-1) return false;
x = y;
}
if(x != 1) return false;//不满足费马小定理
}
return true;
//当x=n-1,或b为奇数时返回true;
}
int main(){
ll a,p;
while(~scanf("%lld%lld",&p,&a) && (a || p)){
if(isPrime(p)) puts("no");
else{
if(qpow(a,p,p) == a) puts("yes");
else puts("no");
}
}
return 0;
}
Miller-Rabin模板题。
#include
#include
const int N = 1e6+10;
int num[N];
typedef long long ll;
int testnum[] = {2,3,5,5,7,11,13,19};
inline ll fmul(ll a,ll b,ll p){
return a*b%p;
}
ll qpow(ll a,ll b,ll m){ //快速幂算法
ll res = 1;
while(b){
if(b&1) res = fmul(res,a,m);
a = fmul(a,a,m);
b >>= 1;
}
return res;
}
bool isPrime(ll n){
if(n == 2 || n == 3 || n == 5) return true;
if(n < 2 || n%2 == 0) return false;
ll d = n-1 , a, x, y; int t = 0;
while((d&1) == 0) d >>= 1,t++;
//此时b为没有因子2的奇数
for(int i = 0;i < 5;i++){
a = testnum[i];
if(n == a) return true;
x = qpow(a,d,n);
//我们要使得所有的a(^(2^r)*d)都满足二次探测定理
for(int j = 0;j < t;j++){
y = fmul(x,x,n);
if(y == 1 && x != 1 && x != n-1) return false;
x = y;
}
if(x != 1) return false;//不满足费马小定理
}
return true;
//当x=n-1,或b为奇数时返回true;
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
ll x;int ans = 0;
for(int i = 1;i <= n;i++){
scanf("%lld",&x);
if(isPrime(x)) ans++;
}
printf("%d\n",ans);
}
}
Pollard_rho算法以及Miller-Rabin算法的应用。
哥德巴赫猜想相关题,考察简单的素数筛,O(N log N)即可。
#include
typedef long long ll;
const int N = 1e6+10;
int isPrime[N];
int n;
void init(){
for(int i = 2;i*i < N;i++) if(!isPrime[i]){
for(int j = i*i;j < N;j += i) isPrime[j] = 1;
}
//for(int i = 1;i <= 100;i++) printf("%d:%d ",i,isPrime[i]);
}
void solve(){
for(int i = 2;i <= n/2;i++){
if(!isPrime[i] && !isPrime[n-i]){
printf("%d = %d + %d\n",n,i,n-i);
break;
}
}
}
int main(){
init();
while(~scanf("%d",&n) && n){
solve();
}
return 0;
}
类似于上一题,因为我们已知任意一个大于4的偶合数都可以分为俩个素数的和(哥德巴赫猜想),那么显然小于8的就无解,而大于8的数,可以根据其奇偶选择-4(2 、 2)或-5(2 、3),再将剩下的偶合数分解即可。
#include
const int N = 1e7+10;
int isPrime[N];
int n;
void init(){
for(int i = 2;i*i < N;i++) if(!isPrime[i]){
for(int j = i*i;j < N;j += i) isPrime[j] = 1;
}
//for(int i = 1;i <= 100;i++) printf("%d:%d ",i,isPrime[i]);
}
void solve(){
if(n&1){
n -= 5;
printf("2 3 ");
} else {
n -= 4;
printf("2 2 ");
}
for(int i = 2;i <= n/2;i++){
if(!isPrime[i] && !isPrime[n-i]){
printf("%d %d\n",i,n-i);
break;
}
}
}
int main(){
init();
while(scanf("%d",&n)!=EOF){
if(n < 8) puts("Impossible.");
else solve();
}
return 0;
}
线性筛+二分查找
#include
const int N = 2e6+10;
int primes[N],v[N],tot;
void init(){
for(int i = 2;i < N;i++){
if(!v[i]){
v[i] = i;
primes[tot++] = i;
}
for(int j = 0;j < tot;j++){
if(primes[j]*i >= N || primes[j] > v[i]) break;
v[i*primes[j]] = primes[j];
}
}
//for(int i = 0;i < 100;i++) printf("%d ",primes[i]);
}
int n;
void solve(){
int l = 0,r = tot;
while(l <= r){
int mid = l+r>>1;
if(primes[mid] > n) r = mid-1;
else l = mid+1;
}
if(primes[r] == n) puts("0");
else printf("%d\n",primes[r+1]-primes[r]);
}
int main(){
init();
while(~scanf("%d",&n) && n){
solve();
}
return 0;
}
想办法求出真约数即可。
梅森素数的题。