可被这些质数之和整除的三个质数的乘积

时间:2019-07-07 08:25:36

标签: primes sieve-of-eratosthenes

我在一次结束的cp竞赛中发现了这个问题,因此可以解决。 如果(p1 + p2 + p3)除p1 * p2 * p3,则三个质数(p1,p2,p3)(不一定区分)称为特殊。如果素数不能超过10 ^ 6,我们必须找到这些特殊对的数量

我尝试了蛮力方法,但是超时了。还有其他方法吗?

2 个答案:

答案 0 :(得分:1)

如果超时,则需要进行一些智能搜索来替换暴力破解。一百万以下还不到80,000素数,所以您超时就不足为奇了。

因此,您需要开始仔细看。

例如,其中p + 2也是素数的任何三元组(2,p,p + 2)都将满足以下条件:

  • 2 + 3 + 5 = 10; 2 * 3 * 5 = 30; 30/10 = 3。
  • 2 + 5 + 7 = 14; 2 * 5 * 7 =70。70/ 14 = 5。
  • ...
  • 2 + p + p + 2 = 2(p + 2); 2 * p *(p + 2)= 2p(p + 2); 2p(p + 2)/ 2(p + 2)= p。
  • ...

还有其他以2开头的三元组吗?是否有以3开头的三元组?如果p1 = 3,p2和p3采取什么形式?将程序运行三倍(最多500个左右),然后在结果中查找模式。然后将这些结果外推到10 ^ 6。

我假定您正在使用筛子生成素数的初始列表。

答案 1 :(得分:1)

自您发布以来,我已经尝试过此问题。我还没有解决问题,但是想在介绍其他内容之前先传递我的见解:

生成素数是不是问题

使用适当的筛分算法,我们可以在几分之一秒内生成10 ** 6以下的所有素数。 (在我的Mac mini上不到1/3秒。)超出此时间优化素数生成的时间浪费了时间。

蛮力法

如果我们尝试在Python中生成三个素数的所有置换,例如:

for prime_1 in primes:
    for prime_2 in primes:
        if prime_2 < prime_1:
            continue
        for prime_3 in primes:
            if prime_3 < prime_2:
                continue
            pass

或者更好的是,通过Python的itertools将问题降低到 C 级别:

from itertools import combinations_with_replacement

for prime_1, prime_2, prime_3 in combinations_with_replacement(primes, 3):
    pass

然后,我们的计时,除了生成素数三元组外,不做任何实际工作,就像这样:

         sec.
10**2    0.04
10**3    0.13
10**4   37.37
10**5     ?

您可以看到每个数量级增加多少时间。这是我的蛮力解决方案示例:

from itertools import combinations_with_replacement

def sieve_primes(n):  # assumes n > 1
    sieve = [False, False, True] + [True, False] * ((n - 1) // 2)
    p = 3

    while p * p <= n:
        if sieve[p]:
            for i in range(p * p, n + 1, p):
                sieve[i] = False
        p += 2

    return [number for number, isPrime in enumerate(sieve) if isPrime]

primes = sieve_primes(10 ** 3)
print("Finished Sieve:", len(primes), "primes")

special = 0

for prime_1, prime_2, prime_3 in combinations_with_replacement(primes, 3):
    if (prime_1 * prime_2 * prime_3) % (prime_1 + prime_2 + prime_3) == 0:
        special += 1

print(special)

避免产生三元组,但仍然蛮力

这是一种避免生成三元组的方法。我们采用生成的最小和最大素数,将它们求立方,并使用 custom 分解函数遍历它们。此自定义因子功能仅返回由恰好三个素数组成的数字的值。对于由或多或少组成的任何数字,它将返回None。这应该比正常分解更快,因为该函数可以尽早放弃。

容易精确检验构成三个素数的数字的特殊性。我们将假装我们的自定义因子分解功能完全不需要时间,只需测量一下我们花多长时间遍历所有有问题的数字即可:

smallest_factor, largest_factor = primes[0], primes[-1]

for number in range(smallest_factor**3, largest_factor**3):
    pass

再次,一些时间安排:

           sec.
10**2      0.14
10**3    122.39
10**4       ?

看起来并不乐观。实际上,这比我们最初的蛮力方法还差。实际上,我们的自定义分解功能会增加很多的时间。这是我关于此解决方案的示例(从上一示例中复制sieve_primes()

def factor_number(n, count):
    size = 0
    factors = []

    for prime in primes:
        while size < count and n % prime == 0:
            factors.append(prime)
            n //= prime
            size += 1

        if n == 1 or size == count:
            break

    if n > 1 or size < count:
        return None

    return factors

primes = sieve_primes(10 ** 3)
print("Finished Sieve:", len(primes), "primes")

special = 0

smallest_factor, largest_factor = primes[0], primes[-1]

for number in range(smallest_factor**3, largest_factor**3):
    factors = factor_number(number, 3)

    if factors:
        if number % sum(factors) == 0:
            special += 1

print(special)