平常遇到的一些数学中的问题,整理一下。参考资料《算法笔记》
一、素数问题
如果用一般的方法去解决素数问题当数很大的时候就会超时,《算法笔记》上提供了一种解决办法,就是取平方根。文中说到“如果在2~n-1中间存在约数,不妨把这个数设置为k,则有n%k==0,可以知道n/k也是一个n的约数,并且k与n/k中有一个一定满足小于sqrt(n),一个大于sqrt(n)”所以判断一个数是否是素数就很简单了,用2到sqrt(n)来判断。 但是这个方法说的关于sqrt(n)的方法没看懂,于是用了第二种方法:打表
打表是一个很不错的方法,特别是在用空间换时间的时候;
#include <bits/stdc++.h>
using namespace std;
///打表求素数
const int maxn = 100; ///打印1到100之间的素数
int used[maxn] = {0};
int prim[maxn] = {0};
int prNum = 0;
void IsPrime()
{
for(int i=2; i<maxn; i++)
{
if(used[i] == 0)
{
prim[prNum++] = i; ///此数组的下标是素数的个数,对应的值是该数
for(int j=i; j<maxn; j+=i)
{
used[j] = 1;
}
}
}
}
void print()
{
for(int i=0; i<prNum; i++)
cout<<prim[i]<<" ";
}
int main()
{
IsPrime();
print();
return 0;
}
二、质因子分解
很强大的理论:要获得质因数前,必须先要求出素数,然后对这些素数进行判断,如果要判断的数是素数的话,那么他不存在质因数(这里说的质因数都是不算1和本身);如果不是素数,就可以分解成素数相乘的形式。有些题目不仅要输出数,也要输出次方,为此可以写一个结构体来解决这个事情。
struct factor
{
int x,cnt; ///cnt表示因数x出现的次数
}fac[10];
fac[10]的大小取10就够了,因为前10个素数:2*3*5*11*13*17*19*23*29已经超出了int的表示范围
具体实现代码如下
int num = 0;
void init()
{
int srt = (int)sqrt(n*1.0); ///n是在主函数中输入的要被分解的数
for(int i=0; i<prNum && prim[i]<=srt; i++)
{
if(n%prim[i] == 0) ///如果第i个素数是n的一个因数的话
{
fac[num].x = prim[i];
fac[num].cnt = 0; ///将cnt置为0
while(n%prim[i] == 0) ///我们开始计算有几个第i个素数
{
fac[num].cnt++;
n =n / prim[i];
}
num++; ///fac数组往后扩展一位
}
}
if(n!=1) ///如果最后处理结果得到的n不为1时
{
fac[num].x = n;
fac[num].cnt = 1;
}
num++;
}
三,大整数运算
3.1 大数的存储
最常用的解法是用数组即可。例如:定义int型数组d[1000],例如22354994,有:d[0]=4, d[1]=9, d[2]=9,d[3]=4, d[4]=5, d[5]=3, d[6]=2, d[7]=2. 是倒着进行存储,其实我们进行读数的时候从高位到低位读,这样存储符合思维习惯,而且进行加减运算也很方便。 但是用string读入时我们需要将str[0] = '2', str[1]=2......反过来进行存储。为了方便进行大数的比较,我们可以使用结构体来进行存储。
struct bign
{
int d[1000];
int len;
bign() ///这里用构造函数将其初始化
{
memset(d,0,sizeof(d));
len = 0;
}
};
这样我们在读入时用倒着存的思想:
bign input()
{
bign a ;
string str; cin>>str;
a.len = str.length();
int k = a.len-1;
for(int i=0; i<a.len; i++)
{
a.d[k] = str[i]-'0'; ///注意这里是倒着存储
k--;
}
return a;
}
3.2 大数的四则运算
有四种:1:高精度加法 2:高精度减法 3:低精度与高精度乘法 4:低精度与高精度除法
先看高精度加法:
用小学生的列竖式方法进行相加,进位可以用carry表示。因为每次加的时候个位数相加,超过10就对10取模,当做进位的数。
代码实现如下:
bign add(bign a,bign b)
{
bign c;
int carry = 0;
for(int i=0; i<a.len || i<b.len; i++)
{
int temp = a.d[i] + b.d[i] + carry; ///加的时候把上次的进位也加上
c.d[i] = temp%10; ///个位数
carry = temp/10; ///进位的数
c.len++;
}
if(carry != 0) ///表示最后进位不为零,即最高位相加时产生了进位
{
c.d[c.len++] = carry;
}
return c;
}
2:高精度减法。从低位开始相减,这里假设被减数>减数, 注意当高位为零时,我们需要把高位的零去掉,但是为了防止结果全是零的情况,我们要保留一个零。
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+a.d[i];
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while(c.len > 1 && c.d[c.len-1] == 0) ///对高位是零的情况进行单独处理
c.len--;
return c;
}
3.高精度和低精度相乘:与平时的列竖式的乘法不一样,用高精度的每一位和低精度相乘,进位什么的就和加法一样
bign mul(bign a,int x)
{
bign c;
int carry = 0;
for(int i=0; i<a.len; i++)
{
int temp = a.d[i]*x+carry;
c.d[c.len++] = temp % 10;
temp = temp / 10;
}
while(carry != 0)
{
c.d[c.len++] = carry% 10;
carry = carry / 10;
}
return c;
}
4.高精度和低精度相除:从高位开始除,当除数<被除数时,将高精度的数往后移一位并将原除数*10+现在高精度的数。除不尽有余数时当做进位来处理
bign div(bign a,int x)
{
bign c;
int temp = 0;
for(int i=a.len-1; i>=0; i--)
{
if(temp != 0)
{
temp = temp*10+a.d[i];
}else{
temp = a.d[i];
}
while(temp < x)
{
i--;
temp = temp*10+a.d[i];
}
c.d[c.len++] = temp/x;
temp = temp % x;
}
return c;
}
四、最大公因数:就是两个数的所有公因数中最大的那个数,最小公倍数就是两个数的所有公倍数中最小的那个数
最大公因数我们一般用gcd()表示,最小公倍数我们一般用lim()表示
关于它们具体实现的代码如下:
int gcd(int a,int b)
{
if( b == 0)
return a;
else return gcd(b,a%b);
}
int lim(int a,int b) ///最小公倍数在最大公因数的基础之上说找到的
{
int c = gcd(a,b);
return a*b/c; ///关于这个理论我不是很明白这个是为什么
}
五、分数的运算: (这里说明的分数是用结构体表示出来的)
分数的运算是:化简,加,减,乘,除
《算法笔记》里面规定了分数的约法三章:
1.分数如果是负数那么就是分母为正,分子为负
2.如果是零,那么分子是零,分母为1
3.所有的分数都已最简形式输出
这个思路实现的算法如下:
struct Fraction
{
int up,down; //up表示分子,down表示分母
};
Fraction reduction(Fraction res)
{
if(res.down<0)
{
res.down = -res.down;
res.up = -res.up;
}
if(res.up == 0)
res.down = 1;
else
{
int d = gcd(res.up,res.down);
res.up = res.up/d;
res.down = res.down/d;
}
return res;
}
对于所有的分子式的运算,最后都按照这三条约束进行化简
///add
Fraction add(Fraction f1,Fraction f2)
{
Fraction res;
res.up = f1.up*f2.down+f2.up*f1.down;
res.down = f1.down * f2.down;
res = reduction(res);
return res;
}
Fraction sub(Fraction f1,Fraction f2)
{
Fraction res;
res.up = f1.up*f2.down-f2.up*f1.down;
res.down = f1.down * f2.down;
res = reduction(res);
return res;
}
Fraction mul(Fraction f1,Fraction f2)
{
Fraction res;
res.up = f1.up*f2.up;
res.down = f1.down * f2.down;
res = reduction(res);
return res;
}
Fraction div(Fraction f1,Fraction f2)
{
Fraction res;
res.up = f1.up*f2.down;
res.down = f1.down * f2.up;
res = reduction(res);
return res;
}