Being a math dude, I wrote the solution. I tested it on the simulator and the project is attached as a .ZIP file with the .DMD project and workspace files. It appears to work.
The key algorithm is in task code-block Optimize. It goes through ALL 1,048,575 combinations of 20 lengths (data-block Lengths0..19), finding the right combination that gets you within the Tolerance (D98) of the Target (D100). Once it hits a combination that is within tolerance, it stops. However, I kept the Tolerance 0 just to see how good the algorithm was, and it's pretty fast at finding the BEST answer.
Basically, when X0 goes from OFF to ON, it does the following:
1. Generate 20 random lengths in Lengths0..Lengths19 with values between 5 and 2005.
2. Generate a random Target between 5000 and 15000.
3. Find the optimal combination of the 20 lengths that fall within tolerance. This is done by code-block Optimize. I would definitely test it ON ACTUAL PLC HARDWARE with a large length that would require going through ALL the combinations with a tolerance of 0 (e.g. a Target of 100,000) just to see how "bad" it can be.
4. I put in metrics in $Main to calculate how long it takes. R0 is how long in SECONDS the Optimize calculation took.
The specific lengths end up being in the bit block Selection0..19. So if bit Selection17 is ON, then Length17 was part of the "optimal" solution. If bit Selection0 is OFF, then Length0 was NOT part of the "optimal" solution. So, some combination of bits Selection0..Selection19 tells you which of the Lengths0..Lengths19 should be "used".
I have also put the Optimized logic below to help the discussion. The full project is in the attached .ZIP file.
// Options: Export code block Optimize from address 0 to 223;
// Code Block delimiter instructions; Unformatted Rung Comments;
// Element Documentation Database; Memory Configuration/Devices with User Add-Ons only;
// prefer NickNames over Element names; rung/address annotations;
// <SPACE> parameter delimiter;
// Write/overwrite file C:\Do-more\Designer1_2\Projects\Optimize.txt
PLC DM-SIM
#BEGIN MEM_CONFIG
Lengths SDWORD decimal 100
Selection BIT decimal 128
GenData TASK 0 -1
Optimize TASK 0 -1
#END
// Beginning of Code Block Optimize
$TSK Optimize
// Rung Optimize#1
// Offset 0
#BEGIN COMMENT
"Initialize current optimal selection to nothing (no lengths are part of the optimized solution),"
"Initialize the optimal error to the largest signed integer (2 billion) (ANYTHING will be better than THAT error)"
"Set my .TimeSlice to 1ms (1000 microseconds, this let me get some work done every ladder scan)"
#END
MOVE 0 CurrOptSelection
MOVE 0x7FFFFFFF OptimalError
MOVE 1000 Optimize.TimeSlice
// Rung Optimize#2
// Offset 7
#BEGIN COMMENT
"This loop goes through EVERY POSSIBLE COMBINATION of lengths as 2 ** 20 bit patterns, from 0x0001 thru 0xFFFFF."
#END
FOR Selection0:SD 0x1 0xFFFFF 1
// Rung Optimize#3
// Offset 14
#BEGIN COMMENT
"Calculate the sum of the current ""selected bits"" in Selection0..Selection19."
"First, initialize the current sum to 0."
#END
MOVE 0 CurrLengthSum
// Rung Optimize#4
// Offset 16
#BEGIN COMMENT
"For 20 rungs, if that Selection bit is set, add the corresponding Lengths value to CurrLengthSum."
"So the first time through, only the first rung that addes Lengths0 will be utilized, and for the last time thorugh (0xFFFFFF), ALL 20 bits will be set, so all 20 rungs will add their lengths."
""
"**** Do NOT use a FOR/NEXT loop of 0..19 with array indexing, because array indexing is SLOW. THIS ALGORITHM NEEDS TO BE FAST! So, it's okay to burn 20 rungs. ***"
#END
STR Selection0
MATH CurrLengthSum "CurrLengthSum + Lengths0"
// Rung Optimize#5
// Offset 25
STR Selection1
MATH CurrLengthSum "CurrLengthSum + Lengths1"
// Rung Optimize#6
// Offset 34
STR Selection2
MATH CurrLengthSum "CurrLengthSum + Lengths2"
// Rung Optimize#7
// Offset 43
STR Selection3
MATH CurrLengthSum "CurrLengthSum + Lengths3"
// Rung Optimize#8
// Offset 52
STR Selection4
MATH CurrLengthSum "CurrLengthSum + Lengths4"
// Rung Optimize#9
// Offset 61
STR Selection5
MATH CurrLengthSum "CurrLengthSum + Lengths5"
// Rung Optimize#10
// Offset 70
STR Selection6
MATH CurrLengthSum "CurrLengthSum + Lengths6"
// Rung Optimize#11
// Offset 79
STR Selection7
MATH CurrLengthSum "CurrLengthSum + Lengths7"
// Rung Optimize#12
// Offset 88
STR Selection8
MATH CurrLengthSum "CurrLengthSum + Lengths8"
// Rung Optimize#13
// Offset 97
STR Selection9
MATH CurrLengthSum "CurrLengthSum + Lengths9"
// Rung Optimize#14
// Offset 106
STR Selection10
MATH CurrLengthSum "CurrLengthSum + Lengths10"
// Rung Optimize#15
// Offset 115
STR Selection11
MATH CurrLengthSum "CurrLengthSum + Lengths11"
// Rung Optimize#16
// Offset 124
STR Selection12
MATH CurrLengthSum "CurrLengthSum + Lengths12"
// Rung Optimize#17
// Offset 133
STR Selection13
MATH CurrLengthSum "CurrLengthSum + Lengths13"
// Rung Optimize#18
// Offset 142
STR Selection14
MATH CurrLengthSum "CurrLengthSum + Lengths14"
// Rung Optimize#19
// Offset 151
STR Selection15
MATH CurrLengthSum "CurrLengthSum + Lengths15"
// Rung Optimize#20
// Offset 160
STR Selection16
MATH CurrLengthSum "CurrLengthSum + Lengths16"
// Rung Optimize#21
// Offset 169
STR Selection17
MATH CurrLengthSum "CurrLengthSum + Lengths17"
// Rung Optimize#22
// Offset 178
STR Selection18
MATH CurrLengthSum "CurrLengthSum + Lengths18"
// Rung Optimize#23
// Offset 187
STR Selection19
MATH CurrLengthSum "CurrLengthSum + Lengths19"
// Rung Optimize#24
// Offset 196
#BEGIN COMMENT
"Calculate the absolute error based on the CurrLengthSum and the Target value"
#END
STR $On
MATH CurrAbsolutError "ABS(Target - CurrLengthSum)"
// Rung Optimize#25
// Offset 206
#BEGIN COMMENT
"Is this Selection combination better than the OptimalError so far?"
"If so, save this combination selection set and error value."
#END
STRLT CurrAbsolutError OptimalError
MOVE CurrAbsolutError OptimalError
MOVE Selection0:SD CurrOptSelection
// Rung Optimize#26
// Offset 214
#BEGIN COMMENT
"Is this optimal error within our Tolerance, if so, break out of the big/long FOR/NEXT loop, cuz we are DONE!"
#END
STRLE OptimalError Tolerance
BREAK
// Rung Optimize#27
// Offset 220
NEXT
// Rung Optimize#28
// Offset 222
#BEGIN COMMENT
"Move the current optimal selection into the ACTUAL Selection bit set using casts."
#END
MOVE CurrOptSelection Selection0:SD
// End of Code Block Optimize
$TSKEND Optimize
#BEGIN ELEMENT_DOC
"D0","StartTime","",""
"D98","Tolerance","",""
"D99","CurrOptSelection","",""
"D100","Target","",""
"D101","CurrLengthSum","",""
"D102","OptimalError","",""
"D103","CurrAbsolutError","",""
"R0","CalcTime","Seconds",""
#END