查找将为您提供最大总和的数字子集

时间:2017-11-17 10:34:29

标签: math max primes combinatorics subset-sum

如何找到2到1000之间的数字子集,在子集中任何两个数字不共享公共素数因子(例如,1000和500共享素数因子)的条件下,将为您提供最大总和2)?

上述问题的一个(可能更容易)变化:子集中最大的数字是多少?我们知道997是素数,并且很容易排除1000和998,因此问题变成子集中是否为999?

3 个答案:

答案 0 :(得分:4)

创建一个包含节点{2,...,1000}的图形,以及当节点具有gcd>时的边缘。 1.解决这个问题的方法与查找maximum-weight independent set problem相同,除了某些特殊情况外,它是NP难的。这个图表案例看起来不像维基百科页面上的列表中的空间案例,甚至不是list

<强>更新

正如詹姆斯所说,可以减少该图中的节点数量。假设使用素数分解p1^k1*...*pn^kn的数字N的签名是元组(p1, ..., pn)

首先,当存在具有较大值和相同签名的节点时,删除节点。这将图表减少到607个节点。

如果存在签名分解为N且sum为(p1, ..., pn)的节点,则下一步缩减是删除带有签名(p1, ..., pn)的节点>= N。这将图形缩减为277个节点。

从这些节点73是孤立的节点(素数> 500。)

答案 1 :(得分:2)

我不知道答案,这对我来说似乎并非无足轻重。这里有一些想法。

总和由可变数量的加数组成,比如s 0 + s 1 + ... s k ,其中s < sub> i 是区间[2,1000]中的整数。现在每个s i 都有一个素数因子分解s i =(p 1 e1 )*(p < sub> 2 e2 )...其中e i ≥1。

来自该子集的任何两个数字不共享公共素因子的条件&#34;相当于声明s i 是成对相对素数,即对于i≠j,gcd(s i ,s j )= 1。同样地,只要一个summand s i 包含一个素数p,这意味着没有其他的summand可能包含该素数。

那么如何将素数排列成加数?一个简单的规则是显而易见的。 [500,1000]中的所有素数只能作为单独的加数出现在总和中。如果它们乘以其他任何东西,即使是最小的素数2,产品也会太大。这样就完成了安排较小素数的任务。而且我不知道他们最好的方法。为了完整起见,我将提供以下显示单向的短python程序。

def sieve_prime_set(n):
    # sieve[i] = set(p1, p2, ...pn) for each prime p_i that divides i.

    sieve = [set() for i in range(n + 1)]
    primes = []
    next_prime = 1
    while True:
        # find the next prime
        for i in range(next_prime + 1, len(sieve)):
            if not sieve[i]:
                next_prime = i
                break
        else:
            break

        primes.append(next_prime)
        # sieve out by this prime
        for kp in range(next_prime, n + 1, next_prime):
            sieve[kp].add(next_prime)

    return sieve, primes

def max_sum_strategy1(sieve):
    last = len(sieve) - 1
    summands = [last]
    max_sum = last
    prime_set = sieve[last]
    while last >= 2:
        last -= 1
        if not sieve[last] & prime_set:
            max_sum += last
            prime_set |= sieve[last]
            summands.append(last)

    return max_sum, summands, prime_set

def max_sum_strategy2(primes, n):
    return sum(p ** int(log(n, p)) for p in primes)



if __name__ == '__main__':
    sieve, primes = sieve_prime_set(1000)
    max_sum, _, _ = max_sum_strategy1(sieve)
    print(max_sum)
    print(max_sum_strategy2(primes, 1000))

输出

84972
81447

显示&#34;策略1&#34;是优越的。

优越,但不一定是最佳的。例如,包括1000似乎很好,但它迫使我们排除每个其他甚至summand和每个可被5整除的summand。如果我们留下1000但是包括998,我们可以使用另一个包含5的其他summand&#39; s素数分解。但包括998强迫其他命令被排除在外。因此,最大化总和并非易事。

答案 2 :(得分:1)

这是解决问题的 Python 脚本。在我的笔记本电脑上运行需要几分钟时间。

import math
import networkx as nx

max_number = 1000

G = nx.Graph()

for i in range(2, max_number + 1):
    G.add_node(i, weight=i)

for i in range(2, max_number + 1):
    for j in range(i+1, max_number + 1):
        if math.gcd(i, j) == 1:
            G.add_edge(i, j)

numbers, sum_of_numbers = nx.max_weight_clique(G)

print(sorted(numbers))
print(sum_of_numbers)

返回的数字列表如下所示;总数是 85684。(可能还有其他有效的数字集可以给出这个总数。)

[41, 59, 67, 71, 79, 83, 97, 101, 103, 107, 113, 127, 131, 137, 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, 841, 853, 857, 859, 863, 877, 881, 883, 887, 893, 901, 907, 911, 919, 925, 929, 937, 941, 947, 949, 953, 961, 967, 971, 973, 976, 977, 979, 981, 983, 989, 991, 997]