文章目录
1.最大公约数和最小公倍数
(1)最大公约数
正整数a与b的最大公约数是指a与b的所有公约数中最大的那个公约数,记为gcd(a,b),而求解最大公约数常用欧几里得算法(即辗转相除法)
欧几里得算法基于下面这个定理:
设a、b均为正整数,则gcd(a,b) = gcd(b,a%b)
可以看到,如果a<b,那么定理的结果就是将a和b交换,如果a>b,那么通过这个定理总可以将数据规模变小,并且减小的非常块,并且由0和任意正整数的最大公约数等于正整数本身,得到递归边界,即:
① gcd(a,0) = a
② gcd(a,b) = gcd(b,a%b)
int MaxCommonDivisor(int a,int b)
{
if (b == 0) return a;
return MaxCommonDivisor(b, a % b);
}
(2)最小公倍数
正整数a与b的最小公倍数是指a与b的所有公倍数中最小的那个公倍数,一般用lcm(a,b)。
最小公倍数的求解在最大公约数的基础上进行,当得到a和b的最大公约数d之后,可以马上得到a和b的最小工公倍数是ab/d(ab实际计算可能溢出,更好的写法是(a/d)*b)
这个公式通过集合可以很好理解,a和b的最大公约数即集合a与集合b的交集,而最小公倍数为集合a和集合b的并集,因此ab多算了一次公因子,因此需要多除一次公因子
int MaxCommonMultiple(int a, int b)
{
int d = MaxCommonDivisor(a, b);
return (a / d) * b;
}
2.分数相关
所谓分数的四则运算是指,给定两个分数的分子和分母,求它们加减乘除的结果。
(1)分数的表示和化简
分数的表示
对一个分数来说,最简洁的写法就是写成假分数的形式用结构体保存。
由于分数的乘法和出发过程中可能是分子或分母超过int型表示范围,一般情况下用long来存储
struct Fraction
{
long up,down; //分子、分母
};
其中需要对这种表示定制三项规则:
① 使分母为非负数,如果分数为负,令分子为负
② 如果该分数恰为0,规定分子为0,分母为1
③ 分子和分母没有除了1以外的公约数(最简)
分数的化简
分数的化简即执行上述三项规则:
① 如果分母down为负数,那么令分子up和donw都变味相反数
② 如果分子up 为0,那么令分母down为1
③ 约分:求出分子绝对值与分母绝对值的最大公约数,然后令分子分母同时除以d
void reduction(Fraction& result)
{
//第一项规则
if (result.down < 0)
{
result.up = -result.up;
result.down = -result.down;
}
//第二项规则
if (result.up == 0)
result.down = 1;
//第三项规则
int d = MaxCommonDivisor(result.up, result.down);
result.up /= d;
result.down /= d;
}
(2)分数的四则运算
按正常的分数四则运算计算即可,注意结果的化简
3.素数(质数)
素数又称质数,是指除了1和本身之外,不能其他数整除的一类数
(1)素数的判断
一个素数要判断为素数,需要判断n是否能被2,3,…n-1中的一个整除。这样的判定方法没有问题,时间复杂度为O(n),但是在很多题目中,判断素数只是整个算法中的一个部分,这时候O(n)实际上有点大,有没有更简单的方法呢?有的。
我们假设n存在约数,设为K,则k*(n/K)= n。且K和n/k分布在sqrt(n)的两端,所以我们只需要判断2,3,4…sqrt(n)向下取整,就可以了,时间复杂度为O(√n)
bool isPrime(int n)
{
int sqr = (int)sqrt(n*1.0);
for (int i = 2; i <= sqr; ++i)
{
if (n % i == 0)
return false;
}
return true;
}
(2)素数表的获取
素数表的获取即对1~n进行枚举,判断每个数是否是素数,如果是素数则加入素数表中国,时间复杂度为(n√n))
4.质因子分解
所谓质因子分解是指将一个正整数n写成一个或多个质数的乘积的形式。
由于每个质因子都可以不止出现一次,因此不妨定义结构体factor,用来存放质因子及其个数:
struct factor
{
int x,cnt; //x为质因子,cnt为其个数
}fac[10];
前面提到过,对一个正整数n来说,如果它存在1和本身之外的因子,那么一定是在sqrt(n)的左右成对,而这里把这个结论用在“质因子”上面,会得到一个强化结论:对一个正整数来说,如果它存在[2,n]范围内的质因子,要么这些质因子全部小于等于sqrt(n),要么只存在一个大于sqrt(n)的质因子,而其余质因子全部小于等于sqrt(n)。得到下面思路
① 枚举1~sqrt(n)范围内的所有质因子p,判断p是否是n的因子
- 如果p是n的因子,那么给fac数组中增加质因子p,并初始化其个数为0。然后,只要p还是n的因子,就让n不断除以p,每次操作令p的个数加1,直到p不再是n的因子为止。
- 如果p不是n的因子,就直接跳过
② 如果在上面步骤结束后n仍然大于1,说明n有且仅有一个大于sqrt(n)的因子,这是需要把这个质因子加入fac数组,并令其个数为1
int FactorPrime(factor fac[],int num)
{
int sqr = (int)sqrt(num * 1.0);
memset(fac, 0, sizeof(factor) * 10);
int k = 0;
for (int i = 2; i <= sqr; ++i)
{
if (num % i != 0)
continue;
fac[k].x = i;
while (num % i == 0)
{
num /= i;
fac[k].cnt++;
}
++k;
}
if (num != 1)
{
fac[k].x = num;
fac[k].cnt = 1;
}
return k;
}