挑战程序设计竞赛 第二章 初级篇 2.6 数学问题的解题窍门数论 辗转相除法 最大公约数 扩展欧几里得算法 素性测试 埃氏筛 快速幂 求模运算 约数枚举 整数分解

数学,特别是数论,在计算机科学中有着重要地位。

辗转相除法求最大公约数:

这里有一个题,是一个数学的思路。线段上格点的个数,注释:格点是指横纵坐标均为整数的点。

如果使用暴力遍历的方法,检查满足min(x1, x2) ≤ x ≤ max(x1, x2),min(y1, y2) ≤ y ≤ max(y1, y2)的格点。这种做法当然可以找出正确答案,但其复杂度为O(|x1-x2| × |y1-y2|),对于数据较大的输入很难处理,我们便使用数学方法优化此题解法。

这道题数学方法:|x1-x2| 和 |y1-y2| 的最大公约数-1(还需要特判一下x1-x2和y1-y2为0)。

接下来就讨论最大公约数的求法了,一个基础的算法叫做辗转相除法,代码:

int gcd(int a, int b)
{
	if(b == 0) return a;
	return gcd(b, a % b);
}

也可以使用三目运算符,写出来更简化:

int gcd(int a, int b)
{
	return b == 0 ? a : gcd(b, a % b);
}

这个可以很容易记下来。

还记得c++库中自带了求最大公约数的函数__gcd(x, y),两个下划线加gcd其中写上两参数。

这个算法的时间复杂度当然很小了,O(log max(a, b))。

继续介绍扩展欧几里得算法,这个曾经做过题,题解连接:

洛谷 P1082 [NOIP2012 提高组] 同余方程 欧几里得算法 扩展欧几里得算法 贝祖定理 乘法逆元 最大公约数 整除与剩余

其中也有 同余方程 欧几里得算法 扩展欧几里得算法 贝祖定理 乘法逆元 最大公约数 整除与剩余等记录。

对辗转相除法做一些扩展,求“整数x和y使得ax+by=1”,很显然,如果gcd(a, b) ≠ 1, 无解。如果gcd(a, b) = 1, 求ax+by=gcd(a, b)。

int exgcd(int a, int b, int& x, int& y)
{
	int d = a;
	if(b != 0)
	{
		d = exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
	else
	{
		x = 1;
		y = 0;
	}
}

下面继续介绍有关素数的基础算法。

素性测试,这可能是大家做过的最基础的数学算法之一,给定一个整数n,判断n是不是素数。

最简单的算法,利用定义进行枚举:

bool is_prime(int n)
{
	for(int i=2;i*i<=n;i++)
	{
		if(n%i==0) return false;
	}
	return n != 1;
}

这里还有不同形式的两个算法,约数枚举和整数分解。

约数枚举:

vector<int> divisor(int n)
{
	vector<int> res;
	for(int i=1;i*i<=n;i++)
	{
		if(n % i == 0)
		{
			res.push_back(i);
			if(i!=n%i) res.push_back(n/i);
		}
	}
	return res;
}

整数分解:

map<int, int> prime_factor(int n)
{
	map<int, int> res;
	for(int i=2;i*i<=n;i++)
	{
		while(n%i==0)
		{
			++res[i];
			n /= i;
		}
	}
	if(n!=1) res[n]=1;
	return res;
}

此算法的时间复杂度在O(sqrt(n)),已经很高效了,当然,如果要使用效率更加高效的算法,我们不妨看一下埃氏筛法。

问题:给定整数n,问n以内有多少素数。

这个算法的思路是将2到n范围内所有整数写下来,从最小的2,将2的倍数都划去,接着3,把三的倍数划去,以此类推,最后剩下的就是素数。

int prime[MAX_N];
bool is_prime[MAX_N + 1];

int sieve(int n)
{
	int p = 0;
	for(int i=0;i<=n;i++) is_prime[i] = true;
	is_prime[0] = is_prime[1] = false;
	for(int i=2;i<=n;i++)
	{
		if(is_prime[i])
		{
			prime[p++] = i;
			for(int j=2*i; j<=n;j+=i) is_prime[j] = false;
		}
	}
	return p;
}

关于素数最后还有一个区间筛,求a,b两点之间的素数个数,当然,可以使用埃氏筛把从2到a的素数个数求出,再把从2到b的素数个数求出,就可做出。

下面介绍一下模运算,经常有题目的结果超出64位整数范围,要求取余mod一个数,或者使用高精度算法,这个其他文章有解释:

这是高精乘的一篇文章:

洛谷 P5703 苹果采购 简单乘法 P1303 A*B Problem 高精度乘法 高精度算法 进位 数据范围 基本运算

关于求模运算,一些数学思想:

a+b≡c+d(mod m)
a-b≡c-d(mod m)
a×b≡c×d(mod m)

最后,再介绍一个重要的算法,快速幂算法。

求幂运算怎么求?pow(a, b),可以,还有一个很重要且巧妙的方法——快速幂。

typedef long long ll;

ll mod_pow(ll x, ll n, ll mod)
{
	ll res = 1;
	while(n > 0)
	{
		if(n & 1) res = res * x % mod;
		x = x * x % mod;
		n >>= 1;
	}
	return res;
}

其时间复杂度为O(log n),还可以使用递归实现:

ll mod_pow(ll x, ll n, ll mod)
{
	if(n == 0) return 1;
	ll res = mod_pow(x * x % mod, n / 2, mod);
	if(n & 1) res = res * x % mod;
	return res;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三元湖有大锦鲤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值