二次剩余方程求解

题目描述:给定方程x2=a(modp),p为素数,求在区间(0,p)的解。


首先,需要判断是否有解,方程有解充要条件为a(p-1)/2=1(modp)。

充分性:

设一原根g,存在唯一的k,使得g^k=a(modp)a(p-1)/2=g^((p-1)*k/2)=(g^((p-1)/2))^k=(-1)^k(modp)=1,因此k为偶数。

所以g^(k/2)是方程的根,得证。

必要性:

费马小定理可知,对于任何属于(0,p)的数b,都满足b^(p-1)=1(modp)。假设x为方程的解,则(x^2)^((p-1)/2)=a^((p-1)/2),即a^((p-1)/2)=x^(p-1)=1(modp),得证

接下来构造一个数b,使得w=b^2-a不是二次剩余(满足w^((p-1)/2)=-1(modp));则x=(b+sqrt(w))^((p+1)/2)为方程的解。

证明:

((b+sqrt(w))^p=b^p+w^(p/2)(modp)=b+w^((p-1)/2)*sqrt(w)(modp)=b-sqrt(w)(modp)

x^2=((b+sqrt(w))^(p+1)=(b-sqrt(w))(b+sqrt(w))=b^2-w=a(modp),得证。


代码实现:

#include 
using namespace std;

#define LL __int64 


LL w;
struct Point//x + y*sqrt(w)
{
	LL x;
	LL y;
};

LL mod(LL a, LL p)
{
	a %= p;
	if (a < 0)
	{
		a += p;
	}
	return a;
}

Point point_mul(Point a, Point b, LL p)
{
	Point res;
	res.x = mod(a.x * b.x, p);
	res.x += mod(w * mod(a.y * b.y, p), p);
	res.x = mod(res.x, p);
	res.y = mod(a.x * b.y, p);
	res.y += mod(a.y * b.x, p);
	res.y = mod(res.y , p);
	return res;
}

Point power(Point a, LL b, LL p)
{
	Point res;
	res.x = 1;
	res.y = 0;
	while(b)
	{
		if (b & 1)
		{
			res = point_mul(res, a, p);
		}
		a = point_mul(a, a, p);
		b = b >> 1;
	}

	return res;
}

LL quick_power(LL a, LL b, LL p)//(a^b)%p
{
	LL res = 1;
	while(b)
	{
		if (b & 1)
		{
			res = (res * a) % p;
		}
		
		a = (a * a) % p;
		b = b >> 1;
	}

	return res;
}


LL Legendre(LL a, LL p) // a^((p-1)/2)
{
	return quick_power(a, (p - 1) >> 1, p);
}

LL equation_solve(LL b, LL p)//求解x^2=b(%p)方程解
{
	if ((Legendre(b, p) + 1) % p == 0)
	{
		return -1;//表示没有解
	}

	LL a, t;
	while(true)
	{
		a = rand() % p;
		t = a * a - b;
		t = mod(t, p);
		if ((Legendre(t, p) + 1) % p == 0)
		{
			break;
		}
	}

	w = t;
	Point temp, res;
	temp.x = a;
	temp.y = 1;
	res = power(temp, (p + 1) >> 1, p);
	return res.x;
}

int main()
{
	LL b, p;
	scanf("%I64d %I64d", &b, &p);
	printf("%I64d\n", equation_solve(b, p));//输出为-1表示,不存在解
	return 0;

}


你可能感兴趣的:(数论)