其他分享
首页 > 其他分享> > 浅谈高次方程之二次剩余

浅谈高次方程之二次剩余

作者:互联网

前言:

我们知道高次方程有两种,在这一章我们来讨论这一种

 

 

 但我们只讨论a=2的特殊情况。

因为作者本身水平很弱,在此并不过多的说一些很学术的词语或证明,有的甚至不会给出证明,如有需要请自行度娘。

在本章的篇幅会大量倾斜于how,而不是why。如果你只想明白该怎么解,那么您可以继续读下去。

在此默认大家都知道(https://kewth.github.io/2019/10/21/%E4%BA%8C%E6%AC%A1%E5%89%A9%E4%BD%99/)这个网址上的东西。

有一个算法可以来解决这类问题,Cipolla算法。但是有个限制就是p为奇素数。

这个算法大概是什么步骤呢?

首先我们要判断n是否有解。

定义关于n和p的一种符号 勒让德记号

当(n)=1时,n一定有解。

当  (n)    =-1时,n一定无解。

 

 

 判断完n是否有解,我们可以知道,n的解应该有两个,且互相为modp意义下的相反数。

下面推出一个重要定理

 

 

 a怎么求呢?我们知道二次剩余的数量为(p-1)/2,所以非二次剩余为(p-1)/2,所以我们可以选随机数。五五开的概率,2,3次估计就出来了。

求出了一个a,就求出了w,就求出了一个x。但w可能是个虚数,x可能是个虚数。根据拉格朗日定理,这里的x的虚部系数为0,所以x不为虚数。但我们仍需要在w的运算中使用复数运算。

 

 

 类似的,给出复数运算的代码:

num mul(num a,num b,ll p)
{
    num ans={0,0};
    ans.x=(((a.x*b.x%p+a.y*b.y%p*w%p))%p+p)%p;
    ans.y=((a.x*b.y%p+a.y*b.x%p)%p+p)%p;
    return ans;
}

可以对着看一下。

复数的表示:

struct num
{
    ll x,y;
};

x为整数部分,y为复数系数,此处复数以w为单位。

然后这个算法就差不多了,下面给出模板题代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll w;
struct num
{
    ll x,y;
};//复数定义
ll ksm(ll a,ll b,ll p)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=1ll*ans%p*a%p;
        a=a%p*a%p;
        b>>=1;
    }
    return ans%p;
}//快速幂
num mul(num a,num b,ll p)
{
    num ans={0,0};
    ans.x=(((a.x*b.x%p+a.y*b.y%p*w%p))%p+p)%p;
    ans.y=((a.x*b.y%p+a.y*b.x%p)%p+p)%p;
    return ans;
}//复数乘法
ll powi(num a,ll b,ll p)
{
    num ans={1,0};
    while(b)
    {
        if(b&1) ans=mul(ans,a,p);
        a=mul(a,a,p);
        b>>=1;
    }
    return ans.x%p;
}//复数快速幂
ll solve(ll n,ll p)
{
    n%=p;
    if(p==2) return n;//特判减少常数
    if(ksm(n,(p-1)/2,p)==p-1) return -1;
    ll a;
    while(1)
    {
        a=rand()%p;
        w=((a*a%p-n)%p+p)%p;
        if(ksm(w,(p-1)/2,p)==p-1) break ;
    }//求a
    num x={a,1};//复数初定义
    return powi(x,(p+1)/2,p);
}
int main()
{
    srand(time(0));
    int t;
    cin>>t;
    while(t--)
    {
        ll n,p;
        cin>>n>>p;
        if(!n)
        {
            cout<<0<<endl;
            continue;
        }
        ll ans1=solve(n,p),ans2;
        if(ans1==-1) cout<<"Hola!"<<endl;
        else
        {
            ans2=p-ans1;
            if(ans1>ans2) swap(ans1,ans2);
            if(ans1==ans2) cout<<ans1<<endl;
            else cout<<ans1<<' '<<ans2<<endl;
        }
    }
    return 0;
}

好的,就到这里结束了。但其实这个算法中间有很多证明过程我没写,有的我也不太懂,但解题谁还管你证明?(手动狗头)话说我开始给的网址讲的算比较详细了,但下面复数运算就没讲了,于是我又去百度&&洛谷题解区找了找,最后整合成这篇文章。(大体还行,细节就别细究了)

总结:

哎,写博客真好玩啊。(又在水总结)等我数学功底强一点了,我再去挑战高次方程的巅峰方程吧。(手动狗头)

 

标签:剩余,return,浅谈,高次方程,ll,x%,num,复数,ans
来源: https://www.cnblogs.com/tomo-ROK/p/15110249.html