求解线性同余方程--扩展欧几里得

2024-01-17 00:58

本文主要是介绍求解线性同余方程--扩展欧几里得,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

资料来源:https://blog.csdn.net/

//求解ax=b(mod m) 返回0为无解,否则返回gcd(a,m)个mod m意义下的解,用X[]存
int mod(int a, int b, int m)
{return equation(a, m, b);
}

先看一道题目:

                                                    poj 2115 C Looooops 【扩展欧几里得】

Description:

A Compiler Mystery: We are given a C-language style for loop of type 

for (variable = A; variable != B; variable += C)statement;

I.e., a loop which starts by setting variable to value A and while variable is not equal to B, repeats statement followed by increasing the variable by C. We want to know how many times does the statement get executed for particular values of A, B and C, assuming that all arithmetics is calculated in a k-bit unsigned integer type (with values 0 <= x < 2k) modulo 2k. 

Input:

The input consists of several instances. Each instance is described by a single line with four integers A, B, C, k separated by a single space. The integer k (1 <= k <= 32) is the number of bits of the control variable of the loop and A, B, C (0 <= A, B, C < 2k) are the parameters of the loop. 

The input is finished by a line containing four zeros. 

Output:

The output consists of several lines corresponding to the instances on the input. The i-th line contains either the number of executions of the statement in the i-th instance (a single integer number) or the word FOREVER if the loop does not terminate. 

Sample Input

3 3 2 16
3 7 2 16
7 3 2 16
3 4 2 16
0 0 0 0

Sample Output

0
2
32766
FOREVER
for (variable = A; variable != B; variable += C)statement;

资料来源1:https://blog.csdn.net/

题意1:求解最小的y使得 A + C*y = B (mod 1<<k)。

思路1:化简等式 (1<<k)*x + y*C = B-A。当且仅当(B-A) % gcd(1<<k, C) == 0才有解。

           令temp = (B-A)/gcd(1<<k, C);

           化简——(1<<k)*x/temp + y*C/temp = gcd(1<<k, C);

           求出x和y,y *= temp。

           由扩展欧几里得 定理三——若gcd(a, b) = d,则方程ax ≡ c (mod b)在[0, b/d - 1]上有唯一解。

           求解最小的y时,只需对(1<<k) / gcd(1<<k, C)取余即可。

AC代码1:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;LL A, B, C, k;
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{if(b == 0) {d = a, x = 1, y = 0;}else{exgcd(b, a%b, d, y, x);y -= x * (a / b);}
}
void solve()
{B -= A;LL d, x, y;LL a = (1LL<<k);exgcd(a, C, d, x, y);if(B % d)printf("FOREVER\n");else{x *= (B / d); y *= (B / d);y = (y % (a / d) + a / d) % (a / d);printf("%lld\n", y);}
}int main()
{while(scanf("%lld%lld%lld%lld", &A, &B, &C, &k) != EOF){if(A == 0 && B == 0 && C == 0 && k == 0)break;solve();}return 0;
}

资料来源2:https://www.cnblogs.com/

本题和poj1061青蛙问题同属一类,都运用到扩展欧几里德算法,可以参考poj1061,解题思路步骤基本都一样。
一,题意:
  对于for(i=A ; i!=B ;i+=C)循环语句,问在k位存储系统中循环几次才会结束。
    比如:当k=4时,存储的数 i 在0-15之间循环。(本题默认为无符号)
  若在有限次内结束,则输出循环次数。
  否则输出死循环。
二,思路:
       本题利用扩展欧几里德算法求线性同余方程,设循环次数为 x ,则解方程 (A + C*x) % 2^k = B ;求出最小正整数 x。 
  1,化简方程化为求线性同余方程标准式 ax ≡ b (mod n);
  2,扩展欧几里德算法求解线性同余方程 C*x ≡ B-A (mod 2^k);
  3,求出最小非负整数解。
三,步骤:
  1,化简:(A + C*x) mod 2^K = B  -->  C*x mod 2^k = B-A  -->   C*x ≡ B-A (mod 2^k);
  2,求线性同余方程 C*x ≡ B-A (mod 2^k) , 就相当于求二元一次方程 C*x + 2^k * y = B-A 
    i,代入扩展欧几里德算法,求解方程 C*x + 2^k * y = gcd(C , 2^k) ; 
      ii,利用方程 C*x + 2^k * y = gcd(C , 2^k)的解 x0 以及公式 x1 = x0 * c/d 求出原方程 a*x + b*y = c 的解 x1 ;前提是:d|c (c 能被 d 整除);
  3,利用周期性变化求最小的非负整数解 公式: x1 = (x1 % (b/d) + (b/d) ) % (b/d);

             若方程的C*x + 2^k * y = B-A 的一组整数解为(x1 , y1),则它的任意整数解为(x1 + k * (b/d) , y1 - k * (a/d) ) ( k取任意整数 ), T = b/d就为 x1 增长的周期 

    i,若x1为负值,取最大的非正值:x1 = x1 % T ; 若x1为正值,以下两步无影响; 
    ii,取正 :x1 = x1 + T ;
    iii, 防止 i 中的 x1=0 即 ii 中的 x1=T :x1 = x1 % T ;

代码如下:

#include<iostream>
using namespace std;void exgcd(long long a,long long b,long long& d,long long& x,long long& y){//int& a 是定义一个存放整形变量a的地址if(!b){ d=a ; x=1 ; y=0; }              // d用来存储gcd(a,b)的值 else { exgcd(b , a%b , d , y , x); y -= x* (a/b); }
}int main(){long long  A,B,C,d,x,y,T;int k ; while(cin>>A>>B>>C>>k){if(A==0&&B==0&&C==0&&k==0)break;long long n = 1LL<<k; //n = 1 * 2^k ;注意此处,若为__int64,则应该是n = (__int64)1 << k;exgcd(C,n,d,x,y);           if( (B-A) % d != 0 ){cout<<"FOREVER\n";}else {x = x * (B-A) / d ;  T = n / d;x = ( x%T + T ) % T ;cout<<x<<endl;}} return 0; 
}

AC代码3:

就是求一个 同余方程组的最小解

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;typedef long long LL;LL ex_gcd(LL a,LL b,LL &x,LL &y){if(!b){x=1,y=0;return a;}else{LL gcd = ex_gcd(b,a%b,x,y);LL tmp = x;x = y;y = tmp-a/b*y;return gcd;}
}int n;
LL r1,r2,a1,a2,x,y;LL solve(){LL r1,r2,a1,a2,x,y;bool tag = 1;cin>>a1>>r1;for(int i=1;i<n;i++){cin>>a2>>r2;LL a = a1,b=a2,c = r2-r1;LL d = ex_gcd(a,b,x,y);if(c%d) tag = 0;LL t = b/d;x = (x*(c/d)%t+t)%t;r1 = a1*x+r1;a1 = a1*(a2/d);}if(tag) return r1;else return -1;
}int main()
{while(~scanf("%d",&n)){printf("%lld\n",solve());}return 0;
}

AC代码4:

/*
题意:
(A+Cx)%2^k=B ==> A+Cx = 2^k*y+B
==> Cx-2^k*y=B-A;
形如 ax+by=c
a=C
b=2^k
c=B-A;*/#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;__int64 f[33];
void make_hxl()
{int i;f[0]=1;f[1]=2;for(i=2;i<=32;i++)f[i]=f[i-1]*2;
}__int64 Ex_gcd(__int64 a,__int64 b,__int64 &x,__int64 &y)
{if(b==0){x=1;y=0;return a;}__int64 g=Ex_gcd(b,a%b,x,y);__int64 hxl=x-(a/b)*y;x=y;y=hxl;return g;
}void make_ini(__int64 A,__int64 B,__int64 C,__int64 k)
{__int64 c,a,b,x,y,d;a=C;b=f[k];c=B-A;d=Ex_gcd(a,b,x,y);if(c%d!=0){printf("FOREVER\n");return;}x=c/d*x;b=b/d;x=(x%b+b)%b;printf("%I64d\n",x);
}
int main()
{__int64 A,B,C,k;make_hxl();while(scanf("%I64d%I64d%I64d%I64d",&A,&B,&C,&k)>0){if(A==0 && B==0 && C==0 &&k==0)break;make_ini(A,B,C,k);}return 0;
}

 

再来看一道题目:

                                                                   青蛙的约会

Description

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。 

Input

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

Sample Input

1 2 3 4 5

Sample Output

4

资料来源:https://mp.csdn.net/

AC代码1:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
ll e_gcd(ll a,ll b,ll &x,ll &y)
{if (b == 0){x = 1,y = 0;return a;}ll ans = e_gcd(b,a%b,x,y);ll tmp = x;x = y;y = tmp - a/b*y;return ans;
}
ll cal(ll a,ll b,ll c)//求最小的x使ax+by=c
{ll x,y;ll gcd = e_gcd(a,b,x,y);if (c%gcd != 0) return -1;x *= c/gcd;b/=gcd;if (b < 0) b = -b;ll ans = x%b;if (ans <= 0) ans += b;return ans;
}
int main()
{ll xa,xb,va,vb,L;while (~scanf("%lld %lld %lld %lld %lld",&xa,&xb,&va,&vb,&L)){ll ans = cal(vb-va,L,xa-xb);if (ans == -1) puts("Impossible");else printf("%lld\n",ans);}return 0;
}

资料来源:https://blog.csdn.net/

AC代码2:

青蛙的约会题解:

假设走了t次相遇,则有等式(x+mt)-(y+nt)=pL成立,等价于求解同余方程(n-m)t≡(x-y) (mod L)的最小整数解

(a)对于一般同余方程ax=d mod b,方程有解,则有(a,d)| b ,所以问题第一步判断解的情况

(b)有(n-m)t+pL=x-y,t、p均为未知变量,将问题转化为求解ax+by=d的最小整数x,扩展欧几里得算法:

briefly:扩展欧几里得算法是辗转相除法求gcd的拓展,表现在ax+by=gcd(a,b),函数extended_gcd()不仅能返回gcd(a,b),还能求出gcd的线性系数x,y,具体的操作步骤如下:

 

 ①首先化简 ,得到新的ax+by=d,注意此时(a,b)=1

 ②先求ax+by=1的解x0、y0(解具有唯一性),利用扩展欧几里得算法得到唯一解x0,则ax+by=d的解x=d*x0

 ③通解X=x+b*k(k为整数)

(c)通过(b)实际上可以得到同余方程的通解,但是题目要求最小整数解,利用min=(X%b+b)%b,X取正取负均满足最小,问题得解

以CX=B-A(mod M)为例:

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
typedef long long ll;
const int maxn=1000000+10;
ll x,y;
ll extended_gcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得算法求ax+by=gcd(a,b)的线性系数x,y,返回gcd(a,b)
{if(b==0) {x=1;y=0;return a;}ll d=extended_gcd(b,a%b,y,x);y-=a/b*x;return d;
}
int main()
{// freopen("input.txt","r",stdin);ll a,b,m,A,B,C,k;while(cin>>A>>B>>C>>k&&(A||B||C||k)){a=C;b=(ll)1<

资料来源:http://www.cnblogs.com/

AC代码3:

/*
题意:两只青蛙同方向跳给出初始位置,和每一次跳的距离和总的长度
问第几天跳到一起,约会了.两只青蛙在同一个地方,满足<------------------x-------------y---------m*T-p*L = n*T-(x-y);   向西为正
转化成  T*(m-n)-p*L =y-x;
==>     ax+by=c;a=m-n;b=-L;c=y-x;
*/#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;__int64 Ex_GCD(__int64 a,__int64 b,__int64 &x,__int64 &y)
{if(b==0){x=1;y=0;return a;}__int64 g=Ex_GCD(b,a%b,x,y);__int64 hxl=x-(a/b)*y;x=y;y=hxl;return g;
}int main()
{__int64 x,y,m,n,l,a,b,c,k,x1,y2;while(scanf("%I64d%I64d%I64d%I64d%I64d",&x,&y,&m,&n,&l)>0){a=m-n;b=-l;c=y-x;k=Ex_GCD(a,b,x1,y2);if(c%k!=0){printf("Impossible\n");continue;}b=b/k;//   b/gcd(a,b);x1=x1*(c/k); // x=x*c/gcd(a,b);x1=x1%(b);while(x1<0) x1=x1+b;printf("%I64d\n",x1);}return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

这篇关于求解线性同余方程--扩展欧几里得的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/614461

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

最大公因数:欧几里得算法

简述         求两个数字 m和n 的最大公因数,假设r是m%n的余数,只要n不等于0,就一直执行 m=n,n=r 举例 以18和12为例 m n r18 % 12 = 612 % 6 = 06 0所以最大公因数为:6 代码实现 #include<iostream>using namespace std;/

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

✨机器学习笔记(二)—— 线性回归、代价函数、梯度下降

1️⃣线性回归(linear regression) f w , b ( x ) = w x + b f_{w,b}(x) = wx + b fw,b​(x)=wx+b 🎈A linear regression model predicting house prices: 如图是机器学习通过监督学习运用线性回归模型来预测房价的例子,当房屋大小为1250 f e e t 2 feet^

PHP7扩展开发之数组处理

前言 这次,我们将演示如何在PHP扩展中如何对数组进行处理。要实现的PHP代码如下: <?phpfunction array_concat ($arr, $prefix) {foreach($arr as $key => $val) {if (isset($prefix[$key]) && is_string($val) && is_string($prefix[$key])) {$arr[

PHP7扩展开发之字符串处理

前言 这次,我们来看看字符串在PHP扩展里面如何处理。 示例代码如下: <?phpfunction str_concat($prefix, $string) {$len = strlen($prefix);$substr = substr($string, 0, $len);if ($substr != $prefix) {return $prefix." ".$string;} else

PHP7扩展开发之类型处理

前言 这次,我们将演示如何在PHP扩展中如何对类型进行一些操作。如,判断变量类型。要实现的PHP代码如下: <?phpfunction get_size ($value) {if (is_string($value)) {return "string size is ". strlen($value);} else if (is_array($value)) {return "array si