质数应用问题
首先我们对素数的判定应该已经很熟悉了。但很可能我们中大多数对质数的应用方面还是有很多不了解的地方。判断一个数是否是质数在数学问题中到底有什么应用了,让我们拭目以待。
-
素数的判定
#include <stdio.h> #include <math.h> bool judge(int x){ if(x <= 1) return false; int bound = (int)sqrt(x)+1; // 计算枚举上界,为防止double值带来的精度损失,所以采用根号值取整后再加1,即宁愿多枚举一个数也不能少枚举一个数 for(int i = 2 ; i < bound ; i++){ if(x % i == 0) return false; } return true; } int main(){ int x; while(scanf("%d",&x)!=EOF){ puts(judge(x)?"yes":"no"); } return 0; }
-
素数筛选法
从2开始遍历到n的所有整数,若当前整数没有因为它是某个小于其的素数的倍数而被标记成非素数,则判定其为素数,并标记它所有的倍数为非素数,然后继续遍历下一个数,直到遍历完所有的整数。此时,所有没被标记成非素数的数字即为我们要求的素数。这种算法被我们称为素数筛选法。
题目描述:
输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。
输入:
输入有多组数据。
每组一行,输入n。
输出:
输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数(素数之间用空格隔开,最后一个素数后面没有空格),如果没有则输出-1。
样例输入:
100
样例输出:
11 31 41 61 71
代码如下:
#include <iostream> #include <cstdio> using namespace std; bool mark[10001]; // 若mark[x]为true,则表示该数x已被标记成非素数 int prime[10001]; // 保存筛选的素数 int primeSize; // 保存筛选的素数的个数 void init(){ for(int i = 0 ; i <= 10000 ; i++){ mark[i] = false; } primeSize = 0; for(int i = 1 ; i <= 10000 ; i++){ if(mark[i] == true) continue; // 若这个数是非素数,则跳过 prime[primeSize++] = i; // 否则,得到一个新的素数 for(int j = i*i ; j <= 10000 ; j+=i){ mark[j] = true; // 并将该数的所有倍数均标记成非素数 } } } int main(){ init(); int n while(scanf("%d",&n)!=EOF){ for(int i = 0 ; i < primeSize ; i++){ if(prime[i] < n && prime[i] % 10 == 1){ if(i != primeSize-1){ printf("%d ",prime[i]); }else{ printf("%d",prime[i]); } } } if(primeSize == 0){ printf("-1\n"); }else{ printf("\n"); } } return 0; }
-
分解素因数
题目描述:
求正整数N(N>1)的质因数的个数
相同的质因数需要重复计算。如120 = 2 * 2 * 2 * 3 * 5,共有5个质因数
输入:
可能有多组测试数据,每组测试数据的输入是一个正整数N,(1<N<10^9)。
输出:
对于每组数据,输出N的质因数的个数。
样例输入:
120
样例输出:
5
代码如下:
#include <iostream> #include <cstdio> using namespace std; bool mark[100001]; int prime[100001]; int primeSize; void init(){ // 素数筛选法 for(int i = 0 ; i <= 100000 ; i++){ mark[i] = false; } primeSize = 0; for(int i = 2 ; i <= 100000 ; i++){ if(mark[i] == true) continue; prime[primeSize++] = i; for(int j = i*i ; j <= 100000 ; j += i){ mark[j] = true; } } } int main(){ init(); int n; while(scanf("%d",&n)!=EOF){ int ansPrime[30]; // 按顺序保存分解出的素因数 int ansSize = 0; // 分解出不同素因数的个数 int ansNum[30]; // 保存分解出的素因数对应的幂指数 for(int i = 0 ; i < primeSize ; i++){ // 依次测试每一个素数 if(n % prime[i] == 0){ ansPrime[ansSize] = prime[i]; ansNum[ansSize] = 0; // 初始化幂指数为0; // 求当前素数的幂指数 while(n%prime[i] == 0){ ansNum[ansSize]++; n /= prime[i]; } ansSize++; if(n == 1) break; // 若已被分解成1,则分解提前终止 } } if(n != 1){ // 若测试完2到100000内所有素因数,n仍未被分解至1,则剩余的因数一定是n ansPrime[ansSize] = n; ansNum[ansSize++] = 1; } int ans; for(int i = 0 ; i < ansSize ; i++){ ans += ansNum[i]; } printf("%d\n",ans); } return 0; }
-
整除问题
题目描述:
给定n,a求最大的k,使n!可以被ak整除但不能被a(k+1)整除。
输入:
两个整数n(2<=n<=1000),a(2<=a<=1000)
输出:
一个整数
样例输入:
6 10
样例输出:
1
要解决该例,我们首先应注意到这样一个问题,n!和a的k次幂可能数值非常巨大,而不能被int(甚至long long)保存,也就不能直接用求余数操作判断它们是否存在整除关系。
算法思想
若a能整除b,即b/a,我们只需要依次测试a中每一个素因数,确定b中该素因数对应的幂指数是a中幂指数的几倍(利用整数除法),这样所有倍数中最小的那个即为我们要求的k。
分析到这里,剩余的工作似乎只对a和n!分解素因数,对a分解素因数在上面已经探讨了。那么对于n!呢?千万不要指望将n!计算出来后再类似a一样对其分解质因数,对于n!数值非常巨大(当n>30时),想要这样的操作几乎是不可能的,那么我们该如何对其分解素因数呢?试着考虑n!中含有素因数p的个数,即确定素因数p对应的幂指数。我们可以知道,n!中包含了1到n区间内所有整数的乘积,这些乘积中每一个p的倍数(包括其本身)都将对n!贡献至少一个p因子,且在1到n中p的倍数共有n/p(整数除法)个,则p的因子数至少为n/p个,即有n/p个整数至少贡献了一个p因子。那么有多少个整数将贡献至少两个p因子呢,有了以上的分析读者应该知道所有p*p的倍数将为n!贡献至少2个p因子,且这样的整数有n/(p * p);同理p * p * p的倍数将贡献至少3个,这样的数有n/(p * p * p);p的四次方的倍数将贡献至少4个,这样的数有n/(p * p * p * p)。那么分析出这些结果对分解n!的质因数有什么帮助呢?
代码如下:
#include <iostream> #include <cstdio> using namespace std; bool mark[1010]; int prime[1010]; int primeSize; void init(){ primeSize = 0; for(int i = 2 ; i <= 1000 ; i++){ if(mark[i]) continue; mark[i] = true; prime[primeSize++] = i; for(int j = i*i ; j <= 1000 ; j+=i){ mark[j] = true; } } }// 筛选0到1000范围内的所有素数 int cnt[1010]; // cnt[i]用来表示,prime[i]所保存的素数在n!中的因子数,即n!分解素因数后,素因数prime[i]对应的幂指数,可能为0 int cnt2[1010]; // cnt2[i]用来表示,prime[i]所保存的素数在a中的因子数 int main(){ int n,a; init(); while(scanf("%d%d",&n,&a)!=EOF){ for(int i = 0 ; i < primeSize ; i++){ cnt[i] = cnt2[i] = 0; // 将两个计数器清零,为新的一次分解做准备 } for(int i = 0 ; i < primeSize ; i++){ int t = n; // 用临时变量t保存n的值 while(t){ cnt[i] += t/prime[i]; t = t/prime[i]; }// 依次计算t/prime[i]^k,累加其值,直到t/prime[i]^k变为0 } int ans = 123123123; // 答案初始值为一个大整数,为取最小值做准备 for(int i = 0 ; i < primeSize ; i++){ while(a%prime[i]==0){ cnt2[i]++; a/=prime[i]; }// 计算a中素因数prime[i]对应的幂指数 if(cnt2[i] == 0) continue; // 若该素数不能从a中分解到,即其对应幂指数为0,则其不影响整除性,跳过 if(cnt[i]/cnt2[i] < ans) // 计算素数prime[i]在两个数中因子数的商 ans = cnt[i]/cnt2[i]; // 统计这些商的最小值 } printf("%d\n",ans); } return 0; }
运行结果: