提高 Brocard 问题的复杂性?

Improving the complexity of Brocard's problem?

Brocard 的问题是n! + 1 = m^2。这个问题的解决方案是称为布朗数 (4,5) 等的整数对,其中只有三个是已知的。

Brocard 问题的非常直接的实现:

import math 

def brocard(n,m):
    if math.factorial(n)+1 == m**2:
        return (n,m)
    else:
        return

a=10000

for n in range(a):
    for m in range(a):
        b=brocard(n,m)
        if b is not None:
            print(b)

这个的时间复杂度应该是 O(n^2) 因为嵌套的 for 循环具有不同的变量和任何 math.factorial 算法的复杂性(显然 divide-and-conquer)。有什么方法可以改进 O(n^2)?

还有其他关于 SO 的解释,例如 。与我的实现相比,这个的时间复杂度如何?

你的算法是O(n^3)

您有两个嵌套循环,并且在内部使用 factorial(),本身具有 O(n) 复杂性。

您的算法测试了所有 (n,m) 组合,即使是 factorial(n)m^2 相距很远的组合,例如n=1m=10000.

您总是在循环内部重新计算 factorial(n),尽管它独立于内部循环变量 m。因此,它可以移到内部循环之外。

而且,您可以逐步进行计算,而不是总是从头开始计算 factorial(n)。每当您将 n 递增 1 时,您可以将前一个阶乘乘以 n

另一种更好的方法是不使用嵌套循环,而是始终将 nm 保持在一个数字范围内,以便 factorial(n) 接近 m^2,以避免检查相差很大的数字对。我们可以通过决定接下来要递增哪个变量来做到这一点。如果阶乘较小,则下一个 brocard 对需要更大的 n。如果正方形较小,我们需要更大的 m.

在伪代码中,那将是

n = 1; m = 1; factorial = 1;
while n < 10000 and m < 10000
    if factorial + 1 == m^2
        found a brocard pair
        // the next brocard pair will have different n and m,
        // so we can increment both
        n = n + 1
        factorial = factorial * n
        m = m + 1
    else if factorial + 1 < m^2
        // n is too small for the current m
        n = n + 1
        factorial = factorial * n
    else
        // m is too small for the given n
        m = m + 1

在每次循环迭代中,我们要么递增n,要么递增m,因此我们最多可以进行 20000 次迭代。算法中没有内循环。我们有 O(n)。因此,这对于 nm 到数百万范围应该足够快。

P.S。还有一些可能的优化。

阶乘(在 n=1 之后,已知没有 brocard 对)总是偶数,所以 m^2 必须是奇数才能满足 brocard 条件,这意味着我们总是可以将 m 递增 2,跳过偶数中间的数字。

对于较大的 n 值,阶乘比平方增加得快得多。因此,不是递增 m 直到其平方达到 factorial+1 值,我们可以重新计算下一个合理的 m 作为 factorial+1.

的整数平方根

或者,使用平方根方法,只计算 factorial(n) 的整数平方根,并检查它是否匹配,而不对 m 进行任何增量步骤。