判断是否为素数
public class Main {
public static void main(String[] args) {
for (int i = 1; i < 20; i++) {
System.out.println(i + " " + isPrime(i));
}
}
public static boolean isPrime(int n) {
if (n < 2)
return false;
if (n == 2)
return true;
if (n % 2 == 0)
return false;
//不直接在for的判断条件写是应为sqrt()函数比较耗时,没必要每次都算一遍
int bound=(int) (Math.sqrt(n)+1);
for (int i = 3; i < bound; i += 2) {
if (n % i == 0)
return false;
}
return true;
}
}
素数筛法
上面的算法是可行的,但是复杂度过高,整个过程显得粗暴而不具技巧性。这里,我们提出一个更优雅的方式解决这种题。
首先思考这样一个问题:
若一个数不是素数,则必存在一个小于它的素数为其因数。这个命题的正确性是显而易见的。
那么假如已经获得了小于一个数的所有素数,只需要确定该数不能被这些素数整除,这个数即为素数。
但这样做似乎需要大量的枚举测试工作。我们可以换一个角度,在获得一个素数时,即将它所有的倍数标记成非素数
这样当我们遍历到一个数时,它没有被任何小于它的素数标记为非素数,则确定其是素数
从2开始遍历2~100 0000区间内所有的整数,若当前整数没有因为它是某个小于其的素数的倍数而被标记为非素数
则判定其为素数,并标记它所有的倍数为非素数。然后继续遍历下一个数,直到遍历完2~100 0000区间内所有的整数。
此时没有被标记成非素数的数字即为我们要求的素数。这种算法被我们称为素数筛法。
简单的素数问题(2008年北京航空航天大学计算机研究生机试真题)
题目描述:
输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。
输入:
输入有多组数据。
每组一行,输入n。
输出:
输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数(素数之间用空格隔开,最后一个素数后面没有空格),如果没有则输出-1。
样例输入:
100
样例输出:
11 31 41 61 71
2008年北京航空航天大学计算机研究生机试真题
public class 素数筛法 {
static int prime[]=new int[10000];//保存筛得素数的个数
static int primeSize=0;
static boolean mark[]=new boolean[10001];
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
//init 素数筛法
for (int i = 0; i < mark.length; i++) {//所有数字均未被标记
mark[i]=false;
}
for (int i = 2; i <= 1000; i++) { //依次从2遍历到10000
if (mark[i]==true) {//若该数字已被标记则跳出
continue;
}
prime[primeSize++]=i;//否则,又是一个新素数
for (int j = i*i; j <= 10000; j+=i) {
mark[j]=true;
}
}
//选最后一位为1的
String result="";
for (int i = 0; i < primeSize; i++) {
if (prime[i]<n&&prime[i]%10==1) {
result+=prime[i]+" ";
}
}
System.out.println(result.equals("")?-1:result.substring(0,result.length()-1));
}
}
读者可能注意到,筛法中我们使用了一个小技巧。当我们判定i为素数,要标记其所有倍数的时候
我们并没有从2*i开始标记 ,而是直接从i*i开始标记。其原因是,
i * k (k < i)必已经在求得k的某个素因数(必小于i)时标记过了,即 i * k 同时也是k的素因数的倍数。
所以这里,我们可以直接从i的平方标记开始算。
尽可能避免重复工作是程序优化的一大思路。
分解素因数
质因数的个数(2007年清华大学计算机研究生机试真题)
题目描述:
求正整数N(N>1)的质因数的个数。
相同的质因数需要重复计算。如120=2*2*2*3*5,共有5个质因数。
输入:
可能有多组测试数据,每组测试数据的输入是一个正整数N,(1 < N < 10^9)。
输出:
对于每组数据,输出N的质因数的个数。
样例输入:
120
样例输出:
5
思路
利用素数筛法预先求出可能称为素因数的素数
遍历所有小于n的素数,判断其是否为n的因数
若确定某数为n的因数,则通过试除确定其对应的幂指数。
最后求出各个幂指数的和即为所求。
public class 素因数的个数 {
static int prime[] = new int[10000];// 保存筛得素数的个数
static int primeSize = 0;
static boolean mark[] = new boolean[10001];
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
primeMothed();
int ansPrime[]=new int[30];//按顺序保存分解出来的素因数
int ansSize=0;//分解出来素因数的个数
int ansNum[]=new int[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) {//若已被分解为1,则分解提前终止
break;
}
}
}
if (n!=1) {//若测完2~100000内所有的素因数,n仍未被分解至1,则剩余的因数一定是n一个大于100000的素因数
ansPrime[primeSize]=n;//记录该大素因数
ansNum[ansSize++]=1;//其幂指数只能为1
}
int ans=0;
for (int i = 0; i < ansSize; i++) {
ans+=ansNum[i];//统计各个素因数的幂指数
}
System.out.println(ans);
}
public static void primeMothed() {
// init 素数筛法
for (int i = 0; i < mark.length; i++) {// 所有数字均未被标记
mark[i] = false;
}
for (int i = 2; i <= 1000; i++) { // 依次从2遍历到10000
if (mark[i] == true) {// 若该数字已被标记则跳出
continue;
}
prime[primeSize++] = i;// 否则,又是一个新素数
for (int j = i * i; j <= 10000; j += i) {
mark[j] = true;
}
}
}
}