【问题标题】:Fastest Integer Search for Any Arbitrary X and Y任意 X 和 Y 的最快整数搜索
【发布时间】:2020-10-19 15:38:33
【问题描述】:

随着离散数学的应用,在python中解决这个问题最快的算法是什么:

使用等式 ax + by = d,其中 a, bd 是用户输入,在 |L 范围内搜索 x 和 y 的整数值对, R| (包括L和R)满足方程。

L 和 R 也是用户输入。

按升序输出 x 和 y 的所有可能值。如果没有可能的对,则打印 none

案例一:

a = 1
b = 5
d = 40
L = 0
R = 10

Result:
0 8
5 7
10 6

案例 2:

a = 14
b = 91
d = 53
L = 1
R = 100

Result:
none

这是我的代码,但我相信有一种 更快的搜索方式。这太低效了。

a = int(input())
b = int(input())
d = int(input())
L = int(input())
R = int(input())

isNone = True
for x in range(L, R+1):
    for y in range(L, R+1):
        if (a*x) + (b*y) == d:
            print(x, y)
            isNone = False
if isNone: print("none")

有没有O(1)的算法?最快的方法是什么?

【问题讨论】:

  • 您所参加的课程是否附带离散数学课本?它是否说明了这一点(可能称为“线性丢番图方程”)?
  • 嗨,没听说过。
  • 无需遍历xy:给定其中一个值,您可以轻松计算出另一个值。如果这是一个整数,并且在给定的范围内,那么您已经找到了解决方案。这将是 O(R-L) - 对于可以具有任意数量解决方案的问题,O(1) 算法是不可能的。
  • @jasonharper 它可以在 O(gcd(a,b)) 的复杂性中解决,假设所有数字都是有界的,这是 O(1)。那是linear diophantine equation
  • @freakish 对不起,我是编程新手,我主要是蛮力解决这些类型的问题。获取 gcd 如何找到所有以 L、R 为界的对?

标签: python algorithm search discrete-mathematics


【解决方案1】:

我想你想申请的身份如下(直接来自维基百科,虽然类似的措辞应该在大多数离散的数学文本中找到,或者可以自己证明):

最简单的线性丢番图方程采用 ax + by = c 的形式,其中 a、b 和 c 是整数。解决方案由以下定理描述:

这个丢番图方程有一个解(其中 x 和 y 是整数)当且仅当 c 是 a 和 b 的最大公约数的倍数。此外,如果 (x, y) 是一个解,则其他解的形式为 (x + kv, y − ku),其中 k 是任意整数,u 和 v 分别是 a 和 b 的商通过 a 和 b 的最大公约数。

这几乎立即回答了这个问题,特别是因为它的标准证明使用了一种称为欧几里得算法的东西。为了简单起见,我们将执行以下操作:

  1. 使用欧几里得算法正向查找g = gcd(a, b)
  2. 通过欧几里得算法反求解找到_x, _y,使得_x*a + _y*b == g
  3. 如果d 不是g 的倍数,则不可能有任何解决方案,因此请尽早退出。
  4. 否则,x, y = _x*(d//g), _y*(d//g) 是一个可能的解决方案。使用它来查找所需范围内的所有解决方案。
def gcd(a, b):
    # forward euclidean algorithm
    q, r, x, qs = None, b, a, []
    while x%r:
        (q, r), x = divmod(x, r), r
        qs.append(q)

    # save the gcd for later
    g = r

    # backsolve euclidean algorithm
    if not qs:
        return 1, 1-a//b, g
    theta, omega = 1, -qs[-1]
    for q in reversed(qs[:-1]):
        theta, omega = omega, theta - q * omega
    
    # theta * a + omega * b == g
    # g might be negative, but we don't care about a canonical solution
    return theta, omega, g

def idivide_zero(a, b):
    # integer division a/b, round toward 0 instead of round down
    q = a // b
    if q < 0 and b*q != a:
        q += 1
    return q

def bounded_solutions(a, b, d, L, R):
    _x, _y, g = gcd(a, b)
    if d%g:
        return

    # a*x + b*y == d
    x, y = _x*(d//g), _y*(d//g)

    # solutions are of the form (x+k*v, y-k*u)
    u, v = a//g, b//g

    # The next trick is to find all solutions in [L, R].
    # Basically, we need L <= x+k*v <= R and L <= y-k*u <= R.
    # Note that valid choices of k exist in a contiguous interval, so
    # we only have to find the lower and upper bounds to be able to
    # quickly enumerate all options.
    xb = sorted(idivide_zero(b-x, v) for b in (L, R))
    yb = sorted(idivide_zero(y-b, u) for b in (L, R))
    m, M = min(xb[0], yb[0]), max(xb[1], yb[1])
    for k in range(m, M+1):
        yield x+k*v, y-k*u

a = int(input())
b = int(input())
d = int(input())
L = int(input())
R = int(input())

empty = True
for x, y in bounded_solutions(a, b, d, L, R):
    print(x, y)
    empty = False
if empty:
    print('none')

代码未经测试。精神上是对的,但可能还有一些调试问题。

【讨论】:

  • 到目前为止一切顺利,但我仍在测试其他案例。同时,我正在努力理解你的每一点代码,所以谢谢你的 cmets。
  • 听起来不错。如果有什么不清楚的地方,请指出,以便我更新答案。我还强烈建议您手动编写几个欧几里得算法示例。 wolfram alpha 有一个不错的介绍。例如,如果我们想要 15084 的 gcd,我们将逐步除以并取余数:150 % 84 == 6684 % 66 == 1866 % 18 == 1218 % 12 == 612 % 6 == 0,得到序列 @ 987654336@。最后一个非零元素始终是 gcd。您可以通过...的系统进行代数反求解...
  • ...您正在处理的方程式以将 gcd 设置为等于某个表达式,从前面的方程式中替换,依此类推,直到您获得 gcd == _x*a + _y*b 形式的系数。这个过程正是我们的代码对thetaomega 所做的(从一个超级小例子开始)。从那里开始,因为如果有一个解,它必须是 gcd 的倍数,我们可以将这些系数乘以一个合适的常数来得到一个解,离散数学定理描述了其余的解。艰苦的工作是找到第一个。
  • 尝试了很多测试用例,完美无缺!感谢您的伟大贡献,好先生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多