【數論】素數
素數:
素數指的是:因子只有 1 和它本身。
素數的分佈:
素數分佈的應用
例題:素數個數的位數(nefu 117)
素數個數的位數
Description
小明是一個聰明的孩子,對數論有著很濃烈的興趣。他發現求1到正整數10n 之間有多少個素數是一個很難的問題,該問題的難以決定於n 值的大小。現在的問題是,告訴你n的值,讓你幫助小明計算小於10n的素數的個數值共有多少位?
Input
輸入資料有若干組,每組資料包含1個整數n(1 < n < 1000000000),若遇到EOF則處理結束。
Output
對應每組資料,將小於10n 的素數的個數值的位數在一行內輸出,格式見樣本輸出。同組資料的輸出,其每個尾數之間空一格,行末沒有空格。
Sample Input
3
7
Sample Output
3
6
#include<cstdio> #include<cmath> #include<iostream> using namespace std; int main() { double n; double t; while(cin>>n){ t=double(n-log10(n)-log10(log(10))); cout<<int(t)+1<<endl; } return 0; }
例題:哥德巴赫猜想(nefu 2):
題目連結
Description
哥德巴赫(Goldbach ]C.,1690.3.18~1764.11.20)是德國數學家;出生于格奧尼格斯別爾格(現名加裡寧城);曾在英國牛津大學學習;原學法學,由於在歐洲各國訪問期間結識了貝努利家族,所以對數學研究產生了興趣;曾擔任中學教師。1725年,到了俄國,同年被選為彼得堡科學院院士;1725年~1740年擔任彼得堡科學院會議祕書;1742年,移居莫斯科,並在俄國外交部任職。
1742年,哥德巴赫在教學中發現,每個不小於6的偶數都是兩個素數(只能被1和它本身整除的數)之和。如6=3+3,14=3+11等等。公元1742年6月7日哥德巴赫寫信給當時的大數學家尤拉,尤拉在6月30日給他的回信中說,他相信這個猜想是正確的,但他不能證明。敘述如此簡單的問題,連尤拉這樣首屈一指的數學家都不能證明,這個猜想便引起了許多數學家的注意。從哥德巴赫提出這個猜想至今,許多數學家都不斷努力想攻克它,但都沒有成功。
我們不需要你去證明哥德巴赫猜想。
如果哥德巴赫猜想是正確的,一個(不小於6的)偶數,都是兩個素數之和。那麼這個偶數能被至少一個素數對錶示,如14,即可以表示為14=3+11,也可以表示為14=7+7。不同的偶數對應的素數對的數目是不一樣的,如偶數6,就只能表示為6=3+3。對於每個給定的偶數,我們希望知道有多少素數對的和等於該偶數。
Input
有多組測試資料。每組測試資料佔一行,包含唯一的一個正偶數n.(6 <= n <= 2^24,)。 輸出以EOF結束。
Output
對於每個輸入的偶數,輸出一行包含唯一的一個整數:表示有多少個素數對的和是輸入的偶數。
Sample Input
6
14
Sample Output
1
2
【題解】:
直接暴力即可,首先我們需要打表出來,自愧不如,原來自己素數打表現在還是出毛病。
書本上標準的答案:
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
const int N=16777226;
bool x[N];
int main()
{
memset(x,true,sizeof(x));
int e=static_cast < int > (sqrt((float)N));
x[0]=x[1]=false;
for(int i=4;i<=N;i+=2){
x[i]=false;
}
for(int i=3;i<=e;i+=2){
if(x[i]){
for(int j=i*i;j<=N;j+=i){
x[j]=false;
}
}
}
int n;
while(scanf("%d",&n)!=EOF&&n){
int ans=0;
for(int i=2;i<n/2+1;i++){
if(x[i]&&x[n-i]){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
埃拉托色尼篩法:
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
const int N=16777226;
typedef long long ll;
bool x[N];
int main()
{
memset(x,true,sizeof(x));
x[0]=x[1]=false;
for(ll i=2;i<N;i++){
if(x[i]){
for(ll j=i*i;j<N;j+=i){
x[j]=false;
}
}
}
int n;
while(scanf("%d",&n)!=EOF&&n){
int ans=0;
for(int i=2;i<n/2+1;i++){
if(x[i]&&x[n-i]){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
6N±1 篩選法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6;
int prime[N],cnt=0;
inline bool Isprime(int x){
if(x==2)return true;
if(x%2==0){
return false;
}
for(ll i=3;i*i<=x;i+=2){
if(x%i==0){
return false;
}
}
return true;
}
void is_prime(){
int t=0;
prime[t++]=2;
prime[t++]=3;
for( ll i=6;i<=N;i+=6){
for( ll j = -1; j <=1 ; j +=2){
if(Isprime(i+j)){
prime[t++]=(i+j);
}
}
}
printf("%d\n",t);
cnt=t;
}
int main()
{
int t1,t2;
t1=clock();
is_prime();
t2=clock();
printf("%d\n",t2-t1);
/*for(int i=0;i<100;i++){
printf(" :%d\n",prime[i]);
}*/
return 0;
}
分拆素數和(hdu 2098)
Problem Description
把一個偶數拆成兩個不同素數的和,有幾種拆法呢?
Input
輸入包含一些正的偶數,其值不會超過10000,個數不會超過500,若遇0,則結束。
Output
對應每個偶數,輸出其拆成不同素數的個數,每個結果佔一行。
Sample Input
30
26
0
Sample Output
3
2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+1e5;
bool x[N];
bool Isprime(ll k){
if(k%2==0){
return false;
}
for(ll i=3;i*i<=k;i+=2){
if(k%i==0) return false;
}
return true;
}
void is_prime(){
memset(x,false,sizeof(x));
x[2]=x[3]=true;
int t=2;
for(ll i=6;i<=N;i+=6){
for(ll j=-1;j<=1;j+=2){
if(Isprime(i+j)){
x[i+j]=true;
t++;
}
}
}
//printf("%d\n",t);
}
int main()
{
is_prime();
int n;
while(scanf("%d",&n)!=EOF&&n){
int ans=0;
for(int i=2;i<=n/2;i++){
if(x[i]==true&&x[n-i]==true&&i+i!=n){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
例題: 雲之遙--素數(nefu 109)
你能判斷1個數是不是素數嗎?
Input
輸入資料有多組,每組1個數N,這裡N是大於0且小於1000的整數。
Output
是素數,輸出YES,否則輸出NO,注意大小寫啊!
Sample Input
1
2
3
4
Sample Output
NO
YES
YES
NO
#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=1e6;
int prime[N],x[N],cnt=0;
void is_prime(){
int t=0;
for( ll i=2;i<N;i++){
if(x[i]==0){
x[i]=1;
prime[t++]=i;
for( ll j=i*i ; j<N; j+=i){
x[j]=2;
}
}
}
cnt=t;
}
int main()
{
is_prime();
int n;
while(scanf("%d",&n)!=EOF){
if(n<N){
x[n]==1?puts("YES"):puts("NO");
continue;
}
int flag=1;
for( int i=0;i<cnt;i++){
if( n%prime[i] ==0 ){
flag=0;
break;
}
}
flag?puts("YES"):puts("NO");
}
return 0;
}
Prime Distance
Description
The branch of mathematics called number theory is about properties of numbers. One of the areas that has captured the interest of number theoreticians for thousands of years is the question of primality. A prime number is a number that is has no proper factors (it is only evenly divisible by 1 and itself). The first prime numbers are 2,3,5,7 but they quickly become less frequent. One of the interesting questions is how dense they are in various ranges. Adjacent primes are two numbers that are both primes, but there are no other prime numbers between the adjacent primes. For example, 2,3 are the only adjacent primes that are also adjacent numbers.
Your program is given 2 numbers: L and U (1<=L< U<=2,147,483,647), and you are to find the two adjacent primes C1 and C2 (L<=C1< C2<=U) that are closest (i.e. C2-C1 is the minimum). If there are other pairs that are the same distance apart, use the first pair. You are also to find the two adjacent primes D1 and D2 (L<=D1< D2<=U) where D1 and D2 are as distant from each other as possible (again choosing the first pair if there is a tie).
Input
Each line of input will contain two positive integers, L and U, with L < U. The difference between L and U will not exceed 1,000,000.
Output
For each L and U, the output will either be the statement that there are no adjacent primes (because there are less than two primes between the two given numbers) or a line giving the two pairs of adjacent primes.
Sample Input
2 17
14 17
Sample Output
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
【題意】:求一個區域內,最近,最遠的兩個素數距離。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e6;
const int inf=0x3f3f3f3f;
int prime[N],x[N],cnt=0;
ll L,R;
void is_prime(){
int t=0;
for( ll i=2;i<N;i++){
if(x[i]==0){
x[i]=1;
prime[t++]=i;
for( ll j=i*i ; j<N; j+=i){
x[j]=2;
}
}
}
cnt=t;
}
int prime2[N],cnt2=0;
bool x2[N];
void is_prime2(){
memset(x2,1,sizeof(x2));
ll b;
for(int i=0;i<cnt;i++){
b=L/prime[i];
while(b*prime[i]<L||b<=1) { b++; }
for(ll j=b*prime[i] ; j<=R ; j+=prime[i] ){
if(j>=1){ x2[ j - L ]=0; }
}
}
if(L==1){
x2[0]=0;
}
}
int main()
{
is_prime();
while(~scanf("%d%d",&L,&R)){
is_prime2();
int tot=0;
for(ll i=L;i<=R;i++){
if(x2[i-L]==1){
prime2[tot++]=i;
//printf("%d\n",i);
}
}
if(tot<2){
printf("There are no adjacent primes.\n");
}else{
int min_L=prime2[0],min_R=prime2[1];
int max_L=prime2[0],max_R=prime2[1];
for(int i=1;i<tot;i++){
int tmp=prime2[i]-prime2[i-1];
if(tmp<(min_R-min_L)){
min_L=prime2[i-1];
min_R=prime2[i];
}
if(tmp>(max_R-max_L)){
max_L=prime2[i-1];
max_R=prime2[i];
}
}
printf("%d,%d are closest, %d,%d are most distant.\n",min_L,min_R,max_L,max_R);
}
}
return 0;
}
算術基本定理:
簡單來說就是,所有的數除{ 0,1,-1 }都可以用 質數來表示。
標準型:
性質一: 設d( n ) 為n的正因子的個數
性質二: 設 為 n 的所有因子之和,則有:
性質三: lcm(a,b) = (a*b) / gcd(a,b)
性質四: n! 的素因子分解中的素數p的冪為:
n!後面有多少個0
Description
從輸入中讀取一個數n,求出n!中末尾0的個數。
Input
輸入有若干行。第一行上有一個整數m,指明接下來的數字的個數。然後是m行,每一行包含一個確定的正整數n,1<=n<=1000000000。
Output
對輸入行中的每一個數據n,輸出一行,其內容是n!中末尾0的個數。
Sample Input
3
3
100
1024
Sample Output
0
24
253
【題解】:
因為只有2和5相乘才能得到10
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll Five[17]={1},Two[33]={1};
int main()
{
for(ll i=1;i<=13;i++){
Five[i]=Five[i-1]*5;
}
for(ll i=1;i<=31;i++){
Two[i]=Two[i-1]*2;
}
ll n;
int T;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
ll cnt_2=0,cnt_5=0;
for(int i=1;i<=31;i++){
cnt_2=(cnt_2+n/Two[i]);
}
for(int i=1;i<=13;i++){
cnt_5=(cnt_5+n/Five[i]);
}
ll ans=min(cnt_2,cnt_5);
printf("%lld\n",ans);
}
return 0;
}
Description
小明的爸爸從外面旅遊回來給她帶來了一個禮物,小明高興地跑回自己的房間,拆開一看是一個很大棋盤(非常大),小明有所失望。不過沒過幾天發現了大棋盤的好玩之處。從起點(0,0)走到終點(n,n)的非降路徑數是C(2n,n),現在小明隨機取出1個素數p, 他想知道C(2n,n)恰好被p整除多少次?小明想了很長時間都沒想出來,現在想請你幫助小明解決這個問題,對於你來說應該不難吧!
Input
有多組測試資料。
第一行是一個正整數T,表示測試資料的組數。接下來每組2個數分別是n和p的值,這裡1<=n,p<=1000000000。
Output
對於每組測試資料,輸出一行,給出C(2n,n)被素數p整除的次數,當整除不了的時候,次數為0。
Sample Input
2
2 2
2 3
Sample Output
1
1
【題解】:直接利用公式即可。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
typedef long long ll;
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1){
ans=ans*a;
}
a=a*a;
b>>=1;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
ll n,p;
scanf("%lld%lld",&n,&p);
double t=(log(2*n)/log(p));
//printf("%f\n",t);
ll tt=ceil(t);
ll ans=0;
for(int i=1;i<=tt;i++){
ll tmp=((2*n)/qpow(p,i))-(2*(n/qpow(p,i)));
ans=ans+tmp;
}
printf("%lld\n",ans);
}
return 0;
}
Description
由於梅森學識淵博,才華橫溢,為人熱情以及最早系統而深入地研究2p-1 型的數(其中p為素數),為了紀念他,數學界就把這種數稱為“梅森數”;並以Mp 記之(其中M為梅森姓名的首字母),即Mp=2p-1 。如果梅森數為素數,則稱之為“梅森素數”。 比如p=2,3,5,7時,Mp都是素數,但211-1 不是素數 。現在請你求出前N個梅森素數。
Input
有多組測試資料。
第一行是一個正整數T,表示測試資料的組數。接下來每組1個數p的值,這裡2<= p <= 62。
Output
對於每組測試資料,判斷Mp 是不是梅森素數,是就輸出“yes ”,否就輸出“no”,輸出後要換行。
Sample Input
2
2
7
Sample Output
yes
yes
【Lucas-Lehmer 判定法】:
設p是素數,第p個梅森數位為: ,
對於 k>=2,利用 :
複雜度 :O(p^3)
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll qmul(ll a, ll b,ll mod){
ll ans=0;
while(b>0){ //注意 b>0!!!並不是 b!=0
if(b&1){
ans=(ans+a)%mod;
}
a=(a+a)%mod;
b>>=1;
//printf("####\n");
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
ll p,n;
scanf("%lld",&n);
p=(1LL<<n)-1;
ll a[80]={0,4},tmp;
for(int i=2;i<=n-1;i++){
tmp=qmul(a[i-1],a[i-1],p);
//printf("%lld\n",tmp);
a[i]=(tmp-2)%p;
}
if(n==2){
printf("yes\n");
}else{
if(a[n-1]==0){
printf("yes\n");
}else{
printf("no\n");
}
}
}
return 0;
}
【Miller素數測試法】:
#include<stdio.h>
#include<time.h>
#include<iostream>
#define Times 12
using namespace std;
typedef long long ll;
ll random(ll n){
return (ll)( (double)rand()/RAND_MAX *n+0.5 );
}
ll qmul(ll a,ll b,ll mod){
ll ans=0;
while(b>0){
if(b&1){
ans=(ans+a)%mod;
}
b>>=1;
a=(a<<1)%mod;
}
return ans;
}
ll qpow(ll a,ll b,ll mod){
ll ans=1;
a%=mod;
while(b){
if(b&1){
ans=qmul(ans,a,mod);
}
b>>=1;
a=qmul(a,a,mod);
}
return ans;
}
bool Witness(ll a,ll n){
ll m=n-1;
int j=0;
while( !(m&1) ){
j++;
m>>=1;
}
ll x=qpow(a,m,n);
if( x==1 || x==n-1){
return false;
}
while(j--){
x=x*x%n;
if(x==n-1){
return false;
}
}
return true;
}
bool miller_rabin(ll n){
if(n<2) return false;
if(n==2) return true;
if( !(n&1) ) return false;
for (int i=1;i<=Times;i++){
ll a=random(n-2)+1;
if(Witness(a,n))return false;
}
return true;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
ll n,p;
scanf("%d",&n);
p = ( 1LL<<n ) - 1;
if(miller_rabin(p)){
printf("yes\n");
}else{
printf("no\n");
}
}
return 0;
}
Problem Description
Give you a lot of positive integers, just to find out how many prime numbers there are.
Input
There are a lot of cases. In each case, there is an integer N representing the number of integers to find. Each integer won’t exceed 32-bit signed integer, and each of them won’t be less than 2.
Output
For each case, print the number of prime numbers you have found out.
Sample Input
3
2 3 4
Sample Output
2
【題解】:模板題,直接套公式即可。
#include<time.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define Times 12
using namespace std;
typedef long long ll;
ll random(ll n){
return (ll) ( (double)rand()/RAND_MAX *n + 0.5 );
}
ll qmul(ll a,ll b,ll mod){
ll ans=0;
while(b>0){
if(b&1){
ans=(ans+a)%mod;
}
a=(a<<1)%mod;
b>>=1;
}
return ans;
}
ll qpow(ll a,ll b,ll mod){
ll ans=1;
while(b){
if(b&1){
ans=qmul(ans,a,mod);
}
b>>=1;
a=qmul(a,a,mod);
}
return ans;
}
bool Witness(ll a,ll n){
ll m=n-1;
int j=0;
while ( !(m&1) ){
j++;
m>>=1;
}
ll x= qpow(a,m,n);
if(x==1||x==n-1){
return false;
}while(j--){
x=x*x%n;
if(x==n-1){
return false;
}
}
return true;
}
bool miller_rabin(ll n ){
if( n<2 )return false;
if( n==2 )return true;
if( !(n&1) )return false;
for(int i=1;i<=Times;i++){
ll a=random(n-2)+1;
if(Witness(a,n))return false;
}
return true;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
int ans=0;
int t;
for(int i=0;i<n;i++){
scanf("%d",&t);
if(miller_rabin(t)){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
Goldbach (計蒜客)
Description:
Goldbach's conjecture is one of the oldest and best-known unsolved problems in number theory and all of mathematics. It states:
Every even integer greater than 2 can be expressed as the sum of two primes.
The actual verification of the Goldbach conjecture shows that even numbers below at least 1e14 can be expressed as a sum of two prime numbers.
Many times, there are more than one way to represent even numbers as two prime numbers.
For example, 18=5+13=7+11, 64=3+61=5+59=11+53=17+47=23+41, etc.
Now this problem is asking you to divide a postive even integer n (2<n<2^63) into two prime numbers.
Although a certain scope of the problem has not been strictly proved the correctness of Goldbach's conjecture, we still hope that you can solve it.
If you find that an even number of Goldbach conjectures are not true, then this question will be wrong, but we would like to congratulate you on solving this math problem that has plagued humanity for hundreds of years.
Input:
The first line of input is a T means the number of the cases.
Next T lines, each line is a postive even integer n (2<n<2^63).
Output:
The output is also T lines, each line is two number we asked for.
T is about 100.
#include<time.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define Times 4
const int N=5e6+10;
using namespace std;
typedef unsigned long long ll;
ll prime[N],cnt=0;
bool x[N];
void is_prime(){
int t=0;
for(ll i=2;i<N;i++){
if(x[i]==false){
prime[t++]=i;
for(ll j=i*i;j<N;j+=i){
x[j]=true;
}
}
}
cnt=t;
}
ll random(ll n){
return (ll) ( (double)rand()/RAND_MAX *n +0.5 );
}
ll qmul(ll a,ll b,ll mod){
ll ans=0;
while(b>0){
if(b&1){
ans=(ans+a)%mod;
}
a=(a<<1)%mod;
b>>=1;
}
return ans;
}
ll qpow(ll a,ll b,ll mod){
ll ans=1;
while(b){
if(b&1){
ans=qmul(ans,a,mod);
}
b>>=1;
a=qmul(a,a,mod);
}
return ans;
}
bool Witness(ll a,ll n){
ll m=n-1;
int j=0;
while ( !(m&1) ){
j++;
m>>=1;
}
ll x= qpow(a,m,n);
if(x==1||x==n-1){
return false;
}while(j--){
x=x*x%n;
if(x==n-1){
return false;
}
}
return true;
}
bool miller_rabin(ll n ){
if( n<2 )return false;
if( n==2 )return true;
if( !(n&1) )return false;
for(int i=1;i<=Times;i++){
ll a=random(n-2)+1;
//ll a=prime[i];
if(Witness(a,n))return false;
}
return true;
}
int main()
{
is_prime();
int T;
scanf("%d",&T);
while(T--){
ll n;
scanf("%llu",&n);
for(ll i=0;i<cnt;i++){
if(miller_rabin(n-prime[i])){
printf("%llu %llu\n",prime[i],n-prime[i]);
break;
}
}
}
return 0;
}