- Construct empty list of combined sections
- LOOP s1 over sections
- set found false
- LOOP s2 over sections
- IF s1 = s2
- CONTINUE
- IF s1,s2 not connected
- CONTINUE
- IF s1,s2 combined population > maximum
- CONTINUE
- ADD s1,s2 to list
- set found true
- break out of loop s2
- IF found
- CONTINUE loop s1
- SET found = true
- WHILE ( found )
found = false
- LOOP C over combination in list
- LOOP U over uncombined sections
- IF not connected
- CONTINUE
- IF combined population > maximum
- CONTINUE
- REPLACE C in list with C + U
- found = true
- Calculate value of list ( sum of populations in sections in list )
Now you have an initial feasible solution and can generate more
- WHILE( reasonable time limit not reached )
- random shuffle the order of sections used by the loops
- run the the above algorithm to get a different feasible solution
- IF new solution has a greater value than previous, replace
Here is the code to find a reasonable feasible solution
void combine()
{
raven::set::cRunWatch aWatcher("combine sections");
// Finds pairs of sections that can be combined
for (auto &a1 : vSection)
{
// check for already combined
if (a1.myfCombined)
continue;
// search for possible section to combine with a1
for (auto &a2 : vSection)
{
// is it uncombined
if (a2.myfCombined)
continue;
// is it itself
if (a1.myName == a2.myName)
continue;
// is the the combined area under the limit
if (a1.myArea + a2.myArea > maxArea)
continue;
// is it physically connected
if (!a1.isConnected(a2))
continue;
// OK to combine
cCombined comb;
comb.add(a1);
comb.add(a2);
theCombined.push_back(comb);
break;
}
}
// Try adding uncombined sections to the combinations already found
bool fimproved = true;
while (fimproved)
{
// loop over the combinations until no more new combinations found
fimproved = false;
for (auto &C : theCombined)
{
// loop over uncombined seaction for possible addition to combimation
for (auto &U : vSection)
{
// it it uncombined
if (U.myfCombined)
continue;
// is the the combined area under the limit
if (C.myArea + U.myArea > maxArea)
continue;
// is it physically connected
if (!C.IsConnected(U))
continue;
// OK, add to combination
C.add(U);
fimproved = true;
}
}
// check we are still finding additional combinations
if (!fimproved)
break;
}
}
The complete application code is at https://github.com/JamesBremner/so75423308
Here is the result of a unit test on a 10 section randomly generated problem
sections
S0 area 2 pop 68 connected 9 4 8 2
S1 area 1 pop 6 connected 7 5 2 6 4
S2 area 1 pop 54 connected 1 6
S3 area 1 pop 96 connected 6 1 8 9 2 7 5
S4 area 1 pop 4 connected 2
S5 area 2 pop 74 connected 1 3 8 7
S6 area 1 pop 63 connected 7 9 3 1 8 5 0
S7 area 1 pop 89 connected 0 2 4 8 6 5
S8 area 1 pop 30 connected
S9 area 1 pop 7 connected 3
combinations
S0 S2 S8 S9 | area 5 pop 159
S1 S4 | area 2 pop 10
S3 S5 | area 3 pop 170
S6 S7 | area 2 pop 152
Here is the result of a timing test on 2,000 node randomly generated problem
raven::set::cRunWatch code timing profile
Calls Mean (secs) Total Scope
1 0.0299339 0.0299339 combine sections
Conclusion: The code generates a feasible solution for your worst case problem in 30 milliseconds, so, for your performance requirement of 2 minutes, 4,000 possible solutions can be checked. This may not be enough to guarantee the optimal will be found but you should get quite close. For smaller problems the optimal is guaranteed.
Here is a timing profile where 4,000 combinations are tried and the best result is stored
Try 18 best value now 100542
Try 101 best value now 100552
Try 183 best value now 100577
Try 308 best value now 100582
raven::set::cRunWatch code timing profile
Calls Mean (secs) Total Scope
4000 0.0328638 131.455 try
4001 0.0327868 131.18 combine sections
Note that because of the strict limits ( area and physical connection ) on which sections are candidates to be combined, there are a lot less than 4,000 feasible combinations. Many different initial arrangements result in the same combination generated.
If you have an actual, real world problem please post it and I will run my code on it for you.