求解线性方程组

时间:2017-02-11 10:17:01

标签: algorithm numbers

我必须找出方程ax+by=c的整数解,使x>=0y>=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)

有没有更好的方法呢?有更好的时间复杂性。

2 个答案:

答案 0 :(得分:5)

使用Extended Euclidean Algorithmlinear 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/dv = b/d。由于x+y是线性的,因此它没有关键点,因此当x尽可能小或y尽可能小时,在第一象限中最小化。上面的k是一个任意整数参数。通过适当使用floorceiling,您可以找到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解决方案将在xy最小时(哪一个取决于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为零时的特殊情况(避免被零除)...