数学问题
数字黑洞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 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型数组来标记。
时间复杂度: 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∑nin)=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不是素数;
- 素数表长至少要比n大1;
- Find_Prime()函数中要记得时 i < m a x n i<maxn i<maxn而不是 i ≤ m a x n i \leq maxn i≤maxn,否则程序一运行就会崩溃;
- 在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;
}