数学归纳法
证明P(1)
假设P(k)正确,证明 P(k+1)
证毕P(n)正确
int main()
{
int sum=0;//sum代表前0个数字的和
for(int i=0;i<=100;i++)
sum=sum+i;//方程右边sum代表前i-1个数字的和,左边是i个数字的和
cout<<sum<<endl;
}
int f(int n)
{
if(n==1) return 1;//f(1)正确
else return n*f(n-1);//假设f(n-1)正确->f(n)正确
}
了解数归更多是为了保证程序设计的正确性,迭代的程序可以用数学归纳法证明。
递归实现组合型枚举
题目:从1-n这n个整数中随机抽取m个,每种方案的数从小到大排列,按字典序输出可能的选择方案。
演示:3 2
输出:123 132 231
分析:求组合数的组合
int main()
{
int n,m;
cin>>n>>m;
vector<int> buff;//临时存储区存储已经选择的数字
output(n,m,1,buff);//第三位放置当前可以选择的数字的最小值
return 0;
}
void output(int n,int m,int p,vector<int>& buff){
if(m==buff.size())
{
for(int i=0;i<m;i++)
{
if(i!=0) cout<<" ";
cout<<buff[i]
}
cout<<endl;
return;
}
for(int i=p;i<=n;i++)
{
buff.push_back(i);
output(n,m,i+1,buff);
buff.pop_back();
}
return;
}
初等数论
素数筛
思路:
(1)首先将2到n范围内的整数写下来,其中2是最小的素数。将表中所有的2的倍数划去.
(2)表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数,再将表中所有的3的倍数划去
(3)让每个合数只被它的最小质因子筛选一次,以达到不重复的目的
证明:合数=A*B(任何一个合数都可以用俩个不为1的素数标识,其中一个大于sqrt(n),另一个小于sqrt(n))
int prime[n+5]={0};
//prime[0]存储素数的个数
void prime()
{
for(int i=2;i*i<=n;i++)
{
if(prime[i]) continue;
for(int j=i*i;j<n;j+=i) prime[j]=1;
}
for(int i=2;i<=n;i++)
if(prime[i]==0)
prime{++prime[o]]=i;
return;
}
prime数组一分为二:
前半段到prime[0]之前存储素数表,之后存储素数的标记信息(素数筛)。
逆元+同余
1.俩个数字对相同的数字取余得到相同的余数
假设这个数字为A和B,此时我们假设认为这俩个数字相等。
A+d == B+d (mod n)
A-d == B+d (mod n)
A * d== Bd (mod n)
2.
在mod意义下,不能直接除以一个数,而要乘以它的逆元。
逆元是d‘ 满足:dd’=1(mod n)
a%b=a-kb k=(a/b)向下取整
欧几里得算法:
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
证明1:b和a%b的最大公约数,是a和b的公约数
证明2:b和a%b的最大公约数也是a和b的最大公约数
假设A和B互质,此时可以计算A在B下的逆元。
贝祖等式:Ax+By=gcd(a,b)=c
扩展欧几里得算法:
x*b+(a-kb)*y=c;
ay+(x-ky)*b=c;
//此时求得a和b的整数解
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0){
//最后一层
x-1,y=0;
return a;
}
int nx,ny;
int r=ex_gcd(b,a%b,nx,ny);
x=ny;
y=nx-a/b*ny;
return r;
}
int inv(int a,int b)
{
int x,y;
ex_gcd(a,b,x,y);//假设a和b一定互素,得到a和b的一组整数解
x%=b;
x+=b;
x%=b;//使得x是个正数
return x;
}
斐波那契推论
F(n+m)=F(m) F(n+1)+F(m-1) F(n)
尝试计算F(1e^15)%9973的值
递归令m=n=n/2
unoedered_map<int,int> f_result;
int f(int n)
{
if(n<=2) return!!n;//归零化处理,如果n是0归零化后为0,其余数字归零化后为1
if(f_result(n)!=f_result.end()){//记忆化思路
int a=n/2,b=n-a;
int val1=f(b),val2=f(a+1),val3=f(b-1),val 4=f(a);
f_result(n)=(val1*val2+val3*val4)%9973;
}
return f_result[n];
}
快速幂算法
O(logN)
例如:求a^10;
(1)分解10为二进制:(1010),位权分别为8 4 2 1
(2)此时可以发现位权是8和2对应a^8 和 a^2
(3)此时用计算出a的一次到a的四次,再把a的平方和八次挑出来乘一次
(4)判断最后一位是奇数还是偶数来判断这个二进制数字末尾是0还是1
满足结合律均可使快速幂
int quick_power(int a,int b)
{
int ans=1,temp=a;
while(b){
if(b%2) ans*=temp;
temp*=temp;
b/=2;
}
return ans;
}