Algorithm 求和小于1000的给定整数所需的最小素数

Algorithm 求和小于1000的给定整数所需的最小素数,algorithm,dynamic-programming,greedy,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 这只是经典的一个变体 在原始背包问题和这个问题中,我们都有一组可以选择的物品。每个项目都有我们优化的成本/价值,并且它的大小受到我们的限制。在原始背包问题中,我们希望在保持权

这是我最近面临的编程挑战

如果给定的数字小于1000,则需要确定与给定数字求和所需的素数最少的数目

示例:

12: 2 (since 12=7+5)
14: 2  (since 14 = 7+7)
如果无法将给定的数拆分为素数之和,则返回-1

以下是几个测试用例:

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

这只是经典的一个变体

在原始背包问题和这个问题中,我们都有一组可以选择的物品。每个项目都有我们优化的成本/价值,并且它的大小受到我们的限制。在原始背包问题中,我们希望在保持权重低于设定的最大值的情况下实现利润最大化。这里,我们想最小化素数的个数,而和正是我们给定的数

我们可以更改DP数组的定义,使得
DP[i][j]
是只使用第一个
i
素数求和到
j
所需的最小素数,或者如果不可能只使用第一个
i
素数求和到
j
无穷大,则我们的递归关系变为
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的偶数都可以表示为两个素数的和,这使得求解速度更快,复杂性与测试素数的算法相同。

简言之:一个数的最大素数是
3
。由于只有168个小于1'000的素数,我们可以完全依赖于两个素数的组合,或者默认为3。通过使用一些额外的属性,我们可以很容易地找到元素的最小数目,甚至可以构造这些数的集合

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

假设这个数是素数,那么答案显然是1

对于非素数,我们必须找到不同的方法来解决这个问题

声明

每一个大于2的偶数整数都可以表示为两个素数的和

一般来说,这个猜想并没有得到证实,但至少我们知道它适用于4×1018以下的所有数字

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

如果这个数是奇数且非素数,我们知道素数的最大数目是
3
。事实上,如果我们从这个数中减去
3
,我们就得到了一个偶数,它可以由
2
或三个元素组成。显然,这被称为:

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

我们唯一能改进上界的方法是找到两个素数,它们的总和等于给定的数。因此,这需要迭代所有素数(最多1'000),并检查n-p是否也是素数。但是,正如所说的,我们可以减去
2
,因为这是唯一会产生奇数的偶数,因此是素数的候选者

综上所述,基本上有以下几种情况:

  • 当n<2时,没有素数,这显然是失败的
  • 对于素数n,答案当然是1,因为我们可以简单地使用这个数
  • 对于大于2的偶数,我们可以使用哥德巴赫猜想,从而返回
    2
    ,我们知道这是最小的,因为除了
    2
    ,没有偶数素数
  • 对于大于2的奇数,我们知道如果n-2是素数,那么这个数就是2,因为
    2
    是素数,
    n-2
    是素数,我们知道没有更好的解,因为n不是素数;最后
  • 对于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
    
    生成素数 我们可以使用相同的方法生成素数,如:

    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))
    
    从迭代器大于
    n
    的那一刻起,我们就可以通过切断线性搜索来提高上述效率,但这通常不会产生太大的差异,因为小于1000的素数的数量非常小

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

    如果我们假设这个猜想成立,那么元组的生成是在O(g×c)中完成的,g是生成所有素数直到n的时间,c是检查一个数字是否是素数的时间

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

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

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

    很抱歉格式不正确,但现在我没有我的计算机,所以这是我能做的事情Atmost什么不适用于您的方法?这只是经典的一个版本,用于
    >>> 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,)
    
    >>> timeit(lambda: list(map(min_prime_tuple, range(0,1000))), number=10_000)
    4.081021320000218