质数、约数相关的算法实现
基本概念
因子/约数:因数是指整数a除以整数b(b≠0) 的商正好是整数而没有余数,我们就说b是a的因数。公约数:如果一个整数同时是几个整数的约数/因子,称这个整数为它们的公因数;
最大公约数:公约数中最大的称为最大公约数
质数:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
自然数:非负整数。
质数
判断一个数是不是质数:
暴力遍历:
public static boolean isPrime1(int prime) {
//for (int i = 2; i <= prime; i++) 全部遍历
/*
由于质数可以变成2个因子的乘,那么如果一个因子大于sqrt(x)
则另外一个小于sqrt(x),或者2个都是sqrt(x)
*/
for (int i = 2; i <= Math.sqrt(prime); i++)
if (prime % i == 0)
return false;
return true;
}
一个数不能被2整除,那么这个数一定不能被其他偶数整除,
优化:
public static boolean isPrime2(int prime) {
if (prime <= 3)
return prime > 1;
if (prime % 2 == 0)
return false;
for (int i = 3; i <= Math.sqrt(prime); i += 2)
if (prime % i == 0)
return false;
return true;
}
(大于等于5的)质数一定和6的倍数相邻,一个是6x-1或6x+1
又因为6x-2/4一定能被2整除(2(x-1))
6x-3一定能被3整除
所以在1-6里面2,3,4,6都是不行的只有1和5可以
代码再次优化:
public static boolean isPrime(int prime) {
if (prime <= 3)
return prime > 1;
if (prime % 6 != 1 && prime % 6 != 5)
return false;
for (int i = 5; i <= Math.sqrt(prime); i += 6) // 乘法比除法快所以用i*i
if (prime % i == 0 || prime % (i + 2) == 0)
return false;
return true;
}
埃式筛法
判断一个数是不是不建议用这个方法
求范围内的质数:
emm,可以知道的只有偶数(除了2)不是。所以外循环可以优化为
代码
/**
* 返回范围内的质数数量
* @param start
* @param end
* @return
*/
public static long countPrime(int start, int end){
int i =start,count = 0;
for (; i < end; i++)
if (isPrime(i))
count++;
return count;
}
/**
* 返回0-指定数的质数数量
* @param end
* @return
*/
public static long countPrime(int end){
if (end < 4)
return end - 1;
return 2+countPrime(5,end);
}
埃式筛法
埃拉托色尼筛法(Sieve of Eratosthenes)是一种用于查找素数的简单而古老的算法。算法步骤如下:
- 创建一个长度为 n n n + 1 1 1 的布尔类型数组 i s p r i m e is_prime isprime,并将所有元素初始化为 t r u e true true 。
- 设置 i s _ p r i m e is\_prime is_prime [ 0 0 0 ]和 i s _ p r i m e is\_prime is_prime [ 1 1 1 ]为 f a l s e false false ,因为 0 0 0 和 1 1 1 不是素数。
- 从 2 2 2 开始循环到 s q r t sqrt sqrt { n n n },如果 i s _ p r i m e is\_prime is_prime [ i i i ]为 t r u e true true ,则将 i i i 的所有倍数(除了 i i i 本身)标记为 f a l s e false false ,即设置 i s _ p r i m e is\_prime is_prime [ j j j ]= f a l s e false false ( j j j = i i i , i i i ( i i i + 1 1 1 ), i i i *( i i i + 2 2 2 )… 直到 j j j > n n n ).
- 遍历 i s _ p r i m e is\_prime is_prime []数组,将值为 t r u e true true 的下标存储到另一个数组 p r i m e s primes primes []中,这样就得到了从 2 2 2 到 n n n 之间的所有素数。
时间复杂度为 O ( n log log n ) O(n\log{\log{n}}) O(nloglogn),空间复杂度为 O ( n ) O(n) O(n)。
public ArrayList<Integer> getPrimes(int n){
ArrayList<Integer> primes = new ArrayList<>();
boolean[] f = new boolean[n+1];
for (int i = 2; i < n + 1; i++) {
if (!f[i]){
primes.add(i);
for (int j = i+i; j < n + 1; j+=i) {
f[j] = true;
}
}
}
return primes;
}
合数
非质数就是合数
质因数
质因数(素因数或质因子)在数论里是指能整除给定正整数的质数。
除了1以外,两个没有其他共同质因子的正整数称为互质。
正整数的因数分解可将正整数表示为一连串的质因子相乘,质因子如重复可以用指数表示。
根据算术基本定理,任何正整数皆有独一无二的质因子分解式 。
质数只有一个质因子就是本身。
分解质因数
public ArrayList<Integer> zhiyin(int n){
ArrayList<Integer> a = new ArrayList<>();
for (int i = 2; i <= n; i++) {
while (n % i == 0) {
a.add(i);
n /= i;
}
}
return a;
}
约数
约数,又称因数。整数a除以整数b(b≠0)除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。
判断约数数量
约数个数定理是指:一个正整数N可以分解为质因数乘积 N = p 1 a 1 ⋅ p 2 a 2 ⋅ . . . ⋅ p k a k N=p_1^{a_1} \cdot p_2^{a_2} \cdot ... \cdot p_k^{a_k} N=p1a1⋅p2a2⋅...⋅pkak,其中 p 1 , p 2 , . . . , p k p_1,p_2,...,p_k p1,p2,...,pk为不同的质数, a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak为正整数,则N的约数个数为 ( a 1 + 1 ) ⋅ ( a 2 + 1 ) ⋅ . . . ⋅ ( a k + 1 ) (a_1+1)\cdot(a_2+1)\cdot...\cdot(a_k+1) (a1+1)⋅(a2+1)⋅...⋅(ak+1)个。
数学公式为:
d
(
N
)
=
(
a
1
+
1
)
⋅
(
a
2
+
1
)
⋅
.
.
.
⋅
(
a
k
+
1
)
\large{d(N) = (a_1+1)\cdot(a_2+1)\cdot...\cdot(a_k+1)}
d(N)=(a1+1)⋅(a2+1)⋅...⋅(ak+1)
其中, d ( N ) d(N) d(N)表示N的约数个数。
分解质因数方法
public static long countDivisor(long divisor){
long count = 1;
for (long i = 2; i*i <= divisor; i++) {
long numi = 0;
while (divisor % i == 0){
numi++;
divisor/=i;
}
count*=numi+1;
}if (divisor > 1)
count*=2;
return count;
}
暴力
public static int countDivisor1(int divisor) {
int count = 0;
int i = 1;
/*for (int i = 1; i <= divisor; i++)
if (divisor % i == 0)
count++;*/
for (; i*i < divisor; i++)
if (divisor % i == 0)
count+=2;
return i*i==divisor?count+1:count;}
最小公倍数
如求
a
a
a 和
b
b
b 的最小公倍数。
则最小公倍数等于
a
∗
b
/
g
c
b
(
a
,
b
)
a*b/gcb(a,b)
a∗b/gcb(a,b)
2个数的乘积除以这2个数的最大公约数
public BigInteger aaa(int a,int b){
BigInteger bi = BigInteger.valueOf(a);
BigInteger bb = BigInteger.valueOf(b);
return bi.multiply(bb)
.divide(bi.gcd(bb));
}
最大公约数
api
java可以直接使用api进行求解
public BigInteger aaa(int a,int b){
return BigInteger.valueOf(a).gcd(BigInteger.valueOf(b));
}
暴力获取
int big = Math.max(a, b);
int small = Math.min(a, b);
if (big % small == 0)
return small;
for (int i = small / 2; i > 1; i--) {
if (big % i == 0 && small % i == 0)
return i;
}
return 1;
辗转相除法(欧几里德算法)
实现
int result = 0;
while (n != 0) {
result = m % n;
m = n;
n = result;
}
return m;
递归实现
public static int gcd (int a,int b) {
if(b==0)
return a;
else
return gcd(b,a%b);
}
更相减损
实现
while(m != n) {
if(m > n)
m -= n;
else
n-= m;
}
return m;
递归
public static int gcd (int a,int b) {
if (a==b)
return a;
else if (a>b)
a = a-b;
else
b = b-a;
return gcd(a, b);
}
互质
2个数互质,说明其最大公约数为1
static int gcd(int a, int b) {
if (a % b == 0)
return b;
return gcd(b, a % b);
}
习题
质数
待补充