00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "algorithms/knapsack_solver.h"
00015
00016 #include <algorithm>
00017 #include <queue>
00018 #include <string>
00019 #include <vector>
00020
00021 #include "base/concise_iterator.h"
00022 #include "base/stl_util.h"
00023 #include "linear_solver/linear_solver.h"
00024 #include "util/bitset.h"
00025
00026 namespace operations_research {
00027
00028 namespace {
00029 const int kNoSelection = -1;
00030 const int kMasterPropagatorId = 0;
00031 const int kMaxNumberOfBruteForceItems = 30;
00032 const int kMaxNumberOf64Items = 64;
00033
00034
00035
00036 struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
00037 explicit CompareKnapsackItemsInDecreasingEfficiencyOrder(int64 _profit_max)
00038 : profit_max(_profit_max) {
00039 }
00040 bool operator() (const KnapsackItemPtr& item1,
00041 const KnapsackItemPtr& item2) const {
00042 return item1->GetEfficiency(profit_max) > item2->GetEfficiency(profit_max);
00043 }
00044 const int64 profit_max;
00045 };
00046
00047
00048
00049
00050
00051
00052 struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
00053 bool operator()(const KnapsackSearchNode* node_1,
00054 const KnapsackSearchNode* node_2) const {
00055 const int64 profit_upper_bound_1 = node_1->profit_upper_bound();
00056 const int64 profit_upper_bound_2 = node_2->profit_upper_bound();
00057 if (profit_upper_bound_1 == profit_upper_bound_2) {
00058 return node_1->current_profit() < node_2->current_profit();
00059 }
00060 return profit_upper_bound_1 < profit_upper_bound_2;
00061 }
00062 };
00063
00064 typedef std::priority_queue<
00065 KnapsackSearchNode*,
00066 std::vector<KnapsackSearchNode*>,
00067 CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder> SearchQueue;
00068
00069
00070 inline bool WillProductOverflow(int64 value_1, int64 value_2) {
00071 const int MostSignificantBitPosition1 = MostSignificantBitPosition64(value_1);
00072 const int MostSignificantBitPosition2 = MostSignificantBitPosition64(value_2);
00073
00074
00075 const int kOverflow = 61;
00076 return MostSignificantBitPosition1 + MostSignificantBitPosition2 > kOverflow;
00077 }
00078
00079
00080 int64 UpperBoundOfRatio(int64 numerator_1,
00081 int64 numerator_2,
00082 int64 denominator) {
00083 DCHECK_GT(denominator, 0LL);
00084 if (!WillProductOverflow(numerator_1, numerator_2)) {
00085 const int64 numerator = numerator_1 * numerator_2;
00086
00087 const int64 result = numerator / denominator;
00088 return result;
00089 } else {
00090 const double ratio = (static_cast<double>(numerator_1)
00091 * static_cast<double>(numerator_2))
00092 / static_cast<double>(denominator);
00093
00094 const int64 result = static_cast<int64>(floor(ratio + 0.5));
00095 return result;
00096 }
00097 }
00098
00099 }
00100
00101
00102 KnapsackSearchNode::KnapsackSearchNode(const KnapsackSearchNode* const parent,
00103 const KnapsackAssignment& assignment)
00104 : depth_((parent == NULL) ? 0 : parent->depth() + 1),
00105 parent_(parent),
00106 assignment_(assignment),
00107 current_profit_(0),
00108 profit_upper_bound_(kint64max),
00109 next_item_id_(kNoSelection) {
00110 }
00111
00112
00113 KnapsackSearchPath::KnapsackSearchPath(const KnapsackSearchNode& from,
00114 const KnapsackSearchNode& to)
00115 : from_(from),
00116 via_(NULL),
00117 to_(to) {
00118 }
00119
00120 void KnapsackSearchPath::Init() {
00121 const KnapsackSearchNode* node_from = MoveUpToDepth(from_, to_.depth());
00122 const KnapsackSearchNode* node_to = MoveUpToDepth(to_, from_.depth());
00123 CHECK_EQ(node_from->depth(), node_to->depth());
00124
00125
00126 while (node_from != node_to) {
00127 node_from = node_from->parent();
00128 node_to = node_to->parent();
00129 }
00130 via_ = node_from;
00131 }
00132
00133 const KnapsackSearchNode* KnapsackSearchPath::MoveUpToDepth(
00134 const KnapsackSearchNode& node,
00135 int depth) const {
00136 const KnapsackSearchNode* current_node = &node;
00137 while (current_node->depth() > depth) {
00138 current_node = current_node->parent();
00139 }
00140 return current_node;
00141 }
00142
00143
00144 KnapsackState::KnapsackState() : is_bound_(), is_in_() {}
00145
00146 void KnapsackState::Init(int number_of_items) {
00147 is_bound_.assign(number_of_items, false);
00148 is_in_.assign(number_of_items, false);
00149 }
00150
00151
00152 bool KnapsackState::UpdateState(bool revert,
00153 const KnapsackAssignment& assignment) {
00154 if (revert) {
00155 is_bound_[assignment.item_id] = false;
00156 } else {
00157 if (is_bound_[assignment.item_id]
00158 && is_in_[assignment.item_id] != assignment.is_in) {
00159 return false;
00160 }
00161 is_bound_[assignment.item_id] = true;
00162 is_in_[assignment.item_id] = assignment.is_in;
00163 }
00164 return true;
00165 }
00166
00167
00168 KnapsackPropagator::KnapsackPropagator(const KnapsackState& state)
00169 : items_(),
00170 current_profit_(0),
00171 profit_lower_bound_(0),
00172 profit_upper_bound_(kint64max),
00173 state_(state) {
00174 }
00175
00176 KnapsackPropagator::~KnapsackPropagator() {
00177 STLDeleteElements(&items_);
00178 }
00179
00180 void KnapsackPropagator::Init(const std::vector<int64>& profits,
00181 const std::vector<int64>& weights) {
00182 const int number_of_items = profits.size();
00183 items_.assign(number_of_items, static_cast<KnapsackItemPtr>(NULL));
00184 for (int i = 0; i < number_of_items; ++i) {
00185 items_[i] = new KnapsackItem(i, weights[i], profits[i]);
00186 }
00187 current_profit_ = 0;
00188 profit_lower_bound_ = kint64min;
00189 profit_upper_bound_ = kint64max;
00190 InitPropagator();
00191 }
00192
00193 bool KnapsackPropagator::Update(bool revert,
00194 const KnapsackAssignment& assignment) {
00195 if (assignment.is_in) {
00196 if (revert) {
00197 current_profit_ -= items_[assignment.item_id]->profit;
00198 } else {
00199 current_profit_ += items_[assignment.item_id]->profit;
00200 }
00201 }
00202 return UpdatePropagator(revert, assignment);
00203 }
00204
00205 void KnapsackPropagator::CopyCurrentStateToSolution(
00206 bool has_one_propagator,
00207 std::vector<bool>* solution) const {
00208 CHECK_NOTNULL(solution);
00209 for (ConstIter<std::vector<KnapsackItemPtr> > it(items_); !it.at_end(); ++it) {
00210 const KnapsackItem* const item = *it;
00211 const int item_id = item->id;
00212 (*solution)[item_id] = state_.is_bound(item_id) && state_.is_in(item_id);
00213 }
00214 if (has_one_propagator) {
00215 CopyCurrentStateToSolutionPropagator(solution);
00216 }
00217 }
00218
00219
00220
00221 KnapsackCapacityPropagator::KnapsackCapacityPropagator(
00222 const KnapsackState& state,
00223 int64 capacity)
00224 : KnapsackPropagator(state),
00225 capacity_(capacity),
00226 consumed_capacity_(0),
00227 break_item_id_(kNoSelection),
00228 sorted_items_(),
00229 profit_max_(0) {
00230 }
00231
00232 KnapsackCapacityPropagator::~KnapsackCapacityPropagator() {
00233 }
00234
00235
00236
00237 void KnapsackCapacityPropagator::ComputeProfitBounds() {
00238 set_profit_lower_bound(current_profit());
00239 break_item_id_ = kNoSelection;
00240
00241 int64 remaining_capacity = capacity_ - consumed_capacity_;
00242 int break_sorted_item_id = kNoSelection;
00243 const int number_of_sorted_items = sorted_items_.size();
00244 for (int sorted_id = 0; sorted_id < number_of_sorted_items; ++sorted_id) {
00245 const KnapsackItem* const item = sorted_items_[sorted_id];
00246 if (!state().is_bound(item->id)) {
00247 break_item_id_ = item->id;
00248
00249 if (remaining_capacity >= item->weight) {
00250 remaining_capacity -= item->weight;
00251 set_profit_lower_bound(profit_lower_bound() + item->profit);
00252 } else {
00253 break_sorted_item_id = sorted_id;
00254 break;
00255 }
00256 }
00257 }
00258
00259 set_profit_upper_bound(profit_lower_bound());
00260
00261 if (break_sorted_item_id != kNoSelection) {
00262 const int64 additional_profit = GetAdditionalProfit(remaining_capacity,
00263 break_sorted_item_id);
00264 set_profit_upper_bound(profit_upper_bound() + additional_profit);
00265 }
00266 }
00267
00268 void KnapsackCapacityPropagator::InitPropagator() {
00269 consumed_capacity_ = 0;
00270 break_item_id_ = kNoSelection;
00271 sorted_items_ = items();
00272 profit_max_ = 0;
00273 for (ConstIter<std::vector<KnapsackItemPtr> > it(sorted_items_);
00274 !it.at_end(); ++it) {
00275 profit_max_ = std::max(profit_max_, (*it)->profit);
00276 }
00277 ++profit_max_;
00278 CompareKnapsackItemsInDecreasingEfficiencyOrder compare_object(profit_max_);
00279 sort(sorted_items_.begin(), sorted_items_.end(), compare_object);
00280 }
00281
00282
00283 bool KnapsackCapacityPropagator::UpdatePropagator(
00284 bool revert,
00285 const KnapsackAssignment& assignment) {
00286 if (assignment.is_in) {
00287 if (revert) {
00288 consumed_capacity_ -= items()[assignment.item_id]->weight;
00289 } else {
00290 consumed_capacity_ += items()[assignment.item_id]->weight;
00291 if (consumed_capacity_ > capacity_) {
00292 return false;
00293 }
00294 }
00295 }
00296 return true;
00297 }
00298
00299 void KnapsackCapacityPropagator::CopyCurrentStateToSolutionPropagator(
00300 std::vector<bool>* solution) const {
00301 CHECK_NOTNULL(solution);
00302 int64 remaining_capacity = capacity_ - consumed_capacity_;
00303 for (ConstIter<std::vector<KnapsackItemPtr> > it(sorted_items_);
00304 !it.at_end(); ++it) {
00305 const KnapsackItem* const item = *it;
00306 if (!state().is_bound(item->id)) {
00307 if (remaining_capacity >= item->weight) {
00308 remaining_capacity -= item->weight;
00309 (*solution)[item->id] = true;
00310 } else {
00311 return;
00312 }
00313 }
00314 }
00315 }
00316
00317 int64 KnapsackCapacityPropagator::GetAdditionalProfit(int64 remaining_capacity,
00318 int break_item_id) const {
00319 const int after_break_item_id = break_item_id + 1;
00320 int64 additional_profit_when_no_break_item = 0;
00321 if (after_break_item_id < sorted_items_.size()) {
00322
00323
00324 const int64 next_weight = sorted_items_[after_break_item_id]->weight;
00325 const int64 next_profit = sorted_items_[after_break_item_id]->profit;
00326 additional_profit_when_no_break_item = UpperBoundOfRatio(
00327 remaining_capacity, next_profit, next_weight);
00328 }
00329
00330 const int before_break_item_id = break_item_id - 1;
00331 int64 additional_profit_when_break_item = 0;
00332 if (before_break_item_id >= 0) {
00333 const int64 previous_weight = sorted_items_[before_break_item_id]->weight;
00334
00335
00336
00337 if (previous_weight != 0) {
00338 const int64 previous_profit = sorted_items_[before_break_item_id]->profit;
00339 const int64 overused_capacity = sorted_items_[break_item_id]->weight
00340 - remaining_capacity;
00341 const int64 ratio = UpperBoundOfRatio(
00342 overused_capacity, previous_profit, previous_weight);
00343 additional_profit_when_break_item = sorted_items_[break_item_id]->profit
00344 - ratio;
00345 }
00346 }
00347
00348 const int64 additional_profit = std::max(additional_profit_when_no_break_item,
00349 additional_profit_when_break_item);
00350 CHECK_GE(additional_profit, 0);
00351 return additional_profit;
00352 }
00353
00354
00355 KnapsackGenericSolver::KnapsackGenericSolver(const string& solver_name)
00356 : BaseKnapsackSolver(solver_name),
00357 propagators_(),
00358 master_propagator_id_(kMasterPropagatorId),
00359 search_nodes_(),
00360 state_(),
00361 best_solution_profit_(0),
00362 best_solution_() {
00363 }
00364
00365 KnapsackGenericSolver::~KnapsackGenericSolver() {
00366 Clear();
00367 }
00368
00369 void KnapsackGenericSolver::Init(const std::vector<int64>& profits,
00370 const std::vector<std::vector<int64> >& weights,
00371 const std::vector<int64>& capacities) {
00372 CHECK_EQ(capacities.size(), weights.size());
00373
00374 Clear();
00375 const int number_of_items = profits.size();
00376 const int number_of_dimensions = weights.size();
00377 state_.Init(number_of_items);
00378 best_solution_.assign(number_of_items, false);
00379 for (int i = 0; i < number_of_dimensions; ++i) {
00380 CHECK_EQ(number_of_items, weights[i].size());
00381
00382 KnapsackCapacityPropagator* propagator =
00383 new KnapsackCapacityPropagator(state_, capacities[i]);
00384 propagator->Init(profits, weights[i]);
00385 propagators_.push_back(propagator);
00386 }
00387 master_propagator_id_ = kMasterPropagatorId;
00388 }
00389
00390 void KnapsackGenericSolver::GetLowerAndUpperBoundWhenItem(int item_id,
00391 bool is_item_in,
00392 int64* lower_bound,
00393 int64* upper_bound) {
00394 CHECK_NOTNULL(lower_bound);
00395 CHECK_NOTNULL(upper_bound);
00396 KnapsackAssignment assignment(item_id, is_item_in);
00397 const bool fail = !IncrementalUpdate(false, assignment);
00398 if (fail) {
00399 *lower_bound = 0LL;
00400 *upper_bound = 0LL;
00401 } else {
00402 *lower_bound = (HasOnePropagator()) ?
00403 propagators_[master_propagator_id_]->profit_lower_bound() : 0LL;
00404 *upper_bound = GetAggregatedProfitUpperBound();
00405 }
00406
00407 const bool fail_revert = !IncrementalUpdate(true, assignment);
00408 if (fail_revert) {
00409 *lower_bound = 0LL;
00410 *upper_bound = 0LL;
00411 }
00412 }
00413
00414 int64 KnapsackGenericSolver::Solve() {
00415 best_solution_profit_ = 0LL;
00416
00417 SearchQueue search_queue;
00418 const KnapsackAssignment assignment(kNoSelection, true);
00419 KnapsackSearchNode* root_node = new KnapsackSearchNode(NULL, assignment);
00420 root_node->set_current_profit(GetCurrentProfit());
00421 root_node->set_profit_upper_bound(GetAggregatedProfitUpperBound());
00422 root_node->set_next_item_id(GetNextItemId());
00423 search_nodes_.push_back(root_node);
00424
00425 if (MakeNewNode(*root_node, false)) {
00426 search_queue.push(search_nodes_.back());
00427 }
00428 if (MakeNewNode(*root_node, true)) {
00429 search_queue.push(search_nodes_.back());
00430 }
00431
00432 KnapsackSearchNode* current_node = root_node;
00433 while (!search_queue.empty() &&
00434 search_queue.top()->profit_upper_bound() > best_solution_profit_) {
00435 KnapsackSearchNode* const node = search_queue.top();
00436 search_queue.pop();
00437
00438 if (node != current_node) {
00439 KnapsackSearchPath path(*current_node, *node);
00440 path.Init();
00441 const bool no_fail = UpdatePropagators(path);
00442 current_node = node;
00443 CHECK_EQ(no_fail, true);
00444 }
00445
00446 if (MakeNewNode(*node, false)) {
00447 search_queue.push(search_nodes_.back());
00448 }
00449 if (MakeNewNode(*node, true)) {
00450 search_queue.push(search_nodes_.back());
00451 }
00452 }
00453 return best_solution_profit_;
00454 }
00455
00456 void KnapsackGenericSolver::Clear() {
00457 STLDeleteElements(&propagators_);
00458 STLDeleteElements(&search_nodes_);
00459 }
00460
00461
00462 bool KnapsackGenericSolver::UpdatePropagators(const KnapsackSearchPath& path) {
00463 bool no_fail = true;
00464
00465 const KnapsackSearchNode* node = &path.from();
00466 const KnapsackSearchNode* via = &path.via();
00467 while (node != via) {
00468 no_fail = IncrementalUpdate(true, node->assignment()) && no_fail;
00469 node = node->parent();
00470 }
00471
00472 node = &path.to();
00473 while (node != via) {
00474 no_fail = IncrementalUpdate(false, node->assignment()) && no_fail;
00475 node = node->parent();
00476 }
00477 return no_fail;
00478 }
00479
00480 int64 KnapsackGenericSolver::GetAggregatedProfitUpperBound() const {
00481 int64 upper_bound = kint64max;
00482 for (ConstIter<std::vector<KnapsackPropagator*> > it(propagators_);
00483 !it.at_end(); ++it) {
00484 (*it)->ComputeProfitBounds();
00485 const int64 propagator_upper_bound = (*it)->profit_upper_bound();
00486 upper_bound = std::min(upper_bound, propagator_upper_bound);
00487 }
00488 return upper_bound;
00489 }
00490
00491 bool KnapsackGenericSolver::MakeNewNode(const KnapsackSearchNode& node,
00492 bool is_in) {
00493 if (node.next_item_id() == kNoSelection) {
00494 return false;
00495 }
00496 KnapsackAssignment assignment(node.next_item_id(), is_in);
00497 KnapsackSearchNode new_node(&node, assignment);
00498
00499 KnapsackSearchPath path(node, new_node);
00500 path.Init();
00501 const bool no_fail = UpdatePropagators(path);
00502 if (no_fail) {
00503 new_node.set_current_profit(GetCurrentProfit());
00504 new_node.set_profit_upper_bound(GetAggregatedProfitUpperBound());
00505 new_node.set_next_item_id(GetNextItemId());
00506 UpdateBestSolution();
00507 }
00508
00509
00510 KnapsackSearchPath revert_path(new_node, node);
00511 revert_path.Init();
00512 UpdatePropagators(revert_path);
00513
00514 if (!no_fail || new_node.profit_upper_bound() < best_solution_profit_) {
00515 return false;
00516 }
00517
00518
00519 KnapsackSearchNode* relevant_node = new KnapsackSearchNode(&node, assignment);
00520 relevant_node->set_current_profit(new_node.current_profit());
00521 relevant_node->set_profit_upper_bound(new_node.profit_upper_bound());
00522 relevant_node->set_next_item_id(new_node.next_item_id());
00523 search_nodes_.push_back(relevant_node);
00524
00525 return true;
00526 }
00527
00528 bool KnapsackGenericSolver::IncrementalUpdate(
00529 bool revert,
00530 const KnapsackAssignment& assignment) {
00531
00532
00533 bool no_fail = state_.UpdateState(revert, assignment);
00534 for (ConstIter<std::vector<KnapsackPropagator*> > it(propagators_);
00535 !it.at_end(); ++it) {
00536 no_fail = (*it)->Update(revert, assignment) && no_fail;
00537 }
00538 return no_fail;
00539 }
00540
00541 void KnapsackGenericSolver::UpdateBestSolution() {
00542 const int64 profit_lower_bound = (HasOnePropagator()) ?
00543 propagators_[master_propagator_id_]->profit_lower_bound() :
00544 propagators_[master_propagator_id_]->current_profit();
00545
00546 if (best_solution_profit_ < profit_lower_bound) {
00547 best_solution_profit_ = profit_lower_bound;
00548 propagators_[master_propagator_id_]->CopyCurrentStateToSolution(
00549 HasOnePropagator(),
00550 &best_solution_);
00551 }
00552 }
00553
00554
00555
00556
00557
00558
00559 class KnapsackBruteForceSolver : public BaseKnapsackSolver {
00560 public:
00561 explicit KnapsackBruteForceSolver(const string& solver_name);
00562
00563
00564 void Init(const std::vector<int64>& profits,
00565 const std::vector<std::vector<int64> >& weights,
00566 const std::vector<int64>& capacities);
00567
00568
00569 int64 Solve();
00570
00571
00572 bool best_solution(int item_id) const {
00573 return (best_solution_ & OneBit32(item_id)) != 0U;
00574 }
00575
00576 private:
00577 int num_items_;
00578 int64 profits_weights_[kMaxNumberOfBruteForceItems * 2];
00579 int64 capacity_;
00580 int64 best_solution_profit_;
00581 uint32 best_solution_;
00582
00583 DISALLOW_COPY_AND_ASSIGN(KnapsackBruteForceSolver);
00584 };
00585
00586 KnapsackBruteForceSolver::KnapsackBruteForceSolver(const string& solver_name)
00587 : BaseKnapsackSolver(solver_name),
00588 num_items_(0),
00589 capacity_(0LL),
00590 best_solution_profit_(0LL),
00591 best_solution_(0U) {
00592 }
00593
00594 void KnapsackBruteForceSolver::Init(const std::vector<int64>& profits,
00595 const std::vector<std::vector<int64> >& weights,
00596 const std::vector<int64>& capacities) {
00597
00598 CHECK_EQ(weights.size(), 1)
00599 << "Brute force solver only works with one dimension.";
00600 CHECK_EQ(capacities.size(), weights.size());
00601
00602 num_items_ = profits.size();
00603 CHECK_EQ(num_items_, weights.at(0).size());
00604 CHECK_LE(num_items_, kMaxNumberOfBruteForceItems)
00605 << "To use KnapsackBruteForceSolver the number of items should be "
00606 << "less than " << kMaxNumberOfBruteForceItems
00607 << ". Current value: " << num_items_ << ".";
00608
00609 for (int i = 0; i < num_items_; ++i) {
00610 profits_weights_[i * 2] = profits.at(i);
00611 profits_weights_[i * 2 + 1] = weights.at(0).at(i);
00612 }
00613 capacity_ = capacities.at(0);
00614 }
00615
00616 int64 KnapsackBruteForceSolver::Solve() {
00617 best_solution_profit_ = 0LL;
00618 best_solution_ = 0U;
00619
00620 const uint32 num_states = OneBit32(num_items_);
00621 uint32 prev_state = 0U;
00622 uint64 sum_profit = 0ULL;
00623 uint64 sum_weight = 0ULL;
00624 uint32 diff_state = 0U;
00625 uint32 local_state = 0U;
00626 int item_id = 0;
00627
00628
00629 for (uint32 state = 1U ; state < num_states; ++state, ++prev_state) {
00630 diff_state = state ^ prev_state;
00631 local_state = state;
00632 item_id = 0;
00633 while (diff_state) {
00634 if (diff_state & 1U) {
00635 if (local_state & 1U) {
00636 sum_profit += profits_weights_[item_id];
00637 sum_weight += profits_weights_[item_id + 1];
00638 CHECK_LT(item_id +1 , 2 * num_items_);
00639 } else {
00640 sum_profit -= profits_weights_[item_id];
00641 sum_weight -= profits_weights_[item_id + 1];
00642 CHECK_LT(item_id + 1 , 2 * num_items_);
00643 }
00644 }
00645 item_id += 2;
00646 local_state = local_state >> 1;
00647 diff_state = diff_state >> 1;
00648 }
00649
00650 if (sum_weight <= capacity_ && best_solution_profit_ < sum_profit) {
00651 best_solution_profit_ = sum_profit;
00652 best_solution_ = state;
00653 }
00654 }
00655
00656 return best_solution_profit_;
00657 }
00658
00659
00660
00661
00662
00663
00664
00665 struct KnapsackItemWithEfficiency {
00666 KnapsackItemWithEfficiency(int _id,
00667 int64 _profit,
00668 int64 _weight,
00669 int64 _profit_max)
00670 : id(_id),
00671 profit(_profit),
00672 weight(_weight),
00673 efficiency((weight > 0) ?
00674 static_cast<double>(_profit) / static_cast<double>(_weight) :
00675 static_cast<double>(_profit_max)) {
00676 }
00677
00678 int id;
00679 int64 profit;
00680 int64 weight;
00681 double efficiency;
00682 };
00683
00684
00685
00686
00687
00688 class Knapsack64ItemsSolver : public BaseKnapsackSolver {
00689 public:
00690 explicit Knapsack64ItemsSolver(const string& solver_name);
00691
00692
00693 void Init(const std::vector<int64>& profits,
00694 const std::vector<std::vector<int64> >& weights,
00695 const std::vector<int64>& capacities);
00696
00697
00698 int64 Solve();
00699
00700
00701 bool best_solution(int item_id) const {
00702 return (best_solution_ & OneBit64(item_id)) != 0ULL;
00703 }
00704
00705 private:
00706 int GetBreakItemId(int64 capacity) const;
00707 void GetLowerAndUpperBound(int64* lower_bound, int64* upper_bound) const;
00708 void GoToNextState(bool has_failed);
00709 void BuildBestSolution();
00710
00711 std::vector<KnapsackItemWithEfficiency> sorted_items_;
00712 std::vector<int64> sum_profits_;
00713 std::vector<int64> sum_weights_;
00714 int64 capacity_;
00715 uint64 state_;
00716 int state_depth_;
00717
00718 int64 best_solution_profit_;
00719 uint64 best_solution_;
00720 int best_solution_depth_;
00721
00722
00723 int64 state_weight_;
00724
00725 int64 rejected_items_profit_;
00726
00727 int64 rejected_items_weight_;
00728 };
00729
00730
00731 bool CompareKnapsackItemWithEfficiencyInDecreasingEfficiencyOrder(
00732 const KnapsackItemWithEfficiency& item1,
00733 const KnapsackItemWithEfficiency& item2) {
00734 return item1.efficiency > item2.efficiency;
00735 }
00736
00737
00738 Knapsack64ItemsSolver::Knapsack64ItemsSolver(const string& solver_name)
00739 : BaseKnapsackSolver(solver_name),
00740 sorted_items_(),
00741 sum_profits_(),
00742 sum_weights_(),
00743 capacity_(0LL),
00744 state_(0ULL),
00745 state_depth_(0),
00746 best_solution_profit_(0LL),
00747 best_solution_(0ULL),
00748 best_solution_depth_(0),
00749 state_weight_(0LL),
00750 rejected_items_profit_(0LL),
00751 rejected_items_weight_(0LL) {
00752 }
00753
00754 void Knapsack64ItemsSolver::Init(const std::vector<int64>& profits,
00755 const std::vector<std::vector<int64> >& weights,
00756 const std::vector<int64>& capacities) {
00757 CHECK_EQ(weights.size(), 1)
00758 << "Brute force solver only works with one dimension.";
00759 CHECK_EQ(capacities.size(), weights.size());
00760
00761 sorted_items_.clear();
00762 sum_profits_.clear();
00763 sum_weights_.clear();
00764
00765 capacity_ = capacities[0];
00766 const int num_items = profits.size();
00767 CHECK_LE(num_items, kMaxNumberOf64Items)
00768 << "To use Knapsack64ItemsSolver the number of items should be "
00769 << "less than " << kMaxNumberOf64Items
00770 << ". Current value: " << num_items << ".";
00771 int64 profit_max = *max_element(profits.begin(), profits.end());
00772
00773 for (int i = 0; i < num_items; ++i) {
00774 sorted_items_.push_back(KnapsackItemWithEfficiency(i,
00775 profits[i],
00776 weights[0][i],
00777 profit_max));
00778 }
00779
00780 sort(sorted_items_.begin(), sorted_items_.end(),
00781 CompareKnapsackItemWithEfficiencyInDecreasingEfficiencyOrder);
00782
00783 int64 sum_profit = 0;
00784 int64 sum_weight = 0;
00785 sum_profits_.push_back(sum_profit);
00786 sum_weights_.push_back(sum_weight);
00787 for (int i = 0; i < num_items; ++i) {
00788 sum_profit += sorted_items_[i].profit;
00789 sum_weight += sorted_items_[i].weight;
00790
00791 sum_profits_.push_back(sum_profit);
00792 sum_weights_.push_back(sum_weight);
00793 }
00794 }
00795
00796 int64 Knapsack64ItemsSolver::Solve() {
00797 const int num_items = sorted_items_.size();
00798 state_ = 1ULL;
00799 state_depth_ = 0;
00800 state_weight_ = sorted_items_[0].weight;
00801 rejected_items_profit_ = 0LL;
00802 rejected_items_weight_ = 0LL;
00803 best_solution_profit_ = 0LL;
00804 best_solution_ = 0ULL;
00805 best_solution_depth_ = 0;
00806
00807 int64 lower_bound = 0LL;
00808 int64 upper_bound = 0LL;
00809 bool fail = false;
00810 while (state_depth_ >= 0) {
00811 fail = false;
00812 if (state_weight_ > capacity_ || state_depth_ >= num_items) {
00813 fail = true;
00814 } else {
00815 GetLowerAndUpperBound(&lower_bound, &upper_bound);
00816 if (best_solution_profit_ < lower_bound) {
00817 best_solution_profit_ = lower_bound;
00818 best_solution_ = state_;
00819 best_solution_depth_ = state_depth_;
00820 }
00821 }
00822 fail = fail || best_solution_profit_ >= upper_bound;
00823 GoToNextState(fail);
00824 }
00825
00826 BuildBestSolution();
00827 return best_solution_profit_;
00828 }
00829
00830 int Knapsack64ItemsSolver::GetBreakItemId(int64 capacity) const {
00831 std::vector<int64>::const_iterator binary_search_iterator =
00832 upper_bound(sum_weights_.begin(),
00833 sum_weights_.end(),
00834 capacity);
00835 return static_cast<int>(binary_search_iterator -
00836 sum_weights_.begin()) -1;
00837 }
00838
00839
00840
00841
00842
00843
00844
00845
00846 void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64* lower_bound,
00847 int64* upper_bound) const {
00848 const int64 available_capacity = capacity_ + rejected_items_weight_;
00849 const int break_item_id = GetBreakItemId(available_capacity);
00850 const int num_items = sorted_items_.size();
00851 if (break_item_id >= num_items) {
00852 *lower_bound = sum_profits_[num_items] - rejected_items_profit_;
00853 *upper_bound = *lower_bound;
00854 return;
00855 }
00856
00857 *lower_bound = sum_profits_[break_item_id] - rejected_items_profit_;
00858 *upper_bound = *lower_bound;
00859 const int64 consumed_capacity = sum_weights_[break_item_id];
00860 const int64 remaining_capacity = available_capacity - consumed_capacity;
00861 const double efficiency = sorted_items_[break_item_id].efficiency;
00862 const int64 additional_profit =
00863 static_cast<int64>(remaining_capacity * efficiency);
00864 *upper_bound += additional_profit;
00865 }
00866
00867
00868
00869
00870
00871
00872 void Knapsack64ItemsSolver::GoToNextState(bool has_failed) {
00873 uint64 mask = OneBit64(state_depth_);
00874 if (!has_failed) {
00875 ++state_depth_;
00876 state_ = state_ | (mask << 1);
00877 state_weight_ += sorted_items_[state_depth_].weight;
00878 } else {
00879
00880 while ((state_ & mask) == 0ULL && state_depth_ >= 0) {
00881 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
00882 rejected_items_profit_ -= item.profit;
00883 rejected_items_weight_ -= item.weight;
00884 --state_depth_;
00885 mask = mask >> 1ULL;
00886 }
00887
00888 if (state_ & mask) {
00889 state_ = state_ & ~mask;
00890 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
00891 rejected_items_profit_ += item.profit;
00892 rejected_items_weight_ += item.weight;
00893 state_weight_ -= item.weight;
00894 }
00895 }
00896 }
00897
00898 void Knapsack64ItemsSolver::BuildBestSolution() {
00899 int64 remaining_capacity = capacity_;
00900 int64 check_profit = 0LL;
00901
00902
00903
00904 for (int i = 0; i <= best_solution_depth_; ++i) {
00905 if (best_solution_ & OneBit64(i)) {
00906 remaining_capacity -= sorted_items_[i].weight;
00907 check_profit += sorted_items_[i].profit;
00908 }
00909 }
00910
00911
00912 const int num_items = sorted_items_.size();
00913 for (int i = best_solution_depth_ + 1; i < num_items; ++i) {
00914 int64 weight = sorted_items_[i].weight;
00915 if (remaining_capacity >= weight) {
00916 remaining_capacity -= weight;
00917 check_profit += sorted_items_[i].profit;
00918 best_solution_ = best_solution_ | OneBit64(i);
00919 } else {
00920 best_solution_ = best_solution_ & ~OneBit64(i);
00921 }
00922 }
00923 CHECK_EQ(best_solution_profit_, check_profit);
00924
00925
00926
00927
00928
00929 uint64 tmp_solution = 0ULL;
00930 for (int i = 0; i < num_items; ++i) {
00931 if (best_solution_ & OneBit64(i)) {
00932 const int original_id = sorted_items_[i].id;
00933 tmp_solution = tmp_solution | OneBit64(original_id);
00934 }
00935 }
00936
00937 best_solution_ = tmp_solution;
00938 }
00939
00940
00941
00942
00943
00944
00945
00946
00947 class KnapsackDynamicProgrammingSolver : public BaseKnapsackSolver {
00948 public:
00949 explicit KnapsackDynamicProgrammingSolver(const string& solver_name);
00950
00951
00952 void Init(const std::vector<int64>& profits,
00953 const std::vector<std::vector<int64> >& weights,
00954 const std::vector<int64>& capacities);
00955
00956
00957 int64 Solve();
00958
00959
00960 bool best_solution(int item_id) const {
00961 return best_solution_.at(item_id);
00962 }
00963
00964 private:
00965 int64 SolveSubProblem(int64 capacity, int num_items);
00966
00967 std::vector<int64> profits_;
00968 std::vector<int64> weights_;
00969 int64 capacity_;
00970 std::vector<int64> computed_profits_;
00971 std::vector<int> selected_item_ids_;
00972 std::vector<bool> best_solution_;
00973 };
00974
00975
00976 KnapsackDynamicProgrammingSolver::KnapsackDynamicProgrammingSolver(
00977 const string& solver_name) : BaseKnapsackSolver(solver_name),
00978 profits_(),
00979 weights_(),
00980 capacity_(0),
00981 computed_profits_(),
00982 selected_item_ids_(),
00983 best_solution_() {
00984 }
00985
00986 void KnapsackDynamicProgrammingSolver::Init(
00987 const std::vector<int64>& profits,
00988 const std::vector<std::vector<int64> >& weights,
00989 const std::vector<int64>& capacities) {
00990 CHECK_EQ(weights.size(), 1)
00991 << "Current implementation of the dynamic programming solver only deals"
00992 << " with one dimension.";
00993 CHECK_EQ(capacities.size(), weights.size());
00994
00995 profits_ = profits;
00996 weights_ = weights[0];
00997 capacity_ = capacities[0];
00998 }
00999
01000 int64 KnapsackDynamicProgrammingSolver::SolveSubProblem(int64 capacity,
01001 int num_items) {
01002 const int64 capacity_plus_1 = capacity + 1;
01003 fill_n(selected_item_ids_.begin(), capacity_plus_1, 0);
01004 fill_n(computed_profits_.begin(), capacity_plus_1, 0LL);
01005 for (int item_id = 0; item_id < num_items; ++item_id) {
01006 const int64 item_weight = weights_[item_id];
01007 const int64 item_profit = profits_[item_id];
01008 for (int64 used_capacity = capacity;
01009 used_capacity >= item_weight;
01010 --used_capacity) {
01011 if (computed_profits_[used_capacity - item_weight] + item_profit >
01012 computed_profits_[used_capacity]) {
01013 computed_profits_[used_capacity] =
01014 computed_profits_[used_capacity - item_weight] + item_profit;
01015 selected_item_ids_[used_capacity] = item_id;
01016 }
01017 }
01018 }
01019 return selected_item_ids_.at(capacity);
01020 }
01021
01022 int64 KnapsackDynamicProgrammingSolver::Solve() {
01023 const int64 capacity_plus_1 = capacity_ + 1;
01024 selected_item_ids_.assign(capacity_plus_1, 0);
01025 computed_profits_.assign(capacity_plus_1, 0LL);
01026
01027 int64 remaining_capacity = capacity_;
01028 int num_items = profits_.size();
01029 best_solution_.assign(num_items, false);
01030
01031 while (remaining_capacity > 0 && num_items > 0) {
01032 const int selected_item_id = SolveSubProblem(remaining_capacity, num_items);
01033 remaining_capacity -= weights_[selected_item_id];
01034 num_items = selected_item_id;
01035 if (remaining_capacity >= 0) {
01036 best_solution_[selected_item_id] = true;
01037 }
01038 }
01039
01040 return computed_profits_[capacity_];
01041 }
01042
01043
01044 class KnapsackMIPSolver : public BaseKnapsackSolver {
01045 public:
01046 KnapsackMIPSolver(
01047 MPSolver::OptimizationProblemType problem_type,
01048 const string& solver_name);
01049
01050
01051 void Init(const std::vector<int64>& profits,
01052 const std::vector<std::vector<int64> >& weights,
01053 const std::vector<int64>& capacities);
01054
01055
01056 int64 Solve();
01057
01058
01059 bool best_solution(int item_id) const {
01060 return best_solution_.at(item_id);
01061 }
01062
01063 private:
01064 MPSolver::OptimizationProblemType problem_type_;
01065 std::vector<int64> profits_;
01066 std::vector<std::vector<int64> > weights_;
01067 std::vector<int64> capacities_;
01068 std::vector<bool> best_solution_;
01069 };
01070
01071 KnapsackMIPSolver::KnapsackMIPSolver(
01072 MPSolver::OptimizationProblemType problem_type, const string& solver_name)
01073 : BaseKnapsackSolver(solver_name),
01074 problem_type_(problem_type),
01075 profits_(),
01076 weights_(),
01077 capacities_(),
01078 best_solution_() {
01079 }
01080
01081 void KnapsackMIPSolver::Init(const std::vector<int64>& profits,
01082 const std::vector<std::vector<int64> >& weights,
01083 const std::vector<int64>& capacities) {
01084 profits_ = profits;
01085 weights_ = weights;
01086 capacities_ = capacities;
01087 }
01088
01089 int64 KnapsackMIPSolver::Solve() {
01090 MPSolver solver(GetName(), problem_type_);
01091
01092 const int num_items = profits_.size();
01093 std::vector<MPVariable*> variables;
01094 solver.MakeBoolVarArray(num_items, "x", &variables);
01095
01096
01097 const int num_dimensions = capacities_.size();
01098 for (int i = 0; i < num_dimensions; ++i) {
01099 MPConstraint* const ct =
01100 solver.MakeRowConstraint(0LL, capacities_.at(i));
01101 for (int j = 0; j < num_items; ++j) {
01102 ct->SetCoefficient(variables.at(j), weights_.at(i).at(j));
01103 }
01104 }
01105
01106
01107
01108
01109 for (int j = 0; j < num_items; ++j) {
01110 solver.SetObjectiveCoefficient(variables.at(j), -profits_.at(j));
01111 }
01112
01113 solver.SuppressOutput();
01114 solver.Solve();
01115
01116
01117 const float kRoundNear = 0.5;
01118 best_solution_.assign(num_items, false);
01119 for (int j = 0; j < num_items; ++j) {
01120 const double value = variables.at(j)->solution_value();
01121 best_solution_.at(j) = value >= kRoundNear;
01122 }
01123
01124 return -solver.objective_value() + kRoundNear;
01125 }
01126
01127
01128 KnapsackSolver::KnapsackSolver(const string& solver_name)
01129 : solver_(new KnapsackGenericSolver(solver_name)),
01130 known_value_(),
01131 best_solution_(),
01132 mapping_reduced_item_id_(),
01133 is_problem_solved_(false),
01134 additional_profit_(0LL),
01135 use_reduction_(true) {
01136 }
01137
01138 KnapsackSolver::KnapsackSolver(SolverType solver_type,
01139 const string& solver_name)
01140 : solver_(),
01141 known_value_(),
01142 best_solution_(),
01143 mapping_reduced_item_id_(),
01144 is_problem_solved_(false),
01145 additional_profit_(0LL),
01146 use_reduction_(true) {
01147 switch (solver_type) {
01148 case KNAPSACK_BRUTE_FORCE_SOLVER:
01149 solver_.reset(new KnapsackBruteForceSolver(solver_name));
01150 break;
01151 case KNAPSACK_64ITEMS_SOLVER:
01152 solver_.reset(new Knapsack64ItemsSolver(solver_name));
01153 break;
01154 case KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER:
01155 solver_.reset(new KnapsackDynamicProgrammingSolver(solver_name));
01156 break;
01157 case KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER:
01158 solver_.reset(new KnapsackGenericSolver(solver_name));
01159 break;
01160 #if defined(USE_CBC)
01161 case KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER:
01162 solver_.reset(new KnapsackMIPSolver(
01163 MPSolver::CBC_MIXED_INTEGER_PROGRAMMING,
01164 solver_name));
01165 break;
01166 #endif // USE_CBC
01167 #if defined(USE_GLPK)
01168 case KNAPSACK_MULTIDIMENSION_GLPK_MIP_SOLVER:
01169 solver_.reset(new KnapsackMIPSolver(
01170 MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING,
01171 solver_name));
01172 break;
01173 #endif // USE_GLPK
01174 default:
01175 LOG(FATAL) << "Unknown knapsack solver type.";
01176 }
01177 }
01178
01179 KnapsackSolver::~KnapsackSolver() {
01180 }
01181
01182 void KnapsackSolver::Init(const std::vector<int64>& profits,
01183 const std::vector<std::vector<int64> >& weights,
01184 const std::vector<int64>& capacities) {
01185 additional_profit_ = 0LL;
01186 is_problem_solved_ = false;
01187
01188 solver_->Init(profits, weights, capacities);
01189 if (use_reduction_) {
01190 const int num_items = profits.size();
01191 const int num_reduced_items = ReduceProblem(num_items);
01192
01193 if (num_reduced_items > 0) {
01194 ComputeAdditionalProfit(profits);
01195 }
01196
01197 if (num_reduced_items > 0 && num_reduced_items < num_items) {
01198 InitReducedProblem(profits, weights, capacities);
01199 }
01200 }
01201 }
01202
01203 int KnapsackSolver::ReduceProblem(int num_items) {
01204 known_value_.assign(num_items, false);
01205 best_solution_.assign(num_items, false);
01206 mapping_reduced_item_id_.assign(num_items, 0);
01207 additional_profit_ = 0LL;
01208
01209 for (int item_id = 0; item_id < num_items; ++item_id) {
01210 mapping_reduced_item_id_[item_id] = item_id;
01211 }
01212
01213 int64 best_lower_bound = 0LL;
01214 std::vector<int64> J0_upper_bounds(num_items, kint64max);
01215 std::vector<int64> J1_upper_bounds(num_items, kint64max);
01216 for (int item_id = 0; item_id < num_items; ++item_id) {
01217 int64 lower_bound = 0LL;
01218 int64 upper_bound = kint64max;
01219 solver_->GetLowerAndUpperBoundWhenItem(item_id,
01220 false,
01221 &lower_bound,
01222 &upper_bound);
01223 J1_upper_bounds.at(item_id) = upper_bound;
01224 best_lower_bound = std::max(best_lower_bound, lower_bound);
01225
01226 solver_->GetLowerAndUpperBoundWhenItem(item_id,
01227 true,
01228 &lower_bound,
01229 &upper_bound);
01230 J0_upper_bounds.at(item_id) = upper_bound;
01231 best_lower_bound = std::max(best_lower_bound, lower_bound);
01232 }
01233
01234 int num_reduced_items = 0;
01235 for (int item_id = 0; item_id < num_items; ++item_id) {
01236 if (best_lower_bound > J0_upper_bounds[item_id]) {
01237 known_value_[item_id] = true;
01238 best_solution_[item_id] = false;
01239 ++num_reduced_items;
01240 } else if (best_lower_bound > J1_upper_bounds[item_id]) {
01241 known_value_[item_id] = true;
01242 best_solution_[item_id] = true;
01243 ++num_reduced_items;
01244 }
01245 }
01246
01247 is_problem_solved_ = num_reduced_items == num_items;
01248 return num_reduced_items;
01249 }
01250
01251 void KnapsackSolver::ComputeAdditionalProfit(const std::vector<int64>& profits) {
01252 const int num_items = profits.size();
01253 additional_profit_ = 0LL;
01254 for (int item_id = 0; item_id < num_items; ++item_id) {
01255 if (known_value_[item_id] && best_solution_[item_id]) {
01256 additional_profit_ += profits[item_id];
01257 }
01258 }
01259 }
01260
01261 void KnapsackSolver::InitReducedProblem(const std::vector<int64>& profits,
01262 const std::vector<std::vector<int64> >& weights,
01263 const std::vector<int64>& capacities) {
01264 const int num_items = profits.size();
01265 const int num_dimensions = capacities.size();
01266
01267 std::vector<int64> reduced_profits;
01268 for (int item_id = 0; item_id < num_items; ++item_id) {
01269 if (!known_value_[item_id]) {
01270 mapping_reduced_item_id_[item_id] = reduced_profits.size();
01271 reduced_profits.push_back(profits[item_id]);
01272 }
01273 }
01274
01275 std::vector<std::vector<int64> > reduced_weights;
01276 std::vector<int64> reduced_capacities = capacities;
01277 for (int dim = 0; dim < num_dimensions; ++dim) {
01278 const std::vector<int64>& one_dimension_weights = weights[dim];
01279 std::vector<int64> one_dimension_reduced_weights;
01280 for (int item_id = 0; item_id < num_items; ++item_id) {
01281 if (known_value_[item_id]) {
01282 if (best_solution_[item_id]) {
01283 reduced_capacities[dim] -= one_dimension_weights[item_id];
01284 }
01285 } else {
01286 one_dimension_reduced_weights.push_back(
01287 one_dimension_weights[item_id]);
01288 }
01289 }
01290 reduced_weights.push_back(one_dimension_reduced_weights);
01291 }
01292 solver_->Init(reduced_profits, reduced_weights, reduced_capacities);
01293 }
01294
01295 int64 KnapsackSolver::Solve() {
01296 return additional_profit_ + ((is_problem_solved_) ? 0 : solver_->Solve());
01297 }
01298
01299 bool KnapsackSolver::BestSolutionContains(int item_id) const {
01300 const int mapped_item_id = (use_reduction_) ?
01301 mapping_reduced_item_id_[item_id] : item_id;
01302 return (use_reduction_ && known_value_[item_id]) ? best_solution_[item_id]
01303 : solver_->best_solution(mapped_item_id);
01304 }
01305
01306 string KnapsackSolver::GetName() const {
01307 return solver_->GetName();
01308 }
01309
01310
01311 void BaseKnapsackSolver::GetLowerAndUpperBoundWhenItem(int item_id,
01312 bool is_item_in,
01313 int64* lower_bound,
01314 int64* upper_bound) {
01315 CHECK_NOTNULL(lower_bound);
01316 CHECK_NOTNULL(upper_bound);
01317 *lower_bound = 0LL;
01318 *upper_bound = kint64max;
01319 }
01320
01321 }