算法中基本数学问题详细总结(C++、最小公约数、最大公倍数、分数四则运算、素数、大整数运算)
一、最小公约数和最大公倍数
- 最大公约数:采用辗转相除法
- 递归式:gcd(a, b) = gcd(b, a % b);
- 递归边界:gcd(a, 0) = a;
int gcd(int a, int b){
return !b ? a : gcd(b, a % b);
}
- 最小公倍数:公式a x b / d,d就是a和b的最大公约数;
int lmd(int a, int b){
return a / gcd(a, b) * b;
}
二、分数四则运算
- 1.分数化简和表示
struct Fraction{
int up, down;
};
- 2.分数化简函数
Fraction reduction(Fraction result){
if(result.down < 0){
result.up = -result.up;
result.down = -result.down;
}
if(result.up == 0){
result.down = 1;
}else{
int d = gcd(abs(a.up), abs(a.down));
result.up /= d;
result.down /= d;
}
return result;
}
- 加减乘除的运算:
Fraction add(Fraction a, Fraction b){
Fraction c;
c.up = a.up * b.down + a.down * b.up;
c.down = a.down * b.down;
return reduction(c);
}
Fraction minu(Fraction a, Fraction b){
Fraction c;
c.up = a.up * b.down - a.down * b.up;
c.down = a.down * b.down;
return reduction(c);
}
Fraction multi(Fraction a, Fraction b){
Fraction c;
c.up = a.up * b.up;
c.down = a.down * b.down;
return reduction(c);
}
Fraction divide(Fraction a, Fraction b){
Fraction c;
c.up = a.up * b.down;
c.down = a.down * b.up;
return reduction(c);
}
- 分数的输出函数
void showResult(Fraction a){
Fraction c = reduction(a);
if(c.down == 1) printf("%d", c.up);
else if(abs(c.up) > c.down){
printf("%d %d/%d", c.up / c.down, abs(c.up) % c.down, c.down);
}else{
printf("%d/%d", c.up, c.down);
}
}
三、素数
1. 素数的判断
- 定义:素数是除了1和本身外不能被其他整数整除的数,1既不是素数也不是合数;
bool isPrime(int n){
if(n <= 1) return false;
int sqr = (int)sqrt(n);
for(int i = 2; i <= sqr; i++){
if(n % i == 0) return false;
}
return true;
}
2. 素数表的获取
- 方法一,在10^5的范围内可以获取;
const int maxn = 101;//表长,即想要查找的范围
int Prime[maxn], Num = 0;//数组用于存储素数,Num代表范围内的素数数量
bool isTrue[maxn] = {0};//用于判断i是否为素数
void find_Prime(){
for(int i = 2; i < maxn; i++){
if(isPrime(i)){
Prime[Num++] = i;
isTrue[i] = true;
}
}
}
- 方法二, 埃氏筛法
- 原理: 算法从小到大枚举所有数,对于每个素数,筛去它的倍数,剩下的就都是素数了;
- isTrue中true代表不是素数,和上面正好相反;
void find_Prime(){
for(int i = 2; i < maxn; i++){
if(isTrue[i] == false){
Prime[Num++] = i;因为从2开始是算素数
for(int j = i + i; j < maxn; j += i){
isTrue[j] = true;
}
}
}
}
四、质因子分解
- 定义,将一个正整数分解成一个或多个质数的乘积模式
- 结构体,对于一个int的整数来说,数组开到10已经足够了;
struct factor{
int x, cnt;//x为质因子,cnt为数量
}fac[10];
原理
- 对于一个正整数n来说,如果它存在[2, n]范围的质因子,要么这些质因子全部都小于等于sqrt(n);要么只存在一个大于sqrt(n)的质因子,而其余的质因子都小于等于sqrt(n);
- 枚举1~sqrt(n)范围内的所有质因子p,判断其是否为n的因子;
- 如果p是n的因子,那么给fac数组增加质因子p,并初始化个数为0,然后只要p还是n的因子,就让n不断除以p,每次操作令p的个数加1,直到p不再是n的因子;
- 如果p不是n的因子直接跳过;
int num = 0;
int sqr = sqrt(n);
for(int i = 0; i < sum && prime[i] <= sqr; i++){
if(n % prime[i] == 0){
ans[num].a = prime[i];
ans[num].sum = 0;
while(n % prime[i] == 0){
ans[num].sum++;
n /= prime[i];
}
num++;
}
if(n == 1) break;
}
if(n != 1){
ans[num].a = n;
ans[num++].sum = 1;
}
五、大整数运算
1. 大整数的存储
- 首先明白使用数组进行存储,然后低位在数组的低位进行存储,然后高位在数组高位进行存储,为后续计算提供便利
struct bign{
int d[1000];
int len;
bign(){
memset(d, 0, sizeof(d));
len = 0;
}
}
- 一般的读入大整数是按照字符串先读入的,然后将字符串存至结构体
bign change(char str[]){
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++){
a.d[i] = str[a.len - 1 - i] - '0';
}
return a;
}
2. 高精度加法
- 保证以长的作为界限进行计算
- 最后有进位记得加上
bign add(bign a, bign b){
bign c;
int carry = 0;
for(int i = 0; i < a.len || i < b.len){//以长的为界限,不用怕,短的面前缺失的部分默认为0,相当于直接加长的
int temp = a.d[i] + b.d[i] + carry;
c.d[len++] = temp % 10;
carry = temp / 10;
}
if(carry != 0){
c.d[len++] = carry;
}
return c;
}
3. 高精度减法
- 注意点一个数,位数不够,向高位借位
- 还有就是可能出现高位很多0的情况,最后使用while保留一位即可;
bign sub(bign a, bign b){
bign c;
for(int i = 0; i < a.len || i < b.len; i++){
if(a.d[i] < b.d[i]){//不够向高位借位
a.d[i + 1]--;
a.d[i] += 10;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;
}
return c;
}
4. 高精度与低精度的乘法
- 低精度就是可用使用int进行存储的数字
- 取bign的某一位与int型整体相乘,再与进位进行相加,所得结果的个位数作为该为的结果,高位部分作为新的进位;
- 最后因为进位可能不只一位,使用while进行处理
bign multi(bgin a, int b){
bign c;
int carry = 0;
for(int i = 0; i < a.len; i++){
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
while(carry != 0){
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
5. 高精度与低精度的除法
- 首先将c的长度赋值和a的长度一样
- 传参比之前多了一个r余数,使用引用,初始一般为0
- 同时i初始是从数组的高位开始,即i = a.len - 1
- 在跟之前的余数相加后,如果不够除商0;
- 同时也可能出现高位很多0的情况,使用while进行删除;
bign divide(bign a, int b, int& r){
bign c;
c.len = a.len;
for(int i = a.len - 1; i >= 0; i--){
r = r * 10 + a.d[i];
if(r < b) c.d[i] = 0;
else{
c.d[i] = r / b;
r = r % b;
}
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;
}
return c;
}
作者:睿晞
身处这个阶段的时候,一定要好好珍惜,这是我们唯一能做的,求学,钻研,为人,处事,交友……无一不是如此。
劝君莫惜金缕衣,劝君惜取少年时。花开堪折直须折,莫待无花空折枝。
曾有一个业界大牛说过这样一段话,送给大家:
“华人在计算机视觉领域的研究水平越来越高,这是非常振奋人心的事。我们中国错过了工业革命,错过了电气革命,信息革命也只是跟随状态。但人工智能的革命,我们跟世界上的领先国家是并肩往前跑的。能身处这个时代浪潮之中,做一番伟大的事业,经常激动的夜不能寐。”
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.