数学基础
目录
前言:int(-2147483648~+2147483647)
在写程序时,需要注意数据的范围。如:使用高斯公式时(n*(n+1)/2)当n=50000时,最终结果不会超过int的范围,但计算n*(n+1)时会超过,使得结果出错。
此时,可以使用long long,也可以先对n和n+1进行奇偶的判断,再将其中为偶数的一项先除以2,使数据大小不会超出int的范围。
最小公倍数
给定两个正整数,计算这两个数的最小公倍数。
输入样例
10 14
输出样例
70
法一
枚举
用两数中较大的一个数进行倍数枚举,并判断是否为另一个数的倍数,以此寻找最小公倍数。
法二
数学方法
最小公倍数LCM(a,b)=a*b/最大公约数GCD(a,b)
使用公式时要注意a*b会不会超过int的范围,故建议写成 a/GCD(a,b)*b
问题变为求最大公约数GCD
欧几里得算法
辗转相除法:辗转相除法是求两个自然数的最大公约数的一种方法,也叫欧几里德算法。
用辗转相除法求几个数的最大公约数,可以先求出其中任意两个数的最大公约数,再求这个最大公约数与第三个数的最大公约数,依次求下去,直到最后一个数为止。最后所得的那个最大公约数,就是所有这些数的最大公约数。
例如,求(319,377):
∵ 319÷377=0(余319)
∴(319,377)=(377,319);
∵ 377÷319=1(余58)
∴(377,319)=(319,58);
∵ 319÷58=5(余29)
∴ (319,58)=(58,29);
∵ 58÷29=2(余0)
∴ (58,29)= 29;
∴ (319,377)=29
int gcd(int big,int small)
{
int temp;
while(small!=0)
{
temp=big%small;
big=small;
small=temp;
}
return big;
}
//注意,这个代码中输入时大小数据颠倒不会出错(经过一个循环后会被更正)
最终代码
#include<stdio.h>
int gcd(int big,int small);
int main()
{
int a,b;
while(scanf("%d %d",&a,&b)==2)
{
int lcm;
lcm=a/gcd(a,b)*b;
printf("%d\n",lcm);
}
return 0;
}
int gcd(int big,int small)
{
int temp;
while(small!=0)
{
temp=big%small;
big=small;
small=temp;
}
return big;
}
计算N的N次幂的个位数
给定一个正整数N,请计算并输出N的N次方的个位数。
输入第一行是一个正整数T,表示有T组测试用例。
接下来的T行,每行包含一个正整数N(1<=N<=1,000,000,000)。
每组数据都输出N的N次幂的个位数,每组数据输出一行。输入样例
2
3
4
输出样例
7
6
法一
直接计算N*N数据太大,要每乘一次N就取一次个位。(如果N的数据特别大,如本题中,则会超时)
法二
循环节
一个循环的单位,常用于大数据的有规律变换中
先取出N的个位数,再找出其的循环节(循环节的长度一定不超过5个数(奇\偶)),再求即可。
#include<stdio.h>
int gewei(int n);
int MyPow(int a,int b);
int main()
{
int num;
scanf("%d",&num);
for(int i=0;i<num;i++)
{
int n;
int a;
int cycle[5]={0};//创建个位的循环数组
int sn=1;//有效的循环个数
scanf("%d",&n);
a=gewei(n);
cycle[0]=a;
for(int j=1;j<5;j++)
{
cycle[j]=gewei(MyPow(a,j+1));
if(cycle[0]==cycle[j])
{
sn=j;
break;
}
}
int x;//判断是否取循环节中的哪一位
if(n%sn==0)
{
x=sn-1;
}
else
{
x=n%sn-1;
}
printf("%d\n",cycle[x]);
}
return 0;
}
int gewei(int n)//取个位
{
return (n%10);
}
int MyPow(int a,int b)//取整数的次方
{
int final=1;
for(int i=0;i<b;i++)
{
final*=a;
}
return final;
}
新斐波那契数列
现在,有一个新的斐波那契数列,定义如下:
F(0) = 7,
F(1) = 11,
F(n) = F(n-1) + F(n-2) (n>=2).Input
输入包含多组测试样例,每组测试样例包含一个整数n(n < 1,000,000).Output
如果F(n)能够被3整除,请输出”yes”,否则请输出”no”。输入样例
0
1
2
输出样例
no
no
yes
此题若求出F(n)一定会超出范围,故转为求F(n-1)和F(n-2) 模3的值,后可以发现F(1)、F(2)……模3后的余数呈循环节变换,以此找到规律。
#include<stdio.h>
#define MAX 20
int judge(int n);
int main()
{
int n;
while(scanf("%d",&n)==1)
{
if(judge(n)==1)//判断输出
printf("yes\n");
else
printf("no\n");
}
return 0;
}
int judge(int n)//判断是否能被3整除
{
n+=1;//调整脚标
int a[MAX]={0};
int sn;//循环节个数
a[0]=7%3;
a[1]=11%3;
a[2]=(a[0]+a[1])%3;
for(int i=2;i<MAX;i++)//计算循环节
{
a[i+1]=(a[i-1]+a[i])%3;
if(a[i]==a[0]&&a[i+1]==a[1])
{
sn=i;
break;
}
}
if(n%sn==0)
{
if(a[sn-1]==0)
return 1;
else
return 0;
}
else
{
if(a[n%sn-1]==0)
return 1;
else
return 0;
}
}
人见人爱A^B
求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”Input
输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。Output
对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。输入样例
2 3
12 6
0 0
输出样例
8
984
此题应学会快速计算幂的方法。
法一
递归
int power(int a,int n)
{
int ans;
if(n==0) ans=1;
else
{
ans=power(a*a,n/2);//底数平方,指数减半
if(n%2==1) ans*=a; //考虑到指数为奇数的情况
}
return ans;
}
法二
循环
int power(int a,int n)
{
int ans=1;
while(n)
{
if(n%2==1) ans*=a; //奇数情况
a=a*a; //底数平方
n=n/2; //指数减半
}
return ans;
}
最终解法
#include<stdio.h>
int main()
{
int a,b;
while(scanf("%d %d",&a,&b)==2)
{
if(a==0&&b==0)
{break;}
else
{
int ans=1;
a=a%1000;
while(b)
{
a=a%1000;
if(b%2==1) ans*=a;
a*=a;
b/=2;
ans=ans%1000;
}
printf("%d\n",ans);
}
}
}
查找整数
在一组数据中(递增)找到是否存在某个数
二分查找法
前提:数据是单调排序
递归
int BiSearch(int a[],int x,int left,int right)
{
if(left>right) //此处不可有=号
return -1;
else
{
int mid=(left+right)/2;
if(a[mid]==x)
return mid;
else if(x>a[mid])
return BiSearch(a,x,mid+1,right);
else if(x<a[mid])
return BiSearch(a,x,left,mid-1);
}
}
循环
int BiSearch(int a[],int n,int x)
{
int left=0,right=n-1;
while(left<=right) //注意此处=不能少
{
int mid=(left+right)/2; //整数除法
if(a[mid]==x) //找到时
reutrn mid;
if(x>a[mid]) //比中值大
left=mid+1;
else //比中值小
right=mid-1;
}
return -1;
}
二分法的查找时间增加是对数式的增加
例题:
题目描述
给定方程 8x^4 + 7x^3 + 2x^2 + 3x + 6 == Y,请计算x在[0,100]范围内的解。Input
输入数据首先是一个正整数T(1<=T<=100),表示有T组测试数据。
接下来T行,每行包含一个实数Y ( fabs(Y) <= 1e10 )。Output
请计算并输出方程在范围[0,100]内的解,结果精确到小数点后4位。
如果无解,则请输出“No solution!”输入样例
2
100
-4
输出样例
1.6152
No solution!
使用二分法不断地趋近正确值。
#include<stdio.h>
#include<math.h>
double F(double x);
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
double y;
scanf("%lf",&y);
if(F(0)<=y&&F(100)>=y)
{
double left=0;
double right=100;
double mid=50;
while(right-left>1e-6)//确定精度
{
mid=(left+right)/2;
if(F(mid)>y)
{
right=mid-1e-7;//调整精度
}
else
{
left=mid+1e-7;
}
}
printf("%0.4f\n",mid);
}
else{
printf("No solution!\n");
}
}
}
double F(double x)
{
double y;
y=8*pow(x,4)+7*pow(x,3)+2*pow(x,2)+3*x+6;
return y;
}
三分算法
若是有凸型的一组数据找极大/极小值,可使用三分法(此处的函数不需要可导)
mid1=(left*2+right)/3
mid2=(left+right*2)/3
此时,有最左值、mid1、mid2、最右值,将函数分为三段,比较mid1和mid2,若mid1>mid2,则说明极大值一定不在最右边的一段;若mid1<mid2,则说明极大值一定不在最左边的一段;若mid1=mid2,则说明极大值在中间的一段。以此种方法不断减少范围,确定最终值。