Reputation: 1893
How to find a subset of numbers from 2 to 1000 that will give you the max sum under the condition that any two numbers from the subset don't share common prime factors (e.g., 1000 and 500 share the prime factor 2)?
One (maybe easier) variation to the above question: what is the largest number in the subset? We know that 997 is a prime number, and it is easy to rule out 1000 and 998, thus the question becomes whether is 999 in the subset?
Upvotes: 8
Views: 1245
Reputation: 1866
Here's a Python script to solve the problem. It takes a couple of minutes to run on my laptop.
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)
The list of numbers returned is shown below; the total is 85684. (There may be other valid sets of numbers that give this total.)
[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]
Upvotes: 2
Reputation: 5458
Create a graph with nodes {2, ..., 1000}, and edges when nodes have gcd > 1. Solution to this problem is same as finding maximum-weight independent set problem, which is NP-hard except in some special cases. This graph case doesn't look like a spacial case from list on Wikipedia page or even on this list.
Update
As James suggested it is possible to reduce number of nodes in this graph. Lets say that signature of a number N with prime decomposition p1^k1*...*pn^kn
is a tuple (p1, ..., pn)
.
First reduction is to remove nodes when there is nodes with larger value and same signature. That reduces graph to 607 nodes.
Next reduction is to remove node N
with signature (p1, ..., pn)
if there are nodes with signatures that is decomposition of (p1, ..., pn)
and there sum is >= N
. That reduces graph to 277 nodes.
From these nodes 73 are isolated nodes (primes > 500.)
Upvotes: 5
Reputation: 42009
I don't know the answer to this, it seems non-trivial to me. Here are a few thoughts.
The sum consists of a variable number of summands, say s0 + s1 + ... sk, where si is an integer in the interval [2, 1000]. Now each si has a prime-power factorization si=(p1e1)*(p2e2) ... where ei ≥ 1.
The condition that "that any two numbers from the subset don't share common prime factors" is equivalent to stating that si are pairwise relatively prime, i.e. gcd(si, sj)=1 for i ≠ j. Also equivalently, whenever one summand si contains a prime p that means that no other summand may contain that prime.
So how do you arrange the primes into summands? One simple rule is immediately obvious. All the primes in [500, 1000] can only appear in the sum alone as individual summands. If they are multiplied by anything else, even the smallest prime 2, the product will be too large. So that leaves the task of arranging the smaller primes. And I don't know they best way to do that. For the sake of completeness I'll provide the following short python program that shows one way.
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))
Output is
84972
81447
showing that "strategy 1" is superior.
Superior, but not necessarily optimal. For example, including 1000 seems good, but it forces us to exclude every other even summand and every summand divisible by 5. If we leave 1000 out but include 998 instead, we get to use another summand that includes 5 in it's prime factorization. But including 998 forces other summands to be excluded. So maximizing the sum is not trivial.
Upvotes: 2