求出一个小于1000的给定整数所需的素数最少

时间:2019-06-18 18:05:17

标签: algorithm dynamic-programming greedy

这是我最近遇到的编程挑战。

  

您得到的数字小于1000,您需要确定求和到给定数字的素数最少为多少。

     

示例:

12: 2 (since 12=7+5)
14: 2  (since 14 = 7+7)
     

如果不可能将给定的数字分解为素数之和,则返回-1。

     

以下是一些测试用例:

88:2
117:3
374:2
363:3
11:1

2 个答案:

答案 0 :(得分:7)

简而言之:一个数字的最大质数为3。由于只有168个质数小于1,000,我们可以穷尽地使用两个质数的组合,或者默认使用3。通过使用一些额外的属性,我们可以轻松地找到最小数量的元素,甚至构造一个集合这些数字中。

如果我们假设可以访问多达1000个素数的列表,则可以解决问题,其中有168个。

鉴于该数字是质数,那么答案显然是 1

对于非素数,我们将不得不找到其他方法来解决问题。

Goldbach's conjecture [wiki]指出

  

甚至大于2的整数也可以表示为两个素数之和。

这个猜想没有得到一般的证明,但至少知道对所有4×10 18 的数字都成立。

因此,这意味着对于n = 2,答案是1;对于偶数 n > 2,答案是2(因为只有一个偶数素数。)

如果数字是奇数且不是素数,我们知道素数的最大数目为3。确实,由于如果从该数字中减去3,我们得到的偶数可以由2或三个元素组成。显然,这称为Goldbach's marginal conjecture [wiki]

  

每个大于5的整数都可以写成三个素数的总和。

提高上限的唯一方法是找到两个等于给定数字的素数。因此,这需要迭代所有质数(最多1'000),并检查 n-p 是否也为质数。但是,正如@ AlexanderZhang所说,我们可以减去2,因为这是唯一一个产生奇数的偶数,因此很可能是质数。

总而言之,基本上有以下几种情况:

  1. 对于 n <2 ,没有质数,因此显然失败;
  2. 对于 n 一个质数,答案当然是一个,因为我们可以简单地使用该数字;
  3. 对于大于2的偶数,我们可以使用哥德巴赫猜想,然后返回2,我们知道这是最小的,因为除了2之外,还有甚至没有素数;
  4. 对于大于2的奇数,我们知道如果 n-2 是质数,则数字是2,因为2是质数,而n-2是素数,我们知道没有更好的解决方案,因为 n 不是素数;最后
  5. 对于一个 n-2 不是素数的奇数,我们知道 n-3 ,根据哥德巴赫猜想,可以构造三个质数之和。我们知道这是最佳的,因为除2外没有其他质数,所以减法是偶数,因此我们可以再次使用哥德巴赫猜想。

因此,我们可以实现如下算法:

primes1000 = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997}

def min_prime(n):
    if n < 2:
        return -1
    if n in primes1000:
        return 1
    # 2 and 3 are prime numbers prime number
    # so all values here are > 3
    if n % 2 == 0:
        return 2    # Goldbach's conjecture, so 2
    if n-2 in primes1000:
        return 2
    return 3  # fallback on 3

例如:

>>> min_prime(12)
2
>>> min_prime(14)
2
>>> min_prime(88)
2
>>> min_prime(117)
3
>>> min_prime(374)
2
>>> min_prime(363)
3
>>> min_prime(11)
1

生成素数

我们可以使用相同的方法生成素数,例如:

def find_sum2(n):
    for p in primes1000:
        if n-p in primes1000:
            return (p, n-p)

def min_prime_tuple(n):
    if n < 2:
        return None
    if n in primes1000:
        return (n,)
    if n % 2 == 0:
        return find_sum2(n)
    if n-2 in primes1000:
        return (2, n-2)
    return (3, *find_sum2(n-3))

例如:

>>> min_prime_tuple(12)
(5, 7)
>>> min_prime_tuple(14)
(3, 11)
>>> min_prime_tuple(88)
(5, 83)
>>> min_prime_tuple(117)
(3, 5, 109)
>>> min_prime_tuple(374)
(7, 367)
>>> min_prime_tuple(363)
(3, 7, 353)
>>> min_prime_tuple(11)
(11,)

我们可以通过从迭代器大于n的那一刻起切断线性搜索来提高效率,但这通常不会产生太大的区别,因为质数的数量小于1000很小。

性能

由于n的上限为1'000,所以没有大哦。此外,如果n是无界的,我们不知道这个猜想是否仍然成立。

如果我们假设猜想成立,那么在 O(g×c)中生成元组,并使用 g 生成直到 n 和 c 检查数字是否为质数的时间。

如果我们对上述不是很有效地在Python中实现的方法进行基准测试,我们将实现以下基准测试:

>>> timeit(lambda: list(map(min_prime_tuple, range(0,1000))), number=10_000)
4.081021320000218

因此,这意味着如果我们以10,000次为不超过1,000的所有数字构造元组,则将在 Intel(R)Core(TM)i7-7500U CPU @ 2.70上的4.08秒内完成GHz 。因此,这意味着我们可以在408.1μs内检查整个范围,或者在0.408μs内检查一个随机数。

答案 1 :(得分:5)

这只是经典knapsack problem的变体。

在最初的背包问题和本背包问题中,我们都有一组项目可供选择。每个项目都有我们正在优化的成本/价值,并且它的大小受到我们的限制。在最初的背包问题中,我们希望最大化利润,同时将权重保持在设定的最大值以下。在这里,我们希望最小化素数,而总和恰好是我们给定的数。

我们可以更改DP数组的定义,使得DP[i][j]是仅使用第一个j素数或无穷大(如果不是)就可以求和到精确的i的最小素数仅使用第一个j素数就不能求和i,并且我们的递归关系变为DP[i][j] = min(DP[i - 1][j], DP[i][j - p[i]] + 1),其中p[i]是第i个素数。然后,可以通过计算DP[numPrimes][N]表中的所有值或使用类似于原始背包问题的记忆来计算DP

如Willem Van Onsem所指出的,此问题是一种特殊情况,其中每个小于4 * 10 ^ 18的偶数都可以表示为两个质数之和,从而可以更快地解决问题,其复杂度与用于测试素数的算法。