【问题标题】:Finding the greatest prime divisor (the fastest program possible)找到最大的素数除数(可能的最快程序)
【发布时间】:2013-12-14 09:11:14
【问题描述】:

我在http://projecteuler.net/上查看问题

第三个问题如下:

13195 的质因数是 5、7、13 和 29。什么是最大的 数字 600851475143 的素数?

我的解决方案代码如下。但它太慢了,我认为需要数周才能完成。 我该如何改进它?还是 Python 本身解决这个问题的速度太慢了?

def IsPrime(num):
    if num < 2:
        return False
    if num == 2:
        return True
    else:
        for div in range(2,num):
            if num % div == 0:
                return False
        return True

GetInput = int (input ("Enter the number: "))


PrimeDivisors = []

for i in range(GetInput, 1, -1):
    print(i)
    if GetInput % i == 0 and IsPrime(i) is True:
        PrimeDivisors.append(i)
        break
    else:
        continue


print(PrimeDivisors)
print("The greatest prime divisor is:", max(PrimeDivisors))

【问题讨论】:

标签: python python-3.x


【解决方案1】:

您的解决方案的问题在于您没有考虑找到的主要因素,因此您在实际找到最大的因素后不必要地检查因素。这是我的解决方案:

def largest_prime_factor(n):
    largest = None

    for i in range(2, n):
        while n % i == 0:
            largest = i
            n //= i

        if n == 1:
            return largest

    if n > 1:
        return n

Project Euler 问题更多是关于数学而不是编程,所以如果你的解决方案太慢,可能不是你的语言有问题。

请注意,我的解决方案偶然适用于这个特定的数字,所以它绝对不是一个通用的解决方案。更快的解决方案are complicated 在这种特定情况下过度杀伤。

【讨论】:

  • 这个答案中链接到的通用数字字段筛解决方案对于这个问题来说绝对是矫枉过正。它最适合长度至少为 100 位小数的数字。
  • @poke:它适用于任何数字,尤其是大数字。 msieve -v 600851475143 运行速度比我的解决方案快。
  • @Bender:我的评论围绕着这样一个事实,即对于小于 100-130 十进制数字的数字,二次筛比 GNFS 更好。
【解决方案2】:

这可能不是最快的算法,但它非常有效:

def prime(x):
    if x in [0, 1]:
        return False
    for n in xrange(2, int(x ** 0.5 + 1)):
        if x % n == 0:
            return False
    return True

def primes():
    """Prime Number Generator

    Generator an infinite sequence of primes

    http://stackoverflow.com/questions/567222/simple-prime-generator-in-python
    """

    # Maps composites to primes witnessing their compositeness.
    # This is memory efficient, as the sieve is not "run forward"
    # indefinitely, but only as long as required by the current
    # number being tested.
    #
    D = {}  

    # The running integer that's checked for primeness
    q = 2  

    while True:
        if q not in D:
            # q is a new prime.
            # Yield it and mark its first multiple that isn't
            # already marked in previous iterations
            # 
            yield q        
            D[q * q] = [q]
        else:
            # q is composite. D[q] is the list of primes that
            # divide it. Since we've reached q, we no longer
            # need it in the map, but we'll mark the next 
            # multiples of its witnesses to prepare for larger
            # numbers
            # 
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]

        q += 1

def primefactors(x):
    if x in [0, 1]:
        yield x
    elif prime(x):
        yield x
    else:
        for n in primes():
            if x % n == 0:
                yield n
                break
        for factor in primefactors(x // n):
            yield factor

用法:

>>> list(primefactors(100))
[2, 2, 5, 5]

【讨论】:

  • 你没有定义primes。此外,使用for prime in primes 似乎比使用while True 简单得多。
  • 啊,我忘了那一点!等一下:)
  • 您的素数检查有问题。 prime(4)True
  • 事实上,您的代码似乎没有错误(至少,如果您认为 0 和 1 的奇怪输出是可以的),但它做了一些愚蠢的事情。要确定一个合数是合数,它会搜索一个因子。如果它找到一个,那么它... 再次搜索一个因子,实现方式完全不同。它也不使用任何先前确定的因素的知识,从头开始重新开始每次搜索。
  • 是的,我同意改进初筛。也许我稍后会找时间这样做:)
【解决方案3】:

我的代码对我来说似乎足够快。使用 collections.defaultdict() 会使 primes() 的代码更清晰,但我猜代码会因为导入它而降低一些速度。

def primes():
    """Prime number generator."""
    n, skip = 2, {}
    while True:
        primes = skip.get(n)
        if primes:
            for p in primes:
                skip.setdefault(n + p, set()).add(p)
            del skip[n]
        else:
            yield n
            skip[n * n] = {n}
        n += 1

def un_factor(n):
    """Does an unique prime factorization on n.

    Returns an ordered tuple of (prime, prime_powers)."""
    if n == 1:
        return ()
    result = []
    for p in primes():
        (div, mod), power = divmod(n, p), 1
        while mod == 0:
            if div == 1:
                result.append((p, power))
                return tuple(result)
            n = div
            div, mod = divmod(n, p)
            if mod != 0:
                result.append((p, power))
            power += 1

试运行:

>>> un_factor(13195)
((5, 1), (7, 1), (13, 1), (29, 1))
>>> un_factor(600851475143)
((71, 1), (839, 1), (1471, 1), (6857, 1))
>>> un_factor(20)
((2, 2), (5, 1))

编辑:基于 this 配方的 primes() 生成器的小修改。

EDIT2:固定为 20。

EDIT3:用 un_factor() 替换了 best_prime_divisor()。

【讨论】:

  • 例如,您的代码不适用于 best_prime_divisor(20)。
【解决方案4】:
def getLargestFactor(n):
    maxFactor = sqrt(n)
    lastFactor = n
    while n%2 == 0:
        n  /= 2
        lastFactor = 2
    for i in xrange(3,int(maxFactor),2 ):
        if sqrt(n) < i:
             return n
        while n%i == 0 and n > 1:
            n  /= i
            lastFactor = i
    return lastFactor

这应该是相当有效的。将每个因素全部除掉,这样我们就只能找到主要因素。并利用一个事实,即只能有一个大于 sqrt(n) 的数的质因数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    • 2014-11-28
    相关资源
    最近更新 更多