【问题标题】:Speed up a search for Carmichael numbers?加快搜索 Carmichael 数字?
【发布时间】:2019-01-20 10:28:44
【问题描述】:

这里的主要问题是:有没有更好的方法在 Python 中使用“all”? 我读过它经历了每一种情况,然后回来看看是否有任何情况。我想一次检查一个条件,然后在一个不工作时立即退出。我相信这被称为“短路......”。我不能使用一系列“和”,因为它们会随着输入而改变,并且可能有数百万个条件。

我正在寻找Carmichael numbers。卡迈克尔数的一个定义是对于所有 1

我使用了代码:

def Carm(num):
    if all(gcd(k,num) == 1 for k in range(3,int(round(num**0.5)),2)) and gcd(2,num) == 1:
        print(num,'is a Prime Number')
    elif all(pow(b,num,num)==b for b in range(1,num)) and gcd(num,2)==1:
        print(num,'is a Carmichael Number')
    else:
        print(num,'is not a Carmichael Number')

【问题讨论】:

  • 首先,我会颠倒您的条件顺序。 and 短路:如果第一个条件是False,则不检查第二个。如果您的一个条件比另一个慢得多,它应该排在最后。
  • 另外,你能把gcd()的代码贴出来吗?
  • 非常感谢您的回复!我应该交换哪些条件?你能建议最好的顺序吗? gcd 是从“分数”导入的:from fractions import gcd
  • 你为什么要通过完整的 GCD 计算来检查可分性?

标签: python python-3.x primes


【解决方案1】:

回答原问题

我能得到的最好结果是通过重新安排您的条件(加速取决于您输入的数字是否通过第一个条件)并使用math.gcd() 代替fractions.gcd(),将速度提高了大约 4.5 倍。

我还让它们返回一个字符串而不是打印一个。这应该更快,但主要是我这样做是因为它更容易测试时序。然后你可以对字符串做任何你想做的事情:

def carm_math_reorder(num):
    if math.gcd(2,num) == 1 and all(math.gcd(k,num) == 1 for k in
                                    range(3, int(round(num**0.5)), 2)):
        return 'Prime'
    elif math.gcd(num, 2) == 1 and all(pow(b, num, num)==b for b in
                                       range(1, num)):
        return 'Carmichael'
    return 'Composite'  # implicit "else" return

% timeit [Carm(num) for num in range(1000)]  # your implementation
16.9 ms ± 89.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

% timeit [carm_math_reorder(num) for num in range(1000)]
3.71 ms ± 45.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

重新排列条件是可行的,因为and 短路(实际上all 也是如此),所以如果第一个条件没有通过,Python 将忽略第二个条件并继续前进。在您的情况下 Carmichael 测试,检查单个 gcd() 的条件比检查 pow(b, n, n) == 1 的条件快,至少在我的测试中。因此gcd() 测试应该首先进行。对于测试数字是否为素数的行可能无关紧要。在我的测试中,这种变化导致了大​​约 2 倍的加速。

我收到fractions.gcd() 已弃用的警告,所以我改用math.gcd(),这样启动速度更快!这让我看到了其余的加速。

当然,如果您需要一次检查 很多 个数字,最好使用 numpy 并将其矢量化。让我知道这是否是您的用例,我会看看是否可以为此想出一个不错的优化版本。

修正卡迈克尔测试

我很确定您对 Carmichael 号码的检查已取消。如果pow(b, n) % n == 1 对于所有整数 b,则数字是 Carmichael,而不是对于所有 b < n。你不能这样检查,所以你需要一个不同的规则。

如果pow(b, n-1) % n == 1 用于所有b = 1, 2, ..., n 使得bn 是互质数,您似乎可以使用一个数字是Carmichael 的事实。 Here is a good example,以及上述示例中的代码(或多或少):

from math import gcd
def is_carm(n):
    """ Check whether n is a Carmichael number """
    for b in range(2, n):
        # If b is relatively prime to n
        if gcd(b, n) == 1:
            # If pow(b, n-1) % n is not 1, not Carmichael
            if pow(b, n-1, n) != 1:
                return False
    return True  # Carmichael!

这比上面的选项慢,但我相信它在技术上是正确的:最好的正确。

【讨论】:

  • 这很好用,谢谢!!!另一种定义是 pow(b,n-1,n)=1 对于所有 b
  • 我认为您的第一次检查不正确。至少according to Wikipediapow(b, n, n) = 1 用于所有整数 b,而不是所有整数b < n!这是不可能签入代码的。我认为您必须使用Korslet's criterion 进行检查,但我什至不知道p-1 | n-1 是什么意思...
  • 我刚刚用我认为更正确的东西更新了答案。我很确定原来的支票有缺陷。
【解决方案2】:

all 已经短路了。无论来源使您认为它不是,要么该来源是错误的,要么您误解了它。我们不知道。

不过,加快速度仍然很容易。例如,您的gcd 调用正在测试num 是否与所有这些数字互质,但这是昂贵且不必要的。我们只需要检查可分性,这可以通过更快的num % k != 0 来完成。此外,被 2 整除比其他任何检查都排除了更多的数字,因此将其放在首位将节省一些工作。另外,gcd(num,2)==1 签入elif 也是多余的。

def is_carmichael(n):
    # Requires a positive integer.
    if n == 1:
        return '1'
    elif n % 2 != 0 and all(n % k != 0 for k in range(3, math.ceil(n**0.5), 2)):
        return 'prime'
    elif all(pow(k, n, n) == k for k in range(1, n)):
        return 'carmichael'
    else:
        return 'non-carmichael composite'

【讨论】:

    猜你喜欢
    • 2019-06-02
    • 1970-01-01
    • 2023-01-05
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-25
    • 1970-01-01
    相关资源
    最近更新 更多