天天看点

算法笔记-数学问题数字黑洞6174最大公约数/最小公倍数分数的四则运算素数大整数运算

数学问题

  • 数字黑洞6174
    • 补充
  • 最大公约数/最小公倍数
    • 最大公约数
    • 最小公倍数
  • 分数的四则运算
    • 分数化简
    • 分数输出
  • 素数
    • 生成素数表
      • 思路一:
      • 时间复杂度: O ( n n ) O(n \sqrt n) O(nn

        ​)

      • 思路二:
      • 时间复杂度: O ( ∑ i = 1 n n i ) = O ( n l o g l o g n ) O(\sum \limits_{i=1}^n \frac{n}{i})=O(nloglogn) O(i=1∑n​in​)=O(nloglogn)
      • 注意:
    • 质因子分解(PAT A1059 Prime Factors)
  • 大整数运算
    • 高精度加法
    • 高精度减法

数字黑洞6174

算法笔记-数学问题数字黑洞6174最大公约数/最小公倍数分数的四则运算素数大整数运算
#include <cstdio>
#include <algorithm>
using namespace std;
bool cmp(int a, int b){
    return a>b;
}
void to_array(int n, int num[])//此时的存入顺序不重要,因为后面都是要重新排序的
{
    for(int i=0;i<4;++i){
        num[i]=n%10;
        n/=10;
    }
}
int to_number(int num[]){
    int sum=0;
    for(int i=0;i<4;++i){
        sum=sum*10+num[i];
    }
    return sum;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    int n,MIN,MAX;
    scanf("%d",&n);
    int num[5];
    while(1){
        to_array(n,num);
        sort(num,num+4);//默认是从小到大
        MIN=to_number(num);
        sort(num,num+4,cmp);//从大到小
        MAX=to_number(num);
        n=MAX-MIN;
        printf("%04d-%04d=%04d\n",MAX,MIN,n);
        if(n==0 || n==6174)break;
    }
    return 0;
}

           

补充

cmp函数

最大公约数/最小公倍数

最大公约数

设a, b均为正整数,则递归式gcd(a, b)=gcd(b, a % b)

0和任意一个整数a的最大公约数都是a(⚠️不是0),这个结论作为递归的边界

int gcd(int a, int b){
	if(b==0)return a;
	else return gcd(b, a % b);
}
           

最小公倍数

当得到a, b的最大公约数后,可以马上得到a, b的最小公倍数是 a b d \frac{ab}{d} dab​

由于实际计算时ab可能溢出,因此更好的写法是 a d b \frac{a}{db} dba​

分数的四则运算

分数化简

算法笔记-数学问题数字黑洞6174最大公约数/最小公倍数分数的四则运算素数大整数运算
struct Fraction{
    int up, int down;
};
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(result.up),abs(result.down));
        result.up /=d;
        result.down /=d;
    }
    return result;
}
           

分数的加/减/乘/除法-先运算最后化简,并且只有在除数(分子)不为0时才用这些函数进行运算。

分数输出

算法笔记-数学问题数字黑洞6174最大公约数/最小公倍数分数的四则运算素数大整数运算
void showResult(Fraction r){
    r=reduction(r);
    if(r.down==1)printf("%lld", r.up);
    //由于分母在分数表示时已经非负了,所以不用abs;r.up/r.down已经表示整个分数的正负
    else if(abs(r.up)>r.down)printf("%d %d/%d",r.up/r.down, abs(r.up) % r.down, r.down);
    else printf("%d/%d",r.up,r.down);
}
           

由于分数的乘除法可能使分子,分母超过int型表示范围,因此应该使用longlong型来存储。

素数

生成素数表

思路一:

设在2~(n-1)中存在n的约数k,即 n % k = = 0 n\%k==0 n%k==0,由 k ∗ n k = = n k*\frac{n}{k}==n k∗kn​==n可知 n k \frac{n}{k} kn​也是n的一个约数,且k与 n k \frac{n}{k} kn​一定满足其中一个小于sqrt(n),另一个大于sqrt(n),因此我们只需要判定n能否被 2 , 3 , . . . , ⌊ s q r t ( n ) ⌋ 2,3,...,\lfloor sqrt(n) \rfloor 2,3,...,⌊sqrt(n)⌋中的一个整除,即可判定n是否为素数。

时间复杂度: O ( n n ) O(n \sqrt n) O(nn

​)

#include <cstdio>
#include <cmath>

bool isPrime(int n){
    int sqr=(int)sqrt(1.0*n);//向下取整
    for(int i=2;i<=sqr;++i)//由素数的定义(除1和它本身没有其他约数)所以从2开始,并且可以取到sqr
    {
        if(n%i==0)return false;
    }
    return true;
}
int prime[101],pNum=0;//其中prime[]是100以内所有素数的集合,pNum是存在prime[]里的素数个数
bool p[101]={0};//p[]是针对每个数是否是素数的bool表
void Find_Prime(){
    for(int i=2;i<101;++i){
        if(isPrime(i)==true){
            prime[pNum++]=i;//使用pNum而不是i作为更新的数组下标,因为这里的i指的是用来判断是否为素数的数字
            p[i]=true;
        }
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    Find_Prime();
    for(int i=0;i<pNum;++i){
        printf("%d",prime[i]);
    }
    return 0;
}

           

思路二:

筛法:从小到大(从第一个素数2开始到n),遍历所有数,筛去它的所有倍数,剩下的就是素数了;当从小到大到达某数a时,如果a没有被前面的步骤的数筛去,那么a一定是素数。“筛”可以通过bool型数组来标记。

算法笔记-数学问题数字黑洞6174最大公约数/最小公倍数分数的四则运算素数大整数运算

时间复杂度: O ( ∑ i = 1 n n i ) = O ( n l o g l o g n ) O(\sum \limits_{i=1}^n \frac{n}{i})=O(nloglogn) O(i=1∑n​in​)=O(nloglogn)

void Find_Prime(){
	for(int i=2;i<101;++i){
		if(p[i]=false){
			prime[pNum++]=i;//p[i]=false说明是素数,首先先把这个素数放入素数集合中;
			for(int j=i+i;j<101;j+=i)//跳到i的倍数
				p[j]=true;
		}	
	}
}
           

注意:

  1. 1不是素数;
  2. 素数表长至少要比n大1;
  3. Find_Prime()函数中要记得时 i < m a x n i<maxn i<maxn而不是 i ≤ m a x n i \leq maxn i≤maxn,否则程序一运行就会崩溃;
  4. 在main函数中要记得调用Find_Prime()函数,否则不会出结果;

质因子分解(PAT A1059 Prime Factors)

将一个正整数n写成一个或多个质数的乘积形势,应该先把素数表(素数表的范围是比较大的)打印出来,注意:每个质因子都可以不止出现一次。

struct factor{
	int x,cnt;//x是质因子,cnt是它出现的个数
}fac[10];
int main(){
	Find_Prime();
	int n, num=0;
	scanf("%d",&n);
	if(n==1)printf("l=1");
	else{
	printf("%d=",n);
	int sqr=(int)sqr(1.0*n);//向下取整
	for(int i=0;i<pNum && prime[i]<=sqr;++i){
		if(n%prime[i]==0){
			fac[num].x=prime[i];
			fac[num].cnt=0;//注意这里的技巧,先不把cnt++,放在后面和while循环一起,不容易乱
			while(n%prime[i]==0){
					fac[num].cnt++;
					n/=prime[i];
			}
			num++;
		}
		if(n==1)break;
	}
	if(n!=1){
		fac[num].x=n;//只能是它本身,质因子个数为1
		fac[num++].cnt=1;
	}
	for(int i=0;i<num;++i){
		if(i>0)printf("*");
		printf("%d",fac[i].x);
		if(fac[i].cnt>1){
			printf("^%d",fac[i].cnt);
		}
	}
}
return 0;
}

           

大整数运算

高精度加法

以最简单情况为例:两个对象都是非负整数。注意:一正一负,去掉负号变成高精度减法;两负,去掉负号变成高精度加法再添上负号。

#include <cstdio>
#include <string>
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;
}
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[c.len++]=temp%10;
        carry=temp/10;
    }
    if(carry!=0)
        c.d[c.len++]=carry;//如果最后进位不为0,直接赋给结果的最高位
    return c;
}
//补充一个输出方法
void print(bign a){
    for(int i=a.len-1;i>=0;--i){
        printf("%d",a.d[i]);
    }
}
int main(int argc, const char * argv[]) {
    char str1[1000],str2[1000];
    scanf("%s%s",str1,str2);
    bign a=change(str1);
    bign b=change(str2);
    print(add(a, b));
    return 0;
}

           

高精度减法

继续阅读