在不使用任何数学技巧的情况下(不确定是否存在......作为一名数学家,您会认为我会在这里有更多的见解),您将不得不迭代所有可能的求和。因此,您肯定需要遍历每个可能的素数,所以我建议第一步是找到最多 10^5 的所有素数。一个基本的(Eratosthenes 的筛子)[https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes] 可能就足够了,尽管现在有更快的筛子。我知道您的问题与语言无关,但您可以将以下内容视为此类筛子的矢量化伪代码。
import numpy as np
def sieve(n):
index = np.ones(n+1, dtype=bool)
index[:2] = False
for i in range(2, int(np.sqrt(n))):
if index[i]:
index[i**2::i] = False
return np.where(index)[0]
还有一些其他简单的优化,但为简单起见,假设我们有一个数组index,其中索引与数字是否为素数完全对应。我们从每个数都是素数开始,将 0 和 1 标记为非素数,然后对于每个素数,我们将它的每个倍数标记为非素数。最后的np.where() 只返回我们的索引对应于True 的索引。
从那里,我们可以考虑使用递归算法来实际解决您的问题。请注意,您可能需要大量不同的素数。数字 26 是 4 个不同的素数之和。它也是 3 和 23 的总和。由于检查 4 个素数比检查 2 个素数更昂贵,我认为从检查可能的最小数开始是合理的。
在这种情况下,我们要做的方式是定义一个辅助函数来确定一个数字是否恰好是 k 个素数的和,然后依次测试该辅助函数的 k 从 1 到任何可能的最大值加数是。
primes = sieve(10**5)
def sum_of_k_primes(x, k, excludes=()):
if k == 1:
if x not in excludes and x in primes:
return (x,)+excludes
else:
return ()
for p in (p for p in primes if p not in excludes):
if x-p < 2:
break
temp = sum_of_k_primes(x-p, k-1, (p,)+excludes)
if temp:
return temp
return ()
通过这个,首先我们检查k 为1 的情况(这是我们递归的基本情况)。这与询问x 是否为素数且不在我们已经找到的素数之一(元组excludes,因为您需要唯一性)相同。如果k 至少为 2,则执行其余代码。我们检查所有我们可能关心的素数,如果我们得到一个不可能的结果(我们列表中没有素数小于 2)就提前停止。我们为较小的 k 递归调用相同的函数,如果成功,我们会将结果向上传播到调用堆栈。
请注意,我们实际上返回了可能的最小唯一素数加数元组。如果您希望您的答案是指定的"NO",则此字段为空,否则它可以让您轻松解释您为什么回答"YES"。
partial = np.cumsum(primes)
def max_primes(x):
return np.argmax(partial > x)
def sum_of_primes(x):
for k in range(1, max_primes(x)+1):
temp = sum_of_k_primes(x, k)
if temp:
return temp
return ()
对于其余的代码,我们将所有素数的部分和存储到给定点(例如,对于素数 2、3、5,部分和将为 2、5、10)。这为我们提供了一种简单的方法来检查最大可能的加数是多少。该函数只是顺序检查x 是否为素数,是否为 2 个素数、3 个素数等的总和......
作为一些示例输出,我们有
>>> sum_of_primes(1001)
(991, 7, 3)
>>> sum_of_primes(26)
(23, 3)
>>> sum_of_primes(27)
(19, 5, 3)
>>> sum_of_primes(6)
()
乍一看,我认为缓存一些中间值可能会有所帮助,但我不相信辅助函数会使用相同的参数调用两次。可能有一种方法可以使用动态编程来做大致相同的事情,但在一个计算量最少的表中,以防止递归的任何重复工作。我不得不考虑更多。
至于您的老师期望的确切输出以及需要编码的语言,这将取决于您。希望这对算法方面有所帮助。