我必须找出方程ax+by=c
的整数解,使x>=0
和y>=0
以及(x+y) is minimum
的值。
我知道如果c%gcd(a,b)}==0
那么它总是可能的。如何找到x和y的值?
我的方法
for(i 0 to 2*c):
x=i
y= (c-a*i)/b
if(y is integer)
ans = min(ans,x+y)
有没有更好的方法呢?有更好的时间复杂性。
答案 0 :(得分:5)
使用Extended Euclidean Algorithm和linear Diophantine equations理论,无需搜索。这是一个Python 3实现:
def egcd(a,b):
s,t = 1,0 #coefficients to express current a in terms of original a,b
x,y = 0,1 #coefficients to express current b in terms of original a,b
q,r = divmod(a,b)
while(r > 0):
a,b = b,r
old_x, old_y = x,y
x,y = s - q*x, t - q*y
s,t = old_x, old_y
q,r = divmod(a,b)
return b, x ,y
def smallestSolution(a,b,c):
d,x,y = egcd(a,b)
if c%d != 0:
return "No integer solutions"
else:
u = a//d #integer division
v = b//d
w = c//d
x = w*x
y = w*y
k1 = -x//v if -x % v == 0 else 1 + -x//v #k1 = ceiling(-x/v)
x1 = x + k1*v # x + k1*v is solution with smallest x >= 0
y1 = y - k1*u
if y1 < 0:
return "No nonnegative integer solutions"
else:
k2 = y//u #floor division
x2 = x + k2*v #y-k2*u is solution with smallest y >= 0
y2 = y - k2*u
if x2 < 0 or x1+y1 < x2+y2:
return (x1,y1)
else:
return (x2,y2)
典型运行:
>>> smallestSolution(1001,2743,160485)
(111, 18)
它的工作方式:首先使用扩展的欧几里德算法来查找d = gcd(a,b)
和一个解,(x,y)
。所有其他解决方案的格式为(x+k*v,y-k*u)
,其中u = a/d
和v = b/d
。由于x+y
是线性的,因此它没有关键点,因此当x
尽可能小或y
尽可能小时,在第一象限中最小化。上面的k
是一个任意整数参数。通过适当使用floor
和ceiling
,您可以找到x
尽可能小的整数点,或y
尽可能小。拿一个总和最小的那个。
在编辑:我的原始代码使用了应用于math.ceiling
的Python函数-x/v
。对于非常大的整数,这是有问题的。我调整它,以便只使用int操作计算上限。它现在可以处理任意大数字:
>>> a = 236317407839490590865554550063
>>> b = 127372335361192567404918884983
>>> c = 475864993503739844164597027155993229496457605245403456517677648564321
>>> smallestSolution(a,b,c)
(2013668810262278187384582192404963131387, 120334243940259443613787580180)
>>> x,y = _
>>> a*x+b*y
475864993503739844164597027155993229496457605245403456517677648564321
大多数计算都是在运行扩展的欧几里德算法时进行的,该算法已知为O(min(a,b))
。
答案 1 :(得分:0)
首先让我们假设a,b,c>0
:
a.x+b.y = c
x+y = min(xi+yi)
x,y >= 0
a,b,c > 0
------------------------
x = ( c - b.y )/a
y = ( c - a.x )/b
c - a.x >= 0
c - b.y >= 0
c >= b.y
c >= a.x
x <= c/x
y <= c/b
如此天真的O(n)
解决方案在C ++中是这样的:
void compute0(int &x,int &y,int a,int b,int c) // naive
{
int xx,yy;
xx=-1; yy=-1;
for (y=0;;y++)
{
x = c - b*y;
if (x<0) break; // y out of range stop
if (x%a) continue; // non integer solution
x/=a; // remember minimal solution
if ((xx<0)||(x+y<=xx+yy)) { xx=x; yy=y; }
}
x=xx; y=yy;
}
如果没有找到解决方案,则返回-1,-1
如果您稍微考虑一下这个等式,那么您应该意识到min解决方案将在x
或y
最小时(哪一个取决于a<b
条件)所以添加这样的启发式算法,我们只能增加最小坐标,直到找到第一个解。这将大大加快整个过程:
void compute1(int &x,int &y,int a,int b,int c)
{
if (a<=b){ for (x=0,y=c;y>=0;x++,y-=a) if (y%b==0) { y/=b; return; } }
else { for (y=0,x=c;x>=0;y++,x-=b) if (x%a==0) { x/=a; return; } }
x=-1; y=-1;
}
我在设置上测量了这个:
x y ax+by x+y a=50 b=105 c=500000000
[ 55.910 ms] 10 4761900 500000000 4761910 naive
[ 0.000 ms] 10 4761900 500000000 4761910 opt
x y ax+by x+y a=105 b=50 c=500000000
[ 99.214 ms] 4761900 10 500000000 4761910 naive
[ 0.000 ms] 4761900 10 500000000 4761910 opt
天真方法时间的~2.0x
差异归因于a/b=~2.0
并选择更差的坐标以在第二次运行中进行迭代。
现在只需处理a,b,c
为零时的特殊情况(避免被零除)...