算法笔记-数学问题

数字黑洞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

分数的四则运算

分数化简

在这里插入图片描述

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时才用这些函数进行运算。

分数输出

在这里插入图片描述

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 kkn==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型数组来标记。
在这里插入图片描述

时间复杂度: 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=1nin)=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 imaxn,否则程序一运行就会崩溃;
  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;
}

高精度减法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值