求最大公约数
求最大公约数,采用的是欧几里得算法,其描述如下:
gcd(a,b) = gcd(b,a mod b) (a>b 且a mod b 不为0)
下面给出证明过程:
1. 将a表示为a = kb + r,其中r = a mod b。
2. 设d | a且d | b(即d为a,b的一个公约数)。则由d | a,d | b => d | (a - kb) => d | r
3. 由d | b,d | r可知d也是(b, a mod b)的公约数
4. 由于d的选取的任意性,(a, b)和(b, a mod b)的公约数一致,最大公约数也相同,得证。
C语言代码如下(递归):
int gcd(int x, int y)
{
return y == 0 ? x : gcd(y, x%y);
}
求模乘的逆
ab ≡ 1 mod n,已知a和n,求b。
解法需采用上一节中的欧几里得算法,将每一步的商(a / b)记录下来并压栈。然后再出栈经过计算获得逆元的值。
下面是完整的C++代码,使用递归求解。
#include <iostream>
#include <stack>
using namespace std;
stack<int> num; //声明一个存放辗转相除每一步除数的向量
void gcd(int x, int y);
int getb(int x, int y);
int main()
{
int a = 37, b, n = 1700; //ab=1 mod n
gcd(n, a); //辗转相除
b = getb(0, 1); //获取模逆
printf("%d\n", b > 0 ? b : n + b); //结果可能是负数,将其转化为正数后输出
}
void gcd(int x, int y) //辗转相除(递归)
{
if (y == 1)
return ;
num.push(x / y); //将除数压栈
gcd(y, x % y); //递归
}
int getb(int x, int y) //获取模逆(递归)
{
if (num.size())
{
int top = num.top(); //获取栈顶元素
num.pop(); //出栈
return getb(y, x - y * top); //递归
}
return y;
}
扩展欧几里得算法
x * a + y * b = c, 已知a, b, c,求x, y
扩展欧几里得算法常用于解模线性方程及方程组
扩展欧几里得定理,描述如下:
已知a,b,满足p * a + q * b = gcd(a, b)的(p, q)一定存在
证明过程略,有兴趣可以参考相关数论书籍。
扩展欧几里得算法的C++语言描述如下(递归):
#include <iostream>
using namespace std;
int a = 21, b = 35;
int x, y;
void getres(int r1, int r2, int x1, int x2, int y1, int y2);
int main()
{
int x1 = 1, y1 = 0, x2 = 0, y2 = 1;
getres(a, b, x1, x2, y1, y2);
printf("%d %d", x, y);
}
void getres(int r1, int r2, int x1, int x2, int y1, int y2)
{
int q, m, n;
if(r2 = a * x2 + b * y2)
{
q = r1 / r2;
m = x1 - q * x2;
n = y1 - q * y2;
x = x2, y = y2;
getres(r2, r2, x2, m, y2, n); //递归
}
}
求欧拉函数
欧拉函数值,即小于n且与n互素的数的个数,记作p(n)。
性质:
1. n为素数时,p(n) = n -1
2. p(p * q) = p(p) * p(q) (p, q互素)
用上述性质即可完成欧拉函数的求解。
C++代码(输入n,输出p(n)):
int p(int n)
{
int m = (int)sqrt(n);
int ans = n;
for (int i = 2; i <= m; i++)
if (n % i == 0) //依次处理素因子
{
ans = ans / i * (i - 1);
while (n % i == 0)
n /= i; //将素因子i除干净
}
if (n > 1)
ans = ans / n * (n - 1);
return ans;
}