From [1]:
The data is organized as follows:
Here is the problem:
- There are 4 integer variables from within a limited set = [1, 10, 20, 40, 100, 200].
- When all 4 is concatenated (say 1 || 1 || 10 || 20 = 111020) the result can be divisible by 13.
I tried Python's pyomo library but can not find a solution as there is no modulus operation to decide if the number can be divisible by 13.
There is no objective in this problem, so let's invent a simple one: find the smallest concatenated number that is divisible by 13.
Discussion
There are two issues here:
- Requiring a number is divisible by 13. This is easy. Just use an integer variable \(\mathit{mul13}\in \{0,1,2,3,\dots\}\) and impose the constraint \[ x = 13 \cdot \mathit{mul13}\] This will assure that \(x\) is a multiple of 13.
- Concatenate numbers as if they are strings. This is not so easy. MIP solvers do not know anything about strings. So we have to find a work-around.
Let's work from the back. 10 || 20 can be interpreted as: \[20 + 10^{\mathit{numDigits}} \cdot 10\] where \(\mathit{numDigits}\) is the number of digits of 20 (i.e., \(\mathit{numDigits}=2\)). So, the 10 is actually worth 1000. With this idea, we can setup the recursion. Let's call each substring a "piece", indexed by \(p\). So, we have: \[\begin{align} & \mathit{cumDigits}_p = \begin{cases} \mathit{cumDigits}_{p+1} + \mathit{numDigits}_p & p \in \{1,2,3,4\} \\ 0 & p \gt 4 \end{cases}\\ & \mathit{cumValue}_p = \begin{cases} \mathit{cumValue}_{p+1} + 10^{\mathit{cumDigits}_{p+1}} \cdot \mathit{pieceValue}_p &p \in \{1,2,3,4\} \\ 0 &p \gt 4 \end{cases} \end{align}\] where \[\begin{align} & \mathit{numDigits}_p &&\text{number of digits in $p$-th piece} \\ & \mathit{cumDigits}_p && \text{cumulative number of digits in pieces $p,p+1,\dots$}\\ &\mathit{pieceValue}_p && \text{value of piece $p$}\\ & \mathit{cumValue}_p && \text{cumulative value of pieces $p,p+1,\dots$} \end{align}\]
The values of \( \mathit{numDigits}_p\) and \(\mathit{value}_p \) depend on what value we have chosen for piece \(p\).
Obviously, this is quite convoluted. Furthermore, recursion for the cumulative value calculation is non-linear. Let's see if we can put this together in an MINLP model.
Data
---- 14 SET p pieces
piece1, piece2, piece3, piece4
---- 14 SET s selection from values
select1, select2, select3, select4, select5, select6
---- 14 PARAMETER value selectable values
select1 1, select2 10, select3 20, select4 40, select5 100, select6 200
---- 14 PARAMETER digit number of digits
select1 1, select2 2, select3 2, select4 2, select5 3, select6 3
The number of digits for each value is calculated as: \[\mathit{digit}_p = \Bigl \lfloor{ 1 + \log_{10} \mathit{value}_p }\Bigr \rfloor \] where the funny brackets indicate the floor function.
Mathematical Model
A complete model can look like:
| MINLP Model |
|---|
| \[ \begin{align} \min\>& \color{darkred} z \\ & \sum_s \color{darkred}{\mathit{select}}_{p,s} = 1 && \forall p && \text{select exactly one value for each piece $p$}&& \text{(linear)}\\ & \color{darkred}{\mathit{pieceValue}}_p = \sum_s \color{darkblue}{\mathit{value}}_s \cdot \color{darkred}{\mathit{select}}_{p,s} && \forall p && \text{selected value} && \text{(linear)}\\ & \color{darkred}{\mathit{numDigits}}_p = \sum_s \color{darkblue}{\mathit{digit}}_s \cdot \color{darkred}{\mathit{select}}_{p,s} && \forall p && \text{associated number of digits}&& \text{(linear)}\\ & \color{darkred}{\mathit{cumDigits}}_p = \color{darkred}{\mathit{cumDigits}}_{p+1} + \color{darkred}{\mathit{numDigits}}_p && \forall p && \text{cumulative number of digits}&& \text{(linear)}\\ & \color{darkred}{\mathit{cumValue}}_p = \color{darkred}{\mathit{cumValue}}_{p+1} + 10^{\color{darkred}{\mathit{cumDigits}}_{p+1}} \cdot \color{darkred}{\mathit{pieceValue}}_p && \forall p &&\text{cumulative value} && \text{(non-linear)} \\ & \color{darkred} z = \color{darkred}{\mathit{cumValue}}_{\color{darkgreen}{\mathit{piece1}}} && && \text{objective variable} && \text{(linear)}\\ & \color{darkred} z = 13 \cdot \color{darkred}{\mathit{mul13}} && && \text{must be multiple of 13} && \text{(linear)}\\ & \color{darkred}{\mathit{select}}_{p,s} \in \{0,1\} \\ & \color{darkred}{\mathit{pieceValue}}_p \in \{0,1,2,\dots\} \\ & \color{darkred}{\mathit{numDigits}}_p \in \{0,1,2,\dots\} \\ & \color{darkred}{\mathit{cumDigits}}_p \in \{0,1,2,\dots\} \\ & \color{darkred}{\mathit{cumValue}}_p \in \{0,1,2,\dots\} \\ & \color{darkred}{\mathit{mul13}} \in \{0,1,2,\dots\} \end{align}\] |
In the above, we assume that \({\mathit{cumValue}}_{p+1}\) and \({\mathit{cumDigits}}_{p+1}\) have a value of zero when we go beyond the last element. This is automatic in GAMS, so we can follow the above model quite straightforwardly.
Results
Low and behold, Baron can solve the model very quickly. The only things I added are reasonable upper bounds on the integer variables and a setting OPTCR=0 in order to search for proven global solutions. The results look like:
---- 61 VARIABLE select.L selection of value
select1 select2
piece1 1
piece2 1
piece3 1
piece4 1
---- 61 VARIABLE pieceValue.L value of single piece
piece1 1, piece2 10, piece3 1, piece4 1
---- 61 VARIABLE numDigits.L number of digits of single piece
piece1 1, piece2 2, piece3 1, piece4 1
---- 61 VARIABLE cumValue.L cumulative value
piece1 11011, piece2 1011, piece3 11, piece4 1
---- 61 VARIABLE cumDigits.L cumulative number of digits
piece1 5, piece2 4, piece3 2, piece4 1
---- 61 VARIABLE Mul13.L = 847 multiple of 13
VARIABLE z.L = 11011 objective
Complete enumeration
To verify the solution we can use a complete enumeration approach. The total number of cases is \(6^4=1296\). This is not too large. Below is a table that illustrates the enumeration. Only the first 50 records are shown. The column div13 has a one where the concatenated number is a multiple of 13. The column smallest marks the best row.
---- 66 PARAMETER data complete enumeration
piece1 piece2 piece3 piece4 concat div13 smallest
k1 11111111
k2 1111011110
k3 1112011120
k4 1114011140
k5 111100111100
k6 111200111200
k7 1110111101
k8 111010111010
k9 1110201110201
k10 111040111040
k11 11101001110100
k12 111020011102001
k13 1120111201
k14 112010112010
k15 112020112020
k16 112040112040
k17 11201001120100
k18 11202001120200
k19 11401114011
k20 1140101140101
k21 114020114020
k22 114040114040
k23 114010011401001
k24 11402001140200
k25 111001111001
k26 11100101110010
k27 11100201110020
k28 11100401110040
k29 1110010011100100
k30 1110020011100200
k31 112001112001
k32 11200101120010
k33 11200201120020
k34 11200401120040
k35 1120010011200100
k36 1120020011200200
k37 110111101111
k38 1101101101101
k39 110120110120
k40 110140110140
k41 110110011011001
k42 11012001101200
k43 110101110101
k44 11010101101010
k45 11010201101020
k46 11010401101040
k47 1101010011010100
k48 1101020011010200
k49 1102011102011
k50 110201011020101
This confirms the solution we found using Baron.
Conclusion
The "string concatenation" problem in the original problem statement is not very easy to model in an MINLP model. We made a heroic effort, and it seems to work for this example. Baron was able to find the optimal solution. However, due to the large numbers involved, I doubt this approach can be used for larger problems. MINLP and MIP algorithms are not very suited for large integer variables.
References
- Could this problem be solved using mixed integer non-linear programming or any other optimization technique?, https://stackoverflow.com/questions/63136355/could-this-problem-be-solved-using-mixed-integer-non-linear-programming-or-any-o