1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "knapsack_solver.h"
25 //////////////////////////////////////////////////////////////////////////////
27 //////////////////////////////////////////////////////////////////////////////
29 std::string
CKnapsackSolver::toString(Algorithm a
)
33 case Optimal
: return "Optimal";
34 case FullAddCheck
: return "FullAddCheck";
35 case AddCheck
: return "AddCheck";
36 case FastAddCheck
: return "FastAddCheck";
37 case FullSingleReplace
: return "FullSingleReplace";
38 case SingleReplace
: return "SingleReplace";
39 case FastSingleReplace
: return "FastSingleReplace";
40 case VeryFastSingleReplace
: return "VeryFastSingleReplace";
41 case TakeAll
: return "TakeAll";
42 default: return "undefined";
46 CKnapsackSolver::Algorithm
CKnapsackSolver::fromString(std::string
const& a
)
48 if (a
=="Optimal") return Optimal
;
49 if (a
=="FullAddCheck") return FullAddCheck
;
50 if (a
=="AddCheck") return AddCheck
;
51 if (a
=="FastAddCheck") return FastAddCheck
;
52 if (a
=="FullSingleReplace") return FullSingleReplace
;
53 if (a
=="SingleReplace") return SingleReplace
;
54 if (a
=="FastSingleReplace") return FastSingleReplace
;
55 if (a
=="VeryFastSingleReplace") return VeryFastSingleReplace
;
56 if (a
=="TakeAll") return TakeAll
;
57 return UndefinedAlgorithm
;
60 /// Find the set that fit the specified maximum weight which have the maximal
61 /// value total, using an already defined good solution.
62 void CKnapsackSolver::optimize(float wMax
, Algorithm algorithm
)
64 H_AUTO(CKnapsackSolver_optimize
);
69 case FullAddCheck
: optimizeFullAddCheck(); break;
70 case AddCheck
: optimizeAddCheck(); break;
71 case FastAddCheck
: optimizeFastAddCheck(); break;
72 case FullSingleReplace
: optimizeFullSingleReplace(); break;
73 case SingleReplace
: optimizeSingleReplace(); break;
75 case FastSingleReplace
: optimizeFastSingleReplace(); break;
76 case VeryFastSingleReplace
: optimizeVeryFastSingleReplace(); break;
77 case Optimal
: optimizeOptimal(); break;
78 case TakeAll
: optimizeTakeAll(); break;
82 /// Algorithm is taken from http://eleves.ensmp.fr/P00/00rouaul/sacados/sacados_swp.html
83 /// This algorithm complexity is O(N^2)
84 void CKnapsackSolver::optimizeOptimal()
86 H_AUTO(CKnapsackSolver_optimizeOptimal
);
87 // :FIXME: Not thread safe
88 // Allocate temporary solution
89 bool* take
= new bool[size()];
90 for (size_t i
=0; i
<size(); ++i
)
92 // Run the optimization recursion
93 optimizeOptimalRec((int)size()-1, _WMax
, 0, take
);
94 // Delete temporary solution
98 /// @param i take[j] for j>i are already determined by the recursion
99 /// @param w Free weight to fill
100 /// @param v Sum of the taken values (ie all value(j) where take[j] is true and j > i)
101 /// @param take Current examined partial solution
102 void CKnapsackSolver::optimizeOptimalRec(int i
, float w
, float v
, bool* take
)
111 std::copy(take
, take
+size(), _Take
);
117 optimizeOptimalRec(i
-1, w
, v
, take
);
121 optimizeOptimalRec(i
- 1, w
- weight(i
), v
+ value(i
), take
);
126 /// Here we consider already defined solution has lots of take[i] that are
127 /// true. We just find the first i for which take[i] is false and that don't
128 /// exceed maximal weight. If we find one we take it which gives a better
130 /// Note: We start at the end since CTargetable puts candidate at end.
131 /// This algorithm complexity is O(N)
132 void CKnapsackSolver::optimizeAddCheck()
134 H_AUTO(CKnapsackSolver_optimizeAddCheck
);
135 int i
= (int)size()-1;
136 float w
= _WMax
- _WBest
;
139 if (!_Take
[i
] && weight(i
) <= w
)
150 /// Same as AddCheck, except that we consider all false take[i], even if we already took some.
151 /// This algorithm complexity is Theta(N)
152 void CKnapsackSolver::optimizeFullAddCheck()
154 H_AUTO(CKnapsackSolver_optimizeFullAddCheck
);
155 int i
= (int)size()-1;
156 float w
= _WMax
- _WBest
;
159 if (!_Take
[i
] && weight(i
) <= w
)
169 /// Same as AddCheck, except that we examine only a single false take[i], event if it cannot be taken.
170 /// This algorithm complexity is O(N), but O(1) when used by CTargetable
171 void CKnapsackSolver::optimizeFastAddCheck()
173 H_AUTO(CKnapsackSolver_optimizeFastAddCheck
);
174 int i
= (int)size()-1;
175 float w
= _WMax
- _WBest
;
192 /// First try FullAddCheck, then try to replace the already taken elements with not taken ones.
193 /// This algorithm complexity is Theta(N^2)
194 void CKnapsackSolver::optimizeFullSingleReplace()
196 optimizeFullAddCheck();
197 H_AUTO(CKnapsackSolver_optimizeFullSingleReplace
);
198 int i
= (int)size()-1;
201 // For each not taken ith element
207 // Find the worst element that ith element can replace
208 int j
= (int)size()-1;
211 if (i
!=j
&& _Take
[j
] && w
<=weight(j
) && v
>value(j
))
219 // If we find one untake it and take ith.
222 _Take
[worst
] = false;
223 _WBest
-= weight(worst
);
224 _VBest
-= value(worst
);
234 /// First try FastAddCheck, and if it fails optimizing try to replace a not
235 /// taken one with an already taken element (the worst one) until a
236 /// replacement occurs.
237 /// This algorithm complexity is Theta(N^2) and O(N^2) for CTargetable
238 void CKnapsackSolver::optimizeSingleReplace()
240 float vBest
= _VBest
;
244 H_AUTO(CKnapsackSolver_optimizeSingleReplace
);
245 int i
= (int)size()-1;
248 // For each not taken ith element
254 // Find the worst element that ith element can replace
255 int j
= (int)size()-1;
258 if (i
!=j
&& _Take
[j
] && w
<=weight(j
) && v
>value(j
))
266 // If we find one untake it and take ith.
269 _Take
[worst
] = false;
270 _WBest
-= weight(worst
);
271 _VBest
-= value(worst
);
282 /// First try FastAddCheck, and if it fails optimizing try to replace the
283 /// first not taken element with an already taken element (the worst one).
284 /// This algorithm complexity is O(N^2) and Theta(N) for CTargetable
285 void CKnapsackSolver::optimizeFastSingleReplace()
287 float vBest
= _VBest
;
288 optimizeFastAddCheck();
291 H_AUTO(CKnapsackSolver_optimizeFastSingleReplace
);
292 int i
= (int)size()-1;
295 // For each not taken ith element
301 // Find the worst element that ith element can replace
302 int j
= (int)size()-1;
305 if (i
!=j
&& _Take
[j
] && w
<=weight(j
) && v
>value(j
))
313 // If we find one untake it and take ith.
316 _Take
[worst
] = false;
317 _WBest
-= weight(worst
);
318 _VBest
-= value(worst
);
329 /// First try FastAddCheck, and if it fails optimizing try to replace the
330 /// first not taken element with an already taken one (the first worst that
331 /// the not taken one).
332 /// This algorithm complexity is O(N^2) and O(N) for CTargetable
333 void CKnapsackSolver::optimizeVeryFastSingleReplace()
335 float vBest
= _VBest
;
336 optimizeFastAddCheck();
339 H_AUTO(CKnapsackSolver_optimizeVeryFastSingleReplace
);
340 int i
= (int)size()-1;
343 // For each not taken ith element
349 // Find the worst element that ith element can replace
350 int j
= (int)size()-1;
353 if (i
!=j
&& _Take
[j
] && w
<=weight(j
) && v
>value(j
))
362 // If we find one untake it and take ith.
365 _Take
[worst
] = false;
366 _WBest
-= weight(worst
);
367 _VBest
-= value(worst
);
378 void CKnapsackSolver::optimizeTakeAll()
380 H_AUTO(CKnapsackSolver_optimizeTakeAll
);
383 int i
= (int)size()-1;
393 CKnapsackSolver::CKnapsackSolver(IKnapsackContext
* context
, bool* _take
)
396 , _AllocatedTake(_take
==NULL
)
401 if (_take
==NULL
&& size()!=0)
404 _Take
= new bool[sz
];
405 for (size_t i
= 0; i
< sz
; ++i
)
410 for (size_t i
=0; i
<size(); ++i
)
422 CKnapsackSolver::~CKnapsackSolver()
428 float CKnapsackSolver::weight(size_t i
)
431 return _Context
->weight(i
);
436 float CKnapsackSolver::value(size_t i
)
439 return _Context
->value(i
);
444 size_t CKnapsackSolver::size()
447 return _Context
->size();
452 bool CKnapsackSolver::take(size_t i
)
454 if (_Take
!=0 && i
>=0 && i
<size())
460 float CKnapsackSolver::totalWeight()
465 float CKnapsackSolver::totalValue()
470 float CKnapsackSolver::totalFreeWeight()
472 return _WMax
- _WBest
;
475 //////////////////////////////////////////////////////////////////////////////
476 // CKnapsackContext //
477 //////////////////////////////////////////////////////////////////////////////
479 CKnapsackContext::CKnapsackContext(size_t size
, float* weights
, float* values
)
486 float CKnapsackContext::weight(size_t i
)
494 float CKnapsackContext::value(size_t i
)
502 size_t CKnapsackContext::size()