Planning a competition - algorithm

I need to produce the schedule of a sport-event.
There are 30 teams. Each team has to play 8 matches. This means that it is not possible for each team to compete again all other teams, but I need to avoid that two team compete more than once against each other.
My idea was to generate all possible matches (for 30 teams: (30*29)/2 = 435 matches) and select from this list 120 matches (8 match for each team: 8 * 30 / 2 = 120 matches).
This is where I'm having a hard time: how can I select these 120 matches? I tried some simple solutions (take first match of the list, then the last, and so on) but they don't seem to work with 30 teams. I also tried to generate all possible match combination and find which one is working but with 30 team, this is too much calculation time.
Is there an existing algorithm that I could implement?
UPDATE
What I need to produce is a simple schedule, without elimination. Each team play 8 matchs, and that's all. At the end of the day there won't be one winner.
Each team will have his schedule, and this schedule won't change wether they win or lose. The planning is done for the whole day and is immutable.
UPDATE 2
At first, I didn't want to put too many constraints to my question, but it seems that without any constraints (other than each team not competing more than once with each other), it's just a matter of random picking 8 matchs for each team.
So here is some more details :
During this sport event, there are 6 differents sports (soccer, handball, basketball, and so on). This means there are 6 simultaneous matchs. A new round is started every 15 minutes.
Each team will have to play 8 matches, and each sport at least once.
These 6 sports are taking place at three different places. This means that during the day, each team will have to move from one place to another. These moves should be reduced as much as possible.
A team cannot play two matches in a row.

You could look into some already known matching approaches:
E.g. Swiss Chess system
Edit:
After reading your requirements again - that every team should play every other team exactly once, and that a winner need not necessarily be decided. It seems like a single Round Robin system would do what you want. You could just drop any extra matchups above the 8 you need.

It's quite simple really, just team up team i with i-4, i-3, i-2, i-1, i+1, i+2, i+3, i+4. This can be done using the algorithm below.
import java.util.*;
public class Test {
public static void main(String[] args) {
int TEAMS = 30, MATCHES = 8;
int[] matchCount = new int[TEAMS]; // for a sanity check.
List<Match> matches = new ArrayList<Match>();
for (int team1 = 0; team1 < TEAMS; team1++)
for (int team2 = team1 + 1; team2 <= team1 + MATCHES/2; team2++) {
matches.add(new Match(team1, team2 % TEAMS));
// Just for a sanity check:
matchCount[team1]++;
matchCount[team2 % TEAMS]++;
}
System.out.println(matches);
// Sanity check:
System.out.println(matches.size());
System.out.println(Arrays.toString(matchCount));
}
static class Match {
int team1, team2;
public Match(int team1, int team2) {
this.team1 = team1;
this.team2 = team2;
}
public String toString() {
return team1 + " vs " + team2;
}
}
}
Output:
[0 vs 1, 0 vs 2, 0 vs 3, 0 vs 4, 1 vs 2, 1 vs 3, 1 vs 4, 1 vs 5, 2 vs 3, 2 vs 4, 2 vs 5, 2 vs 6, 3 vs 4, 3 vs 5, 3 vs 6, 3 vs 7, 4 vs 5, 4 vs 6, 4 vs 7, 4 vs 8, 5 vs 6, 5 vs 7, 5 vs 8, 5 vs 9, 6 vs 7, 6 vs 8, 6 vs 9, 6 vs 10, 7 vs 8, 7 vs 9, 7 vs 10, 7 vs 11, 8 vs 9, 8 vs 10, 8 vs 11, 8 vs 12, 9 vs 10, 9 vs 11, 9 vs 12, 9 vs 13, 10 vs 11, 10 vs 12, 10 vs 13, 10 vs 14, 11 vs 12, 11 vs 13, 11 vs 14, 11 vs 15, 12 vs 13, 12 vs 14, 12 vs 15, 12 vs 16, 13 vs 14, 13 vs 15, 13 vs 16, 13 vs 17, 14 vs 15, 14 vs 16, 14 vs 17, 14 vs 18, 15 vs 16, 15 vs 17, 15 vs 18, 15 vs 19, 16 vs 17, 16 vs 18, 16 vs 19, 16 vs 20, 17 vs 18, 17 vs 19, 17 vs 20, 17 vs 21, 18 vs 19, 18 vs 20, 18 vs 21, 18 vs 22, 19 vs 20, 19 vs 21, 19 vs 22, 19 vs 23, 20 vs 21, 20 vs 22, 20 vs 23, 20 vs 24, 21 vs 22, 21 vs 23, 21 vs 24, 21 vs 25, 22 vs 23, 22 vs 24, 22 vs 25, 22 vs 26, 23 vs 24, 23 vs 25, 23 vs 26, 23 vs 27, 24 vs 25, 24 vs 26, 24 vs 27, 24 vs 28, 25 vs 26, 25 vs 27, 25 vs 28, 25 vs 29, 26 vs 27, 26 vs 28, 26 vs 29, 26 vs 0, 27 vs 28, 27 vs 29, 27 vs 0, 27 vs 1, 28 vs 29, 28 vs 0, 28 vs 1, 28 vs 2, 29 vs 0, 29 vs 1, 29 vs 2, 29 vs 3]
120
[8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]
If you would like a more randomized setup, you could simply assign a random number between 1 and 30 to each team.
Update To cope with your added constraints: Let match i be of sport i mod 6.

Are you sure you couldn't get 32 teams :-) ?
That would make things simpler - have a standard tournament structure, but have the losers from each round play in their own chart.
I think this maximises the number of teams who win at least one match during the event.
With 30 teams, you could have 2 teams play a 'friendly' and have a by in the first round. But the organisation becomes much more complicated.

I do not know of an existing implementation, but here is a possible solution for you:
Make three groups of 9 teams and pair them each with all others, so that each plays once against all others. Each of the 27 teams now plays 8 games.
Now take the remaining three teams, one for each group.
Modify some games of each team:
1-2 -> 1-10, 2-10
3-4 -> 3-10, 4-10
5-6 -> 5-10, 6-10
7-8 -> 7-10, 8-10

Broker's Algorithm might work. Funnily enough I can't find a good description in the net, so I'll try to explain the system.
Effectively, each team would ask each other team for a score for a match and the matchup that scores highest is selected. This is repeated for each team and match. The resulting schedule is saved and a total score is calculated. Then with different starting points (i.e. you could just randomise the team order) this is done again and if the total score is higher, this schedule is selected instead. You repeat this until you either find a schedule yielding a high enought score or after a pre-defined number of tries.
Total score could be calculated by distance travelled by each team, the smaller the number the better. Obviously you don't pick matches that break your rules (too many matches of similar type, same teams playing each other again).
Scoring could go something like this:
If same venue for team: 100 points (i.e. if for both = 200).
For travelling between venues, score is determined for both teams depending on length, i.e. A -> B and B -> A 50 points (they are near) A -> C and C -> A 30 points (not so near)
B -> C and C -> B 0 points (the travelling we want to do as little as possible).
If team hasn't played this sports yet: 100 points.
If team has played this sport once: 50 points.
Of course the problem with BA is finding good values for different parameters so you would need to spend some time finding them.

If you want a simple algorithm that will produce a schedule where teams don't play against each other more than once that is not hard with given parameters.
Here is an example for 10 teams and 5 rounds, the solution is shown as an array where if a value of schedule[i][j] is zero the teams don't play together, and if it is a number then it shows in which round they play together.
1 2 3 4 5 6 7 8 9 10
1 [0, 5, 0, 4, 0, 3, 0, 2, 0, 1]
2 [5, 0, 4, 0, 3, 0, 2, 0, 1, 0]
3 [0, 4, 0, 3, 0, 2, 0, 1, 0, 5]
4 [4, 0, 3, 0, 2, 0, 1, 0, 5, 0]
5 [0, 3, 0, 2, 0, 1, 0, 5, 0, 4]
6 [3, 0, 2, 0, 1, 0, 5, 0, 4, 0]
7 [0, 2, 0, 1, 0, 5, 0, 4, 0, 3]
8 [2, 0, 1, 0, 5, 0, 4, 0, 3, 0]
9 [0, 1, 0, 5, 0, 4, 0, 3, 0, 2]
10[1, 0, 5, 0, 4, 0, 3, 0, 2, 0]
So from this table in first round the teams (1, 10), (2, 9), (3, 8), (4, 7), (5, 6) play, in the second round the teams (1, 8), (2, 7), (3, 6)... etc.
To produce this table the algorithm is rather trivial, here is some python code:
#!/bin/env python
def simpleNRooks(size, rounds, schedule):
''' Place n rooks on board so that they don't hit each other in each round,
nor reuse the spots from previous rounds '''
for i in range(size):
for j in range(rounds):
if size-j*2-i-1 < 0:
schedule[i][2*size-j*2-i-1] = j + 1
else:
schedule[i][size-j*2-i-1] = j + 1
# parameters
teams = 10
matches = 5
# prepare the schedule, 0's designate free space
schedule = [[0 for i in range(teams)] for j in range(teams)]
simpleNRooks(teams, matches, schedule)
print 'Final schedule'
for i in range(teams):
print schedule[i]
If you want to get a different data structure out (for example a list of pairs by rounds) you can use the same principle, but change the loops.

I'm adding a separate answer given the new constraints, as I think it's clearer than adding to my old answer.
Split the 30 teams into 5 groups each of 6 teams: A B C D E
For the first period Group A and B play.
Then C&D, E&A, B&C, D&E, for the next 4 fifteen minute segments.
So at the end of 5 * 15 minutes: Every team has played twice, with at least one rest period between goes.
Have 20 periods and everyone has played 8 times.
It should be easy to allow a team in group B, for example, to play against 8 of the other teams from the 17 other teams in A,B & C groups.
For example play A teams against matching B teams, then later, B teams against matching C teams, then reverse lists, then within groups, then MOD 2, MOD 3 between groups, and within groups.
That just leaves minimising travel time, and ensuring that every team plays every game type. But solve that for one group, and you can apply the same solution to all the other ones?

Related

"super ugly number" clarification

Write a program to find the nth super ugly number.
Super ugly numbers are positive numbers whose all prime factors are in the given prime list of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4.
I don't understand the question. That is what I need help/clarification on:
In the above statement, why are [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] the first 12 super ugly numbers? How is that related to the given input primes = [2, 7, 13, 19]
2 -> 2
4 -> 2 * 2
7 -> 7
8 -> 2 * 2 * 2
13 -> 13
14 -> 2 * 7
16 -> 2 * 2 * 2 * 2
19 -> 19
26 -> 2 * 13
28 -> 2 * 2 * 7
32 -> 2 * 2 * 2 * 2 * 2
Not sure why 1 is on the list. ;)
Edit: The question statement says that 1 should always be a super ugly number.
You are given a list containing a selection of prime numbers : for example, [2,7,13,19].
What you must do is take each natural integer (1, 2, ...), starting from 1, and calculate its prime factors. If all those prime factors belong to the list of "authorized" prime numbers given above, then the number is declared "super ugly".
For example, the prime factors of 14 are [2, 7], which are all in the reference list ([2,7,13,19]). So, 14 is super ugly.
You job is to find the Nth super ugly number with that method.

Splitting 61 into integer partitions

In a program I am doing, I have a vector called a = [5, 6, 7] and I have to split integer 61 into additive partitions using integer from this list. So one example would be
61 = 5 + 6 + 7 + 7 + 6 + 6 + 6 + 6 + 6 + 6
There are many ways to split this. I have to do this programmatically. One approach I found is as follows. I don't know if this always will give result. First I check if 61 is divisible by any number in the list. If it is, then I can just use that number to add many times (i.e. quotient) to get 61. In this case, 61 is a prime number. So this will fail. Next step is to
take first number in the list (in our case, 5) and subtract it from 61 and try to see if the answer is divisible by any member in the list. If it is, then we again found a way to do addition. In this case, subtracting 5 from 61 gives 56, which is divisible by 7 and our solution would be
61 = 5 + 7 + 7 + 7 + 7 + 7 + 7 + 7 + 7
In this manner we continue down the list until we find some answer after subtraction which is divisible by a member in the list.
Now the given list to me, [5, 6, 7] is such that there exists an integer partition such that, we can get 61 from additions using that integer partition. So we won't have to worry whether a solution exists. So my approach seems very crude. I wonder if there is an efficient way to do this
using some algorithm from combinatorics. So my final answer should be a list
of numbers from the integer partition. So one possible answer would be
[5, 6, 7, 7, 6, 6, 6, 6, 6, 6]
thanks
The generic root of 61 is 7
Multiple of 7 nearing 61 will be 8 hence , subtracting 61-7*8 =gave 56,
Similarly multiple of 6 nearing 61 with a gap of 7 will then be 9,
subtracting 61-6*9 =gave 54,then result % remaining two from {5,7} to be satisfied should be zero
Getting intermediate sum and finding their generic root combined with displacement can give the answer.
Hope this helps. !! Happy to help further
I think I found a crude logic here. Let's say given list is [5, 6, 7] and number is 61. we need to find additive list such that total is 61. one such example is
61 = 5 + 6 + 7 + 7 + 6 + 6 + 6 + 6 + 6 + 6
another would be
61 = 5 + 7 + 7 + 7 + 7 + 7 + 7 + 7 + 7
Our job is to get the numbers on the right side as a list. So one possible solution would be
[5, 6, 7, 7, 6, 6, 6, 6, 6, 6]
My algorithm is as follows. First subtract all list members from 61. So 61 - 5 - 6 - 7 = 43. So we get first three members from the list, which are 5, 6, 7. Now remainder here is 43. To get other members, we subtract each of 5, 6, 7 from 43 , one at a time and see if the answer is divisible by any of 5, 6, 7. So
43 - 5 = 38 -> not divisible by any [5, 6, 7]
43 - 6 = 37 -> not divisible by any [5, 6, 7]
43 - 7 = 36 -> divisible by 6 in [5, 6, 7]
and quotient is 6, which means we have to use 6 of 6's and 7 which we subtracted last from 43. So the list would be original list plus 7 and a
list of 6 with length 6. So one possible solution I found is
[5, 6, 7, 7, 6, 6, 6, 6, 6, 6]
And we can verify that the sum is 61. I wrote a program in R programming language. Here it is
get_me_list <- function(number, mylist){
rest <- number - sum(mylist)
flag = FALSE
for(i in seq_along(mylist)){
answer <- rest - mylist[i]
for(j in seq_along(mylist)) {
if( answer %% mylist[j] == 0){
repeat_factor <- answer / mylist[j]
number_to_repeat <- mylist[j]
pivot <- mylist[i]
flag = TRUE
break
}
}
if(flag){
break
}
}
final_list <- c(mylist, pivot, rep(number_to_repeat, repeat_factor))
final_list
}
So get_me_list function takes two inputs. number and my_list. In my case number = 61 and my_list = [5, 6, 7] . In R, a vector is written as
c(5, 6, 7) or 5:7 if its a sequence from 5 to 7. So the output I get is
c(5, 6, 7, 7, 6, 6, 6, 6, 6, 6)
which is a vector in R. I tried giving various values of my_list and compared the solution with manual solution which I calculated using pen and paper. I am getting correct answer with the above code. I don't know if this approach would be valid always. I am off course assuming that its possible to get the sum equal to number using only the members of the my_list.
Please comment on code.
thanks

Retrieving elements from array regarding to an accumulating parameter

Assume that there are 2 arrays of elements and a function call will return elements within them. Each time a retrieval is performed, 8 elements will be retrieved from array 1, while 2 will be retrieved from array 2. And the elements to be retrieved is indicated by a number provided, assume that list 1 has 35 elements, and list 2 has 7, the situation will be like:
Assume the 2 arrays are:
array 1: 0, 1, 2, 3, 4, ..., 35
array 2: 0, 1, 2, 3, 4, 5, 6
number provided elements from array 1 elements from array 2
1 0, 1, 2, 3, 4, 5, 6, 7 0, 1
11 8, 9, 10, 11, 12, 13, 14, 15 2, 3
21 16, 17, 18, 19, 20, 21, 22, 23 4, 5
31 24, 25, 26, 27, 28, 29, 30, 31 6
40 32, 33, 34, 35 0, 1
46 0, 1, 2, 3, 4, 5, 6, 7 2, 3
56 8, 9, 10, 11, 12, 13, 14, 15 4, 5
66 16, 17, 18, 19, 20, 21, 22, 23 6
75 24, 25, 26, 27, 28, 29, 30, 31 0, 1
85 32, 33, 34, 35 2, 3
...
Each time a retrieval is done, the count of numbers returned will be added to the last provided number become the next provided number. If one of the list is exhausted (remaining elements fewer than 8), then the remaining numbers will be retrieved from that list, and next time it will start retrieving elements start from index 0 again, like the situations when number 31 and 40 is passed.
The question is, is there anyway to determine what position to start in both array when a number is provided? e.g. when number 40 is given, I should start at 32 in list 1, and 0 in list 2. Like the above situation, list one is exhausted every 5th retrieval, while list 2 exhausted at every 4th retrieval, but since the provided number is based on the accumulated count of number retrieved, how can I determine where to start this time when a number is given?
I have been thinking this for days and really feel frustrated about it. Thanks for any help!
Their is a cycle. And one cycle will have total_num numbers, we can get total_num from the code bellow:
def get_one_cycle_numbers:
n = len(a) / 8
m = len(b) / 2
g = gcd(n, m)
total_num = len(a) * n / g + len(b) * m / g
return total_num
When we get the provided number num we just num = num % total_num and simulate the cycle.
PS: Hope I got the right understanding of the question.

Riffling Cards in Mathematica

My friend posed this question to me; felt like sharing it here.
Given a deck of cards, we split it into 2 groups, and "interleave them"; let us call this operation a 'split-join'. And repeat the same operation on the resulting deck.
E.g., { 1, 2, 3, 4 } becomes { 1, 2 } & { 3, 4 } (split) and we get { 1, 3, 2, 4 } (join)
Also, if we have an odd number of cards i.e., { 1, 2, 3 } we can split it like { 1, 2 } & { 3 } (bigger-half first) leading to { 1, 3, 2 }
(i.e., n is split up as Ceil[n/2] & n-Ceil[n/2])
The question my friend asked me was:
HOW many such split-joins are needed to get the original deck back?
And that got me wondering:
If the deck has n cards, what is the number of split-joins needed if:
n is even ?
n is odd ?
n is a power of '2' ? [I found that we then need log (n) (base 2) number of split-joins...]
(Feel free to explore different scenarios like that.)
Is there a simple pattern/formula/concept correlating n and the number of split-joins required?
I believe, this is a good thing to explore in Mathematica, especially, since it provides the Riffle[] method.
To quote MathWorld:
The numbers of out-shuffles needed to return a deck of n=2, 4, ... to its original order are 1, 2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, ... (Sloane's A002326), which is simply the multiplicative order of 2 (mod n-1). For example, a deck of 52 cards therefore is returned to its original state after eight out-shuffles, since 2**8=1 (mod 51) (Golomb 1961). The smallest numbers of cards 2n that require 1, 2, 3, ... out-shuffles to return to the deck's original state are 1, 2, 4, 3, 16, 5, 64, 9, 37, 6, ... (Sloane's A114894).
The case when n is odd isn't addressed.
Note that the article also includes a Mathematica notebook with functions to explore out-shuffles.
If we have an odd number of cards n==2m-1, and if we split the cards such that during each shuffle the first group contains m cards, the second group m-1 cards, and the groups are joined such that no two cards of the same group end up next to each other, then the number of shuffles needed is equal to MultiplicativeOrder[2, n].
To show this, we note that after one shuffle the card which was at position k has moved to position 2k for 0<=k<m and to 2k-2m+1 for m<=k<2m-1, where k is such that 0<=k<2m-1. Written modulo n==2m-1 this means that the new position is Mod[2k, n] for all 0<=k<n. Therefore, for each card to return to its original position we need N shuffles where N is such that Mod[2^N k, n]==Mod[k, n] for all 0<=k<n from which is follows that N is any multiple of MultiplicativeOrder[2, n].
Note that due to symmetry the result would have been exactly the same if we had split the deck the other way around, i.e. the first group always contains m-1 cards and the second group m cards. I don't know what would happen if you alternate, i.e. for odd shuffles the first group contains m cards, and for even shuffles m-1 cards.
There's old work by magician/mathematician Persi Diaconnis about restoring the order with perfect riffle shuffles. Ian Stewart wrote about that work in one of his 1998 Scientific American Mathematical Recreation columns -- see, e.g.: http://www.whydomath.org/Reading_Room_Material/ian_stewart/shuffle/shuffle.html
old question I know, but strange no one put up an actual mathematica solution..
countrifflecards[deck_] := Module[{n = Length#deck, ct, rifdeck},
ct = 0;
rifdeck =
Riffle ##
Partition[ # , Ceiling[ n/2], Ceiling[ n/2], {1, 1}, {} ] &;
NestWhile[(++ct; rifdeck[#]) &, deck, #2 != deck &,2 ]; ct]
This handles even and odd cases:
countrifflecards[RandomSample[ Range[#], #]] & /# Range[2, 52, 2]
{1, 2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 28, 5, 10, 12, 36,
12, 20, 14, 12, 23, 21, 8}
countrifflecards[RandomSample[ Range[#], #]] & /# Range[3, 53, 2]
{2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 28, 5, 10, 12, 36, 12,
20, 14, 12, 23, 21, 8, 52}
You can readily show if you add a card to the odd-case the extra card will stay on the bottom and not change the sequence, hence the odd case result is just the n+1 even result..
ListPlot[{#, countrifflecards[RandomSample[ Range[#], #]]} & /#
Range[2, 1000]]

finding maximal subsets

For given n, find the subset S of {1,2,...,n} such that
all elements of S are coprime
the sum of the elements of S is as large as possible
Doing a brute force search takes too long and I can't find a pattern. I know that I can just take all the primes from 1 to n, but that's probably not the right answer. Thanks.
I would tackle this as a dynamic programming problem. Let me walk through it for 20. First take the primes in reverse order.
19, 17, 13, 11, 7, 5, 3, 2
Now we're going to walk up the best solutions which have used subsets of those primes of increasing size. We're going to do a variation of breadth first search, but with the trick that we always use the largest currently unused prime (plus possibly more). I will represent all of the data structures in the form size: {set} = (total, next_number). (I'm doing this by hand, so all mistakes are mine.) Here is how we build up the data structure. (In each step I consider all ways of growing all sets of one smaller size from the previous step, and take the best totals.)
Try to reproduce this listing and, modulo any mistakes I made, you should have an algorithm.
Step 0
0: {} => (1, 1)
Step 1
1: {19} => (20, 19)
Step 2
2: {19, 17} => (37, 17)
Step 3
3: {19, 17, 13} => (50, 13)
Step 4
4: {19, 17, 13, 11} => (61, 11)
Step 5
5: {19, 17, 13, 11, 7} => (68, 7)
6: {19, 17, 13, 11, 7, 2} => (75, 14)
Step 6
6: {19, 17, 13, 11, 7, 5} => (73, 5)
{19, 17, 13, 11, 7, 2} => (75, 14)
7: {19, 17, 13, 11, 7, 5, 2} => (88, 20)
{19, 17, 13, 11, 7, 5, 3} => (83, 15)
Step 7
7: {19, 17, 13, 11, 7, 5, 2} => (88, 20)
{19, 17, 13, 11, 7, 5, 3} => (83, 15)
8: {19, 17, 13, 11, 7, 5, 3, 2} => (91, 18)
Step 8
8: {19, 17, 13, 11, 7, 5, 3, 2} => (99, 16)
And now we just trace the data structures backwards to read off 16, 15, 7, 11, 13, 17, 19, 1 which we can sort to get 1, 7, 11, 13, 15, 16, 17, 19.
(Note there are a lot of details to get right to turn this into a solution. Good luck!)
You can do a little better by taking powers of primes, up the to bound you have. For example, suppose that n=30. Then you want to start with
1, 16, 27, 25, 7, 11, 13, 17, 19, 23, 29
Now look at where there are places to improve. Certainly you cannot increase any of the primes that are already at least n/2: 17, 19, 23, 29 (why?). Also, 3^3 and 5^2 are pretty close to 30, so they're also probably best left alone (why?).
But what about 2^4, 7, 11 and 13? We can take the 2's and combine them with 7, 11, or 13. This would give:
2 * 13 = 26 replaces 16 + 13 = 29 BAD
2 * 11 = 22 replaces 16 + 11 = 27 BAD
2^2 * 7 = 28 replaces 16 + 7 = 23 GOOD
So it looks like we should get the following list (now sorted):
1, 11, 13, 17, 19, 23, 25, 27, 28, 29
Try to prove that this cannot be improved, and that should give you some insight into the general case.
Good luck!
The following is quite practical.
Let N = {1, 2, 3, ..., n}.
Let p1 < p2 < p3 < ... < pk be the primes in N.
Let Ti be the natural numbers in N divisible by pi but not by any prime less than pi.
We can pick at most one number from each subset Ti.
Now recurse.
S = {1}.
Check if pi is a divisor of any of the numbers already in S. If it is, skip Ti.
Otherwise, pick a number xi from Ti coprime to the elements already in S, and add it to S.
Go to next i.
When we reach k + 1, calculate the sum of the elements in S. If new maximum, save S away.
Continue.
Take n = 30.
The primes are 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29.
T1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}
T2 = {3, 9, 15, 21, 27}
T3 = {5, 25}
T4 = {7}
T5 = {11}
T6 = {13}
T7 = {17}
T8 = {19}
T9 = {23}
T10 = {29}
So fewer than 15 * 5 * 2 = 150 possibilities.
Here is my original wrong result for n = 100.
1 17 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 88 89 91 95 97 99
Sum = 1374
It should be
1 17 23 29 31 37 41 43 47 53 59 61 67 71 73 79 81 83 88 89 91 95 97
Sum = 1356
Less than 2 seconds for n = 150. About 9 seconds for n = 200.
I think that this is similar to the subset problem, which is NP-Complete.
First, break each number into its prime factors (or use a list of primes to generate the full list from 1 to n, same thing).
Solve the subset problem with recursive descend by finding a subset that contains no common primes.
Run through all solutions and find the largest one.
I implemented a recursive solution in Prolog, based on taking the list of integers in descending order. On my fairly ancient Toshiba laptop, SWI-Prolog produces answers without hesitation for N < 90. Here are some timings for N = 100 to 150 by tens:
N Sum Time(s)
----- --------- -------
100 1356 1.9
110 1778 2.4
120 1962 4.2
130 2273 11.8
140 2692 16.3
150 2841 30.5
The timings reflect an implementation that starts from scratch for each value of N. A lot of the computation for N+1 can be skipped if the result for N is previously known, so if a range of values N are to be computed, it would make sense to take advantage of that.
Prolog source code follows.
/*
Check if two positive integers are coprime
recursively via Euclid's division algorithm
*/
coprime(0,Z) :- !, Z = 1.
coprime(A,B) :-
C is B mod A,
coprime(C,A).
/*
Find the sublist of first argument that are
integers coprime to the second argument
*/
listCoprime([ ],_,[ ]).
listCoprime([H|T],X,L) :-
( coprime(H,X)
-> L = [H|M]
; L = M
),
listCoprime(T,X,M).
/*
Find the sublist of first argument of coprime
integers having the maximum possible sum
*/
sublistCoprimeMaxSum([ ],S,[ ],S).
sublistCoprimeMaxSum([H|T],A,L,S) :-
listCoprime(T,H,R),
B is A+H,
sublistCoprimeMaxSum(R,B,U,Z),
( T = R
-> ( L = [H|U], S = Z )
; ( sublistCoprimeMaxSum(T,A,V,W),
( W < Z
-> ( L = [H|U], S = Z )
; ( L = V, S = W )
)
)
).
/* Test utility to generate list N,..,1 */
list1toN(1,[1]).
list1toN(N,[N|L]) :-
N > 1,
M is N-1,
list1toN(M,L).
/* Test calling sublistCoprimeMaxSum/4 */
testCoprimeMaxSum(N,CoList,Sum) :-
list1toN(N,L),
sublistCoprimeMaxSum(L,0,CoList,Sum).

Resources