32 const int kNoSelection = -1;
33 const int kPrimaryPropagatorId = 0;
34 const int kMaxNumberOfBruteForceItems = 30;
35 const int kMaxNumberOf64Items = 64;
39 struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
40 explicit CompareKnapsackItemsInDecreasingEfficiencyOrder(int64_t _profit_max)
54 struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
55 bool operator()(
const KnapsackSearchNode* node_1,
56 const KnapsackSearchNode* node_2)
const {
57 const int64_t profit_upper_bound_1 = node_1->profit_upper_bound();
58 const int64_t profit_upper_bound_2 = node_2->profit_upper_bound();
59 if (profit_upper_bound_1 == profit_upper_bound_2) {
60 return node_1->current_profit() < node_2->current_profit();
62 return profit_upper_bound_1 < profit_upper_bound_2;
66 typedef std::priority_queue<
67 KnapsackSearchNode*, std::vector<KnapsackSearchNode*>,
68 CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder>
72 inline bool WillProductOverflow(int64_t value_1, int64_t value_2) {
77 const int kOverflow = 61;
78 return MostSignificantBitPosition1 + MostSignificantBitPosition2 > kOverflow;
82 int64_t UpperBoundOfRatio(int64_t numerator_1, int64_t numerator_2,
83 int64_t denominator) {
84 DCHECK_GT(denominator, int64_t{0});
85 if (!WillProductOverflow(numerator_1, numerator_2)) {
86 const int64_t numerator = numerator_1 * numerator_2;
88 const int64_t result = numerator / denominator;
92 (
static_cast<double>(numerator_1) *
static_cast<double>(numerator_2)) /
93 static_cast<double>(denominator);
95 const int64_t result =
static_cast<int64_t
>(floor(
ratio + 0.5));
105 : depth_((parent == nullptr) ? 0 : parent->depth() + 1),
107 assignment_(assignment),
109 profit_upper_bound_(std::numeric_limits<int64_t>::
max()),
110 next_item_id_(kNoSelection) {}
115 : from_(from), via_(nullptr), to_(to) {}
120 CHECK_EQ(node_from->
depth(), node_to->
depth());
123 while (node_from != node_to) {
124 node_from = node_from->
parent();
125 node_to = node_to->
parent();
133 while (current_node->
depth() > depth) {
134 current_node = current_node->
parent();
143 is_bound_.assign(number_of_items,
false);
144 is_in_.assign(number_of_items,
false);
151 is_bound_[assignment.
item_id] =
false;
153 if (is_bound_[assignment.
item_id] &&
157 is_bound_[assignment.
item_id] =
true;
167 profit_lower_bound_(0),
168 profit_upper_bound_(std::numeric_limits<int64_t>::
max()),
174 const std::vector<int64_t>& weights) {
175 const int number_of_items = profits.size();
177 for (
int i = 0; i < number_of_items; ++i) {
178 items_[i] =
new KnapsackItem(i, weights[i], profits[i]);
188 if (assignment.
is_in) {
190 current_profit_ -= items_[assignment.
item_id]->profit;
192 current_profit_ += items_[assignment.
item_id]->profit;
199 bool has_one_propagator, std::vector<bool>* solution)
const {
200 CHECK(solution !=
nullptr);
202 const int item_id = item->id;
203 (*solution)[item_id] = state_.
is_bound(item_id) && state_.
is_in(item_id);
205 if (has_one_propagator) {
215 consumed_capacity_(0),
216 break_item_id_(kNoSelection),
226 break_item_id_ = kNoSelection;
228 int64_t remaining_capacity = capacity_ - consumed_capacity_;
229 int break_sorted_item_id = kNoSelection;
230 const int number_of_sorted_items = sorted_items_.size();
231 for (
int sorted_id = 0; sorted_id < number_of_sorted_items; ++sorted_id) {
232 const KnapsackItem*
const item = sorted_items_[sorted_id];
233 if (!
state().is_bound(item->
id)) {
234 break_item_id_ = item->
id;
236 if (remaining_capacity >= item->
weight) {
237 remaining_capacity -= item->
weight;
240 break_sorted_item_id = sorted_id;
248 if (break_sorted_item_id != kNoSelection) {
249 const int64_t additional_profit =
250 GetAdditionalProfit(remaining_capacity, break_sorted_item_id);
256 consumed_capacity_ = 0;
257 break_item_id_ = kNoSelection;
258 sorted_items_ =
items();
261 profit_max_ =
std::max(profit_max_, item->profit);
264 CompareKnapsackItemsInDecreasingEfficiencyOrder compare_object(profit_max_);
265 std::stable_sort(sorted_items_.begin(), sorted_items_.end(), compare_object);
271 if (assignment.
is_in) {
273 consumed_capacity_ -=
items()[assignment.
item_id]->weight;
275 consumed_capacity_ +=
items()[assignment.
item_id]->weight;
276 if (consumed_capacity_ > capacity_) {
285 std::vector<bool>* solution)
const {
286 CHECK(solution !=
nullptr);
287 int64_t remaining_capacity = capacity_ - consumed_capacity_;
289 if (!
state().is_bound(item->id)) {
290 if (remaining_capacity >= item->weight) {
291 remaining_capacity -= item->weight;
292 (*solution)[item->id] =
true;
300 int64_t KnapsackCapacityPropagator::GetAdditionalProfit(
301 int64_t remaining_capacity,
int break_item_id)
const {
302 const int after_break_item_id = break_item_id + 1;
303 int64_t additional_profit_when_no_break_item = 0;
304 if (after_break_item_id < sorted_items_.size()) {
307 const int64_t next_weight = sorted_items_[after_break_item_id]->weight;
308 const int64_t next_profit = sorted_items_[after_break_item_id]->profit;
309 additional_profit_when_no_break_item =
310 UpperBoundOfRatio(remaining_capacity, next_profit, next_weight);
313 const int before_break_item_id = break_item_id - 1;
314 int64_t additional_profit_when_break_item = 0;
315 if (before_break_item_id >= 0) {
316 const int64_t previous_weight = sorted_items_[before_break_item_id]->weight;
320 if (previous_weight != 0) {
321 const int64_t previous_profit =
322 sorted_items_[before_break_item_id]->profit;
323 const int64_t overused_capacity =
324 sorted_items_[break_item_id]->weight - remaining_capacity;
325 const int64_t
ratio = UpperBoundOfRatio(overused_capacity,
326 previous_profit, previous_weight);
327 additional_profit_when_break_item =
328 sorted_items_[break_item_id]->profit -
ratio;
332 const int64_t additional_profit =
std::max(
333 additional_profit_when_no_break_item, additional_profit_when_break_item);
334 CHECK_GE(additional_profit, 0);
335 return additional_profit;
342 primary_propagator_id_(kPrimaryPropagatorId),
345 best_solution_profit_(0),
351 const std::vector<int64_t>& profits,
352 const std::vector<std::vector<int64_t>>& weights,
353 const std::vector<int64_t>& capacities) {
354 CHECK_EQ(capacities.size(), weights.size());
357 const int number_of_items = profits.size();
358 const int number_of_dimensions = weights.size();
359 state_.
Init(number_of_items);
360 best_solution_.assign(number_of_items,
false);
361 for (
int i = 0; i < number_of_dimensions; ++i) {
362 CHECK_EQ(number_of_items, weights[i].size());
366 propagator->
Init(profits, weights[i]);
367 propagators_.push_back(propagator);
369 primary_propagator_id_ = kPrimaryPropagatorId;
377 const bool fail = !IncrementalUpdate(
false, assignment);
384 ? propagators_[primary_propagator_id_]->profit_lower_bound()
389 const bool fail_revert = !IncrementalUpdate(
true, assignment);
397 bool* is_solution_optimal) {
399 DCHECK(is_solution_optimal !=
nullptr);
400 best_solution_profit_ = 0LL;
401 *is_solution_optimal =
true;
403 SearchQueue search_queue;
409 search_nodes_.push_back(root_node);
411 if (MakeNewNode(*root_node,
false)) {
412 search_queue.push(search_nodes_.back());
414 if (MakeNewNode(*root_node,
true)) {
415 search_queue.push(search_nodes_.back());
419 while (!search_queue.empty() &&
420 search_queue.top()->profit_upper_bound() > best_solution_profit_) {
422 *is_solution_optimal =
false;
428 if (node != current_node) {
431 const bool no_fail = UpdatePropagators(path);
433 CHECK_EQ(no_fail,
true);
436 if (MakeNewNode(*node,
false)) {
437 search_queue.push(search_nodes_.back());
439 if (MakeNewNode(*node,
true)) {
440 search_queue.push(search_nodes_.back());
443 return best_solution_profit_;
446 void KnapsackGenericSolver::Clear() {
452 bool KnapsackGenericSolver::UpdatePropagators(
const KnapsackSearchPath& path) {
455 const KnapsackSearchNode* node = &path.from();
456 const KnapsackSearchNode* via = &path.via();
457 while (node != via) {
458 no_fail = IncrementalUpdate(
true, node->assignment()) && no_fail;
459 node = node->parent();
463 while (node != via) {
464 no_fail = IncrementalUpdate(
false, node->assignment()) && no_fail;
465 node = node->parent();
470 int64_t KnapsackGenericSolver::GetAggregatedProfitUpperBound()
const {
472 for (KnapsackPropagator*
const prop : propagators_) {
473 prop->ComputeProfitBounds();
474 const int64_t propagator_upper_bound = prop->profit_upper_bound();
480 bool KnapsackGenericSolver::MakeNewNode(
const KnapsackSearchNode& node,
482 if (node.next_item_id() == kNoSelection) {
485 KnapsackAssignment assignment(node.next_item_id(), is_in);
486 KnapsackSearchNode new_node(&node, assignment);
488 KnapsackSearchPath path(node, new_node);
490 const bool no_fail = UpdatePropagators(path);
492 new_node.set_current_profit(GetCurrentProfit());
493 new_node.set_profit_upper_bound(GetAggregatedProfitUpperBound());
494 new_node.set_next_item_id(GetNextItemId());
495 UpdateBestSolution();
499 KnapsackSearchPath revert_path(new_node, node);
501 UpdatePropagators(revert_path);
503 if (!no_fail || new_node.profit_upper_bound() < best_solution_profit_) {
508 KnapsackSearchNode* relevant_node =
new KnapsackSearchNode(&node, assignment);
509 relevant_node->set_current_profit(new_node.current_profit());
510 relevant_node->set_profit_upper_bound(new_node.profit_upper_bound());
511 relevant_node->set_next_item_id(new_node.next_item_id());
512 search_nodes_.push_back(relevant_node);
517 bool KnapsackGenericSolver::IncrementalUpdate(
518 bool revert,
const KnapsackAssignment& assignment) {
521 bool no_fail = state_.
UpdateState(revert, assignment);
522 for (KnapsackPropagator*
const prop : propagators_) {
523 no_fail = prop->Update(revert, assignment) && no_fail;
528 void KnapsackGenericSolver::UpdateBestSolution() {
529 const int64_t profit_lower_bound =
531 ? propagators_[primary_propagator_id_]->profit_lower_bound()
532 : propagators_[primary_propagator_id_]->current_profit();
534 if (best_solution_profit_ < profit_lower_bound) {
535 best_solution_profit_ = profit_lower_bound;
536 propagators_[primary_propagator_id_]->CopyCurrentStateToSolution(
537 HasOnePropagator(), &best_solution_);
551 void Init(
const std::vector<int64_t>& profits,
552 const std::vector<std::vector<int64_t>>& weights,
553 const std::vector<int64_t>& capacities)
override;
560 return (best_solution_ &
OneBit32(item_id)) != 0U;
565 int64_t profits_weights_[kMaxNumberOfBruteForceItems * 2];
567 int64_t best_solution_profit_;
568 uint32_t best_solution_;
574 const std::string& solver_name)
578 best_solution_profit_(0LL),
579 best_solution_(0U) {}
582 const std::vector<int64_t>& profits,
583 const std::vector<std::vector<int64_t>>& weights,
584 const std::vector<int64_t>& capacities) {
586 CHECK_EQ(weights.size(), 1)
587 <<
"Brute force solver only works with one dimension.";
588 CHECK_EQ(capacities.size(), weights.size());
590 num_items_ = profits.size();
591 CHECK_EQ(num_items_, weights.at(0).size());
592 CHECK_LE(num_items_, kMaxNumberOfBruteForceItems)
593 <<
"To use KnapsackBruteForceSolver the number of items should be "
594 <<
"less than " << kMaxNumberOfBruteForceItems
595 <<
". Current value: " << num_items_ <<
".";
597 for (
int i = 0; i < num_items_; ++i) {
598 profits_weights_[i * 2] = profits.at(i);
599 profits_weights_[i * 2 + 1] = weights.at(0).at(i);
601 capacity_ = capacities.at(0);
605 bool* is_solution_optimal) {
606 DCHECK(is_solution_optimal !=
nullptr);
607 *is_solution_optimal =
true;
608 best_solution_profit_ = 0LL;
611 const uint32_t num_states =
OneBit32(num_items_);
612 uint32_t prev_state = 0U;
613 uint64_t sum_profit = 0ULL;
614 uint64_t sum_weight = 0ULL;
615 uint32_t diff_state = 0U;
616 uint32_t local_state = 0U;
620 for (uint32_t state = 1U; state < num_states; ++state, ++prev_state) {
621 diff_state = state ^ prev_state;
625 if (diff_state & 1U) {
626 if (local_state & 1U) {
627 sum_profit += profits_weights_[item_id];
628 sum_weight += profits_weights_[item_id + 1];
629 CHECK_LT(item_id + 1, 2 * num_items_);
631 sum_profit -= profits_weights_[item_id];
632 sum_weight -= profits_weights_[item_id + 1];
633 CHECK_LT(item_id + 1, 2 * num_items_);
637 local_state = local_state >> 1;
638 diff_state = diff_state >> 1;
641 if (sum_weight <= capacity_ && best_solution_profit_ < sum_profit) {
642 best_solution_profit_ = sum_profit;
643 best_solution_ = state;
647 return best_solution_profit_;
663 static_cast<double>(_weight)
664 : static_cast<double>(_profit_max)) {}
681 void Init(
const std::vector<int64_t>& profits,
682 const std::vector<std::vector<int64_t>>& weights,
683 const std::vector<int64_t>& capacities)
override;
690 return (best_solution_ &
OneBit64(item_id)) != 0ULL;
694 int GetBreakItemId(int64_t
capacity)
const;
696 void GoToNextState(
bool has_failed);
697 void BuildBestSolution();
699 std::vector<KnapsackItemWithEfficiency> sorted_items_;
700 std::vector<int64_t> sum_profits_;
701 std::vector<int64_t> sum_weights_;
706 int64_t best_solution_profit_;
707 uint64_t best_solution_;
708 int best_solution_depth_;
711 int64_t state_weight_;
713 int64_t rejected_items_profit_;
715 int64_t rejected_items_weight_;
734 best_solution_profit_(0LL),
735 best_solution_(0ULL),
736 best_solution_depth_(0),
738 rejected_items_profit_(0LL),
739 rejected_items_weight_(0LL) {}
742 const std::vector<int64_t>& profits,
743 const std::vector<std::vector<int64_t>>& weights,
744 const std::vector<int64_t>& capacities) {
745 CHECK_EQ(weights.size(), 1)
746 <<
"Brute force solver only works with one dimension.";
747 CHECK_EQ(capacities.size(), weights.size());
749 sorted_items_.clear();
750 sum_profits_.clear();
751 sum_weights_.clear();
753 capacity_ = capacities[0];
754 const int num_items = profits.size();
755 CHECK_LE(num_items, kMaxNumberOf64Items)
756 <<
"To use Knapsack64ItemsSolver the number of items should be "
757 <<
"less than " << kMaxNumberOf64Items <<
". Current value: " << num_items
759 int64_t
profit_max = *std::max_element(profits.begin(), profits.end());
761 for (
int i = 0; i < num_items; ++i) {
762 sorted_items_.push_back(
766 std::sort(sorted_items_.begin(), sorted_items_.end(),
769 int64_t sum_profit = 0;
770 int64_t sum_weight = 0;
771 sum_profits_.push_back(sum_profit);
772 sum_weights_.push_back(sum_weight);
773 for (
int i = 0; i < num_items; ++i) {
774 sum_profit += sorted_items_[i].profit;
775 sum_weight += sorted_items_[i].weight;
777 sum_profits_.push_back(sum_profit);
778 sum_weights_.push_back(sum_weight);
783 bool* is_solution_optimal) {
784 DCHECK(is_solution_optimal !=
nullptr);
785 *is_solution_optimal =
true;
786 const int num_items = sorted_items_.size();
789 state_weight_ = sorted_items_[0].weight;
790 rejected_items_profit_ = 0LL;
791 rejected_items_weight_ = 0LL;
792 best_solution_profit_ = 0LL;
793 best_solution_ = 0ULL;
794 best_solution_depth_ = 0;
799 while (state_depth_ >= 0) {
801 if (state_weight_ > capacity_ || state_depth_ >= num_items) {
807 best_solution_ = state_;
808 best_solution_depth_ = state_depth_;
811 fail = fail || best_solution_profit_ >=
upper_bound;
816 return best_solution_profit_;
819 int Knapsack64ItemsSolver::GetBreakItemId(int64_t
capacity)
const {
820 std::vector<int64_t>::const_iterator binary_search_iterator =
822 return static_cast<int>(binary_search_iterator - sum_weights_.begin()) - 1;
832 void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64_t*
lower_bound,
834 const int64_t available_capacity = capacity_ + rejected_items_weight_;
835 const int break_item_id = GetBreakItemId(available_capacity);
836 const int num_items = sorted_items_.size();
837 if (break_item_id >= num_items) {
838 *
lower_bound = sum_profits_[num_items] - rejected_items_profit_;
843 *
lower_bound = sum_profits_[break_item_id] - rejected_items_profit_;
845 const int64_t consumed_capacity = sum_weights_[break_item_id];
846 const int64_t remaining_capacity = available_capacity - consumed_capacity;
847 const double efficiency = sorted_items_[break_item_id].efficiency;
848 const int64_t additional_profit =
849 static_cast<int64_t
>(remaining_capacity * efficiency);
858 void Knapsack64ItemsSolver::GoToNextState(
bool has_failed) {
859 uint64_t mask =
OneBit64(state_depth_);
862 state_ = state_ | (mask << 1);
863 state_weight_ += sorted_items_[state_depth_].weight;
866 while ((state_ & mask) == 0ULL && state_depth_ >= 0) {
867 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
868 rejected_items_profit_ -= item.profit;
869 rejected_items_weight_ -= item.weight;
875 state_ = state_ & ~mask;
876 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
877 rejected_items_profit_ += item.profit;
878 rejected_items_weight_ += item.weight;
879 state_weight_ -= item.weight;
884 void Knapsack64ItemsSolver::BuildBestSolution() {
885 int64_t remaining_capacity = capacity_;
886 int64_t check_profit = 0LL;
890 for (
int i = 0; i <= best_solution_depth_; ++i) {
892 remaining_capacity -= sorted_items_[i].weight;
893 check_profit += sorted_items_[i].profit;
898 const int num_items = sorted_items_.size();
899 for (
int i = best_solution_depth_ + 1; i < num_items; ++i) {
900 int64_t
weight = sorted_items_[i].weight;
901 if (remaining_capacity >=
weight) {
902 remaining_capacity -=
weight;
903 check_profit += sorted_items_[i].profit;
904 best_solution_ = best_solution_ |
OneBit64(i);
906 best_solution_ = best_solution_ & ~
OneBit64(i);
909 CHECK_EQ(best_solution_profit_, check_profit);
915 uint64_t tmp_solution = 0ULL;
916 for (
int i = 0; i < num_items; ++i) {
918 const int original_id = sorted_items_[i].id;
919 tmp_solution = tmp_solution |
OneBit64(original_id);
923 best_solution_ = tmp_solution;
938 void Init(
const std::vector<int64_t>& profits,
939 const std::vector<std::vector<int64_t>>& weights,
940 const std::vector<int64_t>& capacities)
override;
947 return best_solution_.at(item_id);
951 int64_t SolveSubProblem(int64_t
capacity,
int num_items);
953 std::vector<int64_t> profits_;
954 std::vector<int64_t> weights_;
956 std::vector<int64_t> computed_profits_;
957 std::vector<int> selected_item_ids_;
958 std::vector<bool> best_solution_;
963 const std::string& solver_name)
969 selected_item_ids_(),
973 const std::vector<int64_t>& profits,
974 const std::vector<std::vector<int64_t>>& weights,
975 const std::vector<int64_t>& capacities) {
976 CHECK_EQ(weights.size(), 1)
977 <<
"Current implementation of the dynamic programming solver only deals"
978 <<
" with one dimension.";
979 CHECK_EQ(capacities.size(), weights.size());
982 weights_ = weights[0];
983 capacity_ = capacities[0];
986 int64_t KnapsackDynamicProgrammingSolver::SolveSubProblem(int64_t
capacity,
988 const int64_t capacity_plus_1 =
capacity + 1;
989 std::fill_n(selected_item_ids_.begin(), capacity_plus_1, 0);
990 std::fill_n(computed_profits_.begin(), capacity_plus_1, int64_t{0});
991 for (
int item_id = 0; item_id < num_items; ++item_id) {
992 const int64_t item_weight = weights_[item_id];
993 const int64_t item_profit = profits_[item_id];
994 for (int64_t used_capacity =
capacity; used_capacity >= item_weight;
996 if (computed_profits_[used_capacity - item_weight] + item_profit >
997 computed_profits_[used_capacity]) {
998 computed_profits_[used_capacity] =
999 computed_profits_[used_capacity - item_weight] + item_profit;
1000 selected_item_ids_[used_capacity] = item_id;
1004 return selected_item_ids_.at(
capacity);
1008 bool* is_solution_optimal) {
1009 DCHECK(is_solution_optimal !=
nullptr);
1010 *is_solution_optimal =
true;
1011 const int64_t capacity_plus_1 = capacity_ + 1;
1012 selected_item_ids_.assign(capacity_plus_1, 0);
1013 computed_profits_.assign(capacity_plus_1, 0LL);
1015 int64_t remaining_capacity = capacity_;
1016 int num_items = profits_.size();
1017 best_solution_.assign(num_items,
false);
1019 while (remaining_capacity > 0 && num_items > 0) {
1020 const int selected_item_id = SolveSubProblem(remaining_capacity, num_items);
1021 remaining_capacity -= weights_[selected_item_id];
1022 num_items = selected_item_id;
1023 if (remaining_capacity >= 0) {
1024 best_solution_[selected_item_id] =
true;
1028 return computed_profits_[capacity_];
1044 void Init(
const std::vector<int64_t>& profits,
1045 const std::vector<std::vector<int64_t>>& weights,
1046 const std::vector<int64_t>& capacities)
override;
1053 return best_solution_.at(item_id);
1058 void SolveSubProblem(
bool first_storage, int64_t
capacity,
int start_item,
1062 int64_t DivideAndConquer(int64_t
capacity,
int start_item,
int end_item);
1064 std::vector<int64_t> profits_;
1065 std::vector<int64_t> weights_;
1067 std::vector<int64_t> computed_profits_storage1_;
1068 std::vector<int64_t> computed_profits_storage2_;
1069 std::vector<bool> best_solution_;
1074 const std::string& solver_name)
1079 computed_profits_storage1_(),
1080 computed_profits_storage2_(),
1084 const std::vector<int64_t>& profits,
1085 const std::vector<std::vector<int64_t>>& weights,
1086 const std::vector<int64_t>& capacities) {
1087 CHECK_EQ(weights.size(), 1)
1088 <<
"Current implementation of the divide and conquer solver only deals"
1089 <<
" with one dimension.";
1090 CHECK_EQ(capacities.size(), weights.size());
1093 weights_ = weights[0];
1094 capacity_ = capacities[0];
1097 void KnapsackDivideAndConquerSolver::SolveSubProblem(
bool first_storage,
1101 std::vector<int64_t>& computed_profits_storage =
1102 (first_storage) ? computed_profits_storage1_ : computed_profits_storage2_;
1103 const int64_t capacity_plus_1 =
capacity + 1;
1104 std::fill_n(computed_profits_storage.begin(), capacity_plus_1, 0LL);
1105 for (
int item_id = start_item; item_id < end_item; ++item_id) {
1106 const int64_t item_weight = weights_[item_id];
1107 const int64_t item_profit = profits_[item_id];
1108 for (int64_t used_capacity =
capacity; used_capacity >= item_weight;
1110 if (computed_profits_storage[used_capacity - item_weight] + item_profit >
1111 computed_profits_storage[used_capacity]) {
1112 computed_profits_storage[used_capacity] =
1113 computed_profits_storage[used_capacity - item_weight] + item_profit;
1119 int64_t KnapsackDivideAndConquerSolver::DivideAndConquer(int64_t
capacity,
1122 int item_boundary = start_item + ((end_item - start_item) / 2);
1124 SolveSubProblem(
true,
capacity, start_item, item_boundary);
1125 SolveSubProblem(
false,
capacity, item_boundary, end_item);
1127 int64_t max_solution = 0, capacity1 = 0, capacity2 = 0;
1129 for (int64_t capacity_id = 0; capacity_id <=
capacity; capacity_id++) {
1130 if ((computed_profits_storage1_[capacity_id] +
1131 computed_profits_storage2_[(
capacity - capacity_id)]) > max_solution) {
1132 capacity1 = capacity_id;
1133 capacity2 =
capacity - capacity_id;
1134 max_solution = (computed_profits_storage1_[capacity_id] +
1135 computed_profits_storage2_[(
capacity - capacity_id)]);
1139 if ((item_boundary - start_item) == 1) {
1140 if (weights_[start_item] <= capacity1) best_solution_[start_item] =
true;
1141 }
else if ((item_boundary - start_item) > 1) {
1142 DivideAndConquer(capacity1, start_item, item_boundary);
1145 if ((end_item - item_boundary) == 1) {
1146 if (weights_[item_boundary] <= capacity2)
1147 best_solution_[item_boundary] =
true;
1148 }
else if ((end_item - item_boundary) > 1) {
1149 DivideAndConquer(capacity2, item_boundary, end_item);
1151 return max_solution;
1155 bool* is_solution_optimal) {
1156 DCHECK(is_solution_optimal !=
nullptr);
1157 *is_solution_optimal =
true;
1158 const int64_t capacity_plus_1 = capacity_ + 1;
1159 computed_profits_storage1_.assign(capacity_plus_1, 0LL);
1160 computed_profits_storage2_.assign(capacity_plus_1, 0LL);
1161 best_solution_.assign(profits_.size(),
false);
1163 return DivideAndConquer(capacity_, 0, profits_.size());
1169 const std::string& solver_name);
1172 void Init(
const std::vector<int64_t>& profits,
1173 const std::vector<std::vector<int64_t>>& weights,
1174 const std::vector<int64_t>& capacities)
override;
1181 return best_solution_.at(item_id);
1186 std::vector<int64_t> profits_;
1187 std::vector<std::vector<int64_t>> weights_;
1188 std::vector<int64_t> capacities_;
1189 std::vector<bool> best_solution_;
1194 const std::string& solver_name)
1203 const std::vector<std::vector<int64_t>>& weights,
1204 const std::vector<int64_t>& capacities) {
1207 capacities_ = capacities;
1211 bool* is_solution_optimal) {
1212 DCHECK(is_solution_optimal !=
nullptr);
1213 *is_solution_optimal =
true;
1216 const int num_items = profits_.size();
1217 std::vector<MPVariable*> variables;
1221 const int num_dimensions = capacities_.size();
1222 CHECK(weights_.size() == num_dimensions)
1223 <<
"Weights should be vector of num_dimensions (" << num_dimensions
1224 <<
") vectors of size num_items (" << num_items <<
").";
1225 for (
int i = 0; i < num_dimensions; ++i) {
1227 for (
int j = 0; j < num_items; ++j) {
1228 ct->SetCoefficient(variables.at(j), weights_.at(i).at(j));
1236 for (
int j = 0; j < num_items; ++j) {
1245 const float kRoundNear = 0.5;
1246 best_solution_.assign(num_items,
false);
1247 for (
int j = 0; j < num_items; ++j) {
1248 const double value = variables.at(j)->solution_value();
1249 best_solution_.at(j) =
value >= kRoundNear;
1252 return -objective->
Value() + kRoundNear;
1257 :
KnapsackSolver(KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER,
1261 const std::string& solver_name)
1265 mapping_reduced_item_id_(),
1266 is_problem_solved_(false),
1267 additional_profit_(0LL),
1268 use_reduction_(true),
1269 time_limit_seconds_(std::numeric_limits<double>::infinity()) {
1270 switch (solver_type) {
1272 solver_ = std::make_unique<KnapsackBruteForceSolver>(solver_name);
1275 solver_ = std::make_unique<Knapsack64ItemsSolver>(solver_name);
1278 solver_ = std::make_unique<KnapsackDynamicProgrammingSolver>(solver_name);
1281 solver_ = std::make_unique<KnapsackGenericSolver>(solver_name);
1284 solver_ = std::make_unique<KnapsackDivideAndConquerSolver>(solver_name);
1286 #if defined(USE_CBC)
1288 solver_ = std::make_unique<KnapsackMIPSolver>(
1292 #if defined(USE_SCIP)
1294 solver_ = std::make_unique<KnapsackMIPSolver>(
1298 #if defined(USE_XPRESS)
1299 case KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER:
1300 solver_ = std::make_unique<KnapsackMIPSolver>(
1304 #if defined(USE_CPLEX)
1305 case KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER:
1306 solver_ = std::make_unique<KnapsackMIPSolver>(
1311 LOG(FATAL) <<
"Unknown knapsack solver type.";
1318 const std::vector<std::vector<int64_t>>& weights,
1319 const std::vector<int64_t>& capacities) {
1320 for (
const std::vector<int64_t>& w : weights) {
1321 CHECK_EQ(profits.size(), w.size())
1322 <<
"Profits and inner weights must have the same size (#items)";
1324 CHECK_EQ(capacities.size(), weights.size())
1325 <<
"Capacities and weights must have the same size (#bins)";
1326 time_limit_ = std::make_unique<TimeLimit>(time_limit_seconds_);
1327 is_solution_optimal_ =
false;
1328 additional_profit_ = 0LL;
1329 is_problem_solved_ =
false;
1331 const int num_items = profits.size();
1332 std::vector<std::vector<int64_t>> reduced_weights;
1333 std::vector<int64_t> reduced_capacities;
1334 if (use_reduction_) {
1335 const int num_reduced_items = ReduceCapacities(
1336 num_items, weights, capacities, &reduced_weights, &reduced_capacities);
1337 if (num_reduced_items > 0) {
1338 ComputeAdditionalProfit(profits);
1341 reduced_weights = weights;
1342 reduced_capacities = capacities;
1344 if (!is_problem_solved_) {
1345 solver_->Init(profits, reduced_weights, reduced_capacities);
1346 if (use_reduction_) {
1347 const int num_reduced_items = ReduceProblem(num_items);
1349 if (num_reduced_items > 0) {
1350 ComputeAdditionalProfit(profits);
1353 if (num_reduced_items > 0 && num_reduced_items < num_items) {
1354 InitReducedProblem(profits, reduced_weights, reduced_capacities);
1358 if (is_problem_solved_) {
1359 is_solution_optimal_ =
true;
1363 int KnapsackSolver::ReduceCapacities(
1364 int num_items,
const std::vector<std::vector<int64_t>>& weights,
1365 const std::vector<int64_t>& capacities,
1366 std::vector<std::vector<int64_t>>* reduced_weights,
1367 std::vector<int64_t>* reduced_capacities) {
1368 known_value_.assign(num_items,
false);
1369 best_solution_.assign(num_items,
false);
1370 mapping_reduced_item_id_.assign(num_items, 0);
1371 std::vector<bool> active_capacities(weights.size(),
true);
1372 int number_of_active_capacities = 0;
1373 for (
int i = 0; i < weights.size(); ++i) {
1374 int64_t max_weight = 0;
1375 for (int64_t
weight : weights[i]) {
1378 if (max_weight <= capacities[i]) {
1379 active_capacities[i] =
false;
1381 ++number_of_active_capacities;
1384 reduced_weights->reserve(number_of_active_capacities);
1385 reduced_capacities->reserve(number_of_active_capacities);
1386 for (
int i = 0; i < weights.size(); ++i) {
1387 if (active_capacities[i]) {
1388 reduced_weights->push_back(weights[i]);
1389 reduced_capacities->push_back(capacities[i]);
1392 if (reduced_capacities->empty()) {
1395 for (
int item_id = 0; item_id < num_items; ++item_id) {
1396 known_value_[item_id] =
true;
1397 best_solution_[item_id] =
true;
1399 is_problem_solved_ =
true;
1407 int KnapsackSolver::ReduceProblem(
int num_items) {
1408 known_value_.assign(num_items,
false);
1409 best_solution_.assign(num_items,
false);
1410 mapping_reduced_item_id_.assign(num_items, 0);
1411 additional_profit_ = 0LL;
1413 for (
int item_id = 0; item_id < num_items; ++item_id) {
1414 mapping_reduced_item_id_[item_id] = item_id;
1417 int64_t best_lower_bound = 0LL;
1418 std::vector<int64_t> J0_upper_bounds(num_items,
1420 std::vector<int64_t> J1_upper_bounds(num_items,
1422 for (
int item_id = 0; item_id < num_items; ++item_id) {
1423 if (time_limit_->LimitReached()) {
1428 solver_->GetLowerAndUpperBoundWhenItem(item_id,
false, &
lower_bound,
1433 solver_->GetLowerAndUpperBoundWhenItem(item_id,
true, &
lower_bound,
1439 int num_reduced_items = 0;
1440 for (
int item_id = 0; item_id < num_items; ++item_id) {
1441 if (best_lower_bound > J0_upper_bounds[item_id]) {
1442 known_value_[item_id] =
true;
1443 best_solution_[item_id] =
false;
1444 ++num_reduced_items;
1445 }
else if (best_lower_bound > J1_upper_bounds[item_id]) {
1446 known_value_[item_id] =
true;
1447 best_solution_[item_id] =
true;
1448 ++num_reduced_items;
1452 is_problem_solved_ = num_reduced_items == num_items;
1453 return num_reduced_items;
1456 void KnapsackSolver::ComputeAdditionalProfit(
1457 const std::vector<int64_t>& profits) {
1458 const int num_items = profits.size();
1459 additional_profit_ = 0LL;
1460 for (
int item_id = 0; item_id < num_items; ++item_id) {
1461 if (known_value_[item_id] && best_solution_[item_id]) {
1462 additional_profit_ += profits[item_id];
1467 void KnapsackSolver::InitReducedProblem(
1468 const std::vector<int64_t>& profits,
1469 const std::vector<std::vector<int64_t>>& weights,
1470 const std::vector<int64_t>& capacities) {
1471 const int num_items = profits.size();
1472 const int num_dimensions = capacities.size();
1474 std::vector<int64_t> reduced_profits;
1475 for (
int item_id = 0; item_id < num_items; ++item_id) {
1476 if (!known_value_[item_id]) {
1477 mapping_reduced_item_id_[item_id] = reduced_profits.size();
1478 reduced_profits.push_back(profits[item_id]);
1482 std::vector<std::vector<int64_t>> reduced_weights;
1483 std::vector<int64_t> reduced_capacities = capacities;
1484 for (
int dim = 0; dim < num_dimensions; ++dim) {
1485 const std::vector<int64_t>& one_dimension_weights = weights[dim];
1486 std::vector<int64_t> one_dimension_reduced_weights;
1487 for (
int item_id = 0; item_id < num_items; ++item_id) {
1488 if (known_value_[item_id]) {
1489 if (best_solution_[item_id]) {
1490 reduced_capacities[dim] -= one_dimension_weights[item_id];
1493 one_dimension_reduced_weights.push_back(one_dimension_weights[item_id]);
1496 reduced_weights.push_back(one_dimension_reduced_weights);
1498 solver_->Init(reduced_profits, reduced_weights, reduced_capacities);
1502 return additional_profit_ +
1503 ((is_problem_solved_)
1505 : solver_->Solve(time_limit_.get(), &is_solution_optimal_));
1509 const int mapped_item_id =
1510 (use_reduction_) ? mapping_reduced_item_id_[item_id] : item_id;
1511 return (use_reduction_ && known_value_[item_id])
1512 ? best_solution_[item_id]
1513 : solver_->best_solution(mapped_item_id);
virtual void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound)
virtual std::string GetName() const
Knapsack64ItemsSolver(const std::string &solver_name)
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t >> &weights, const std::vector< int64_t > &capacities) override
bool best_solution(int item_id) const override
KnapsackBruteForceSolver(const std::string &solver_name)
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t >> &weights, const std::vector< int64_t > &capacities) override
bool best_solution(int item_id) const override
KnapsackCapacityPropagator(const KnapsackState &state, int64_t capacity)
bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment) override
void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const override
void ComputeProfitBounds() override
~KnapsackCapacityPropagator() override
void InitPropagator() override
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
KnapsackDivideAndConquerSolver(const std::string &solver_name)
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t >> &weights, const std::vector< int64_t > &capacities) override
bool best_solution(int item_id) const override
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t >> &weights, const std::vector< int64_t > &capacities) override
bool best_solution(int item_id) const override
KnapsackDynamicProgrammingSolver(const std::string &solver_name)
~KnapsackGenericSolver() override
KnapsackGenericSolver(const std::string &solver_name)
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound) override
int64_t Solve(TimeLimit *time_limit, bool *is_solution_optimal) override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t >> &weights, const std::vector< int64_t > &capacities) override
KnapsackMIPSolver(MPSolver::OptimizationProblemType problem_type, const std::string &solver_name)
bool best_solution(int item_id) const override
void Init(const std::vector< int64_t > &profits, const std::vector< int64_t > &weights)
int64_t current_profit() const
void CopyCurrentStateToSolution(bool has_one_propagator, std::vector< bool > *solution) const
virtual bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment)=0
virtual void InitPropagator()=0
const KnapsackState & state() const
const std::vector< KnapsackItemPtr > & items() const
int64_t profit_upper_bound() const
virtual void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const =0
int64_t profit_lower_bound() const
void set_profit_lower_bound(int64_t profit)
virtual ~KnapsackPropagator()
KnapsackPropagator(const KnapsackState &state)
bool Update(bool revert, const KnapsackAssignment &assignment)
void set_profit_upper_bound(int64_t profit)
void set_current_profit(int64_t profit)
void set_next_item_id(int id)
KnapsackSearchNode(const KnapsackSearchNode *const parent, const KnapsackAssignment &assignment)
void set_profit_upper_bound(int64_t profit)
const KnapsackSearchNode *const parent() const
const KnapsackSearchNode * MoveUpToDepth(const KnapsackSearchNode &node, int depth) const
KnapsackSearchPath(const KnapsackSearchNode &from, const KnapsackSearchNode &to)
This library solves knapsack problems.
bool BestSolutionContains(int item_id) const
Returns true if the item 'item_id' is packed in the optimal knapsack.
KnapsackSolver(const std::string &solver_name)
int64_t Solve()
Solves the problem and returns the profit of the optimal solution.
SolverType
Enum controlling which underlying algorithm is used.
@ KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER
SCIP based solver.
@ KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER
Generic Solver.
@ KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER
Dynamic Programming approach for single dimension problems.
@ KNAPSACK_DIVIDE_AND_CONQUER_SOLVER
Divide and Conquer approach for single dimension problems.
@ KNAPSACK_64ITEMS_SOLVER
Optimized method for single dimension small problems.
@ KNAPSACK_BRUTE_FORCE_SOLVER
Brute force method.
@ KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER
CBC Based Solver.
std::string GetName() const
virtual ~KnapsackSolver()
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities)
Initializes the solver and enters the problem to be solved.
void Init(int number_of_items)
bool is_bound(int id) const
bool UpdateState(bool revert, const KnapsackAssignment &assignment)
The class for constraints of a Mathematical Programming (MP) model.
A class to express a linear objective.
void SetCoefficient(const MPVariable *const var, double coeff)
Sets the coefficient of the variable in the objective.
double Value() const
Returns the objective value of the best solution found so far.
void SetMinimization()
Sets the optimization direction to minimize.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
void MakeBoolVarArray(int nb, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of boolean variables.
MPObjective * MutableObjective()
Returns the mutable objective object.
MPConstraint * MakeRowConstraint(double lb, double ub)
Creates a linear constraint with given bounds.
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
@ CPLEX_MIXED_INTEGER_PROGRAMMING
@ XPRESS_MIXED_INTEGER_PROGRAMMING
@ SCIP_MIXED_INTEGER_PROGRAMMING
@ CBC_MIXED_INTEGER_PROGRAMMING
ResultStatus Solve()
Solves the problem using the default parameter values.
void SuppressOutput()
Suppresses solver logging.
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
ModelSharedTimeLimit * time_limit
MPSolver::OptimizationProblemType problem_type
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
void STLDeleteElements(T *container)
Collection of objects used to extend the Constraint Solver library.
bool CompareKnapsackItemWithEfficiencyInDecreasingEfficiencyOrder(const KnapsackItemWithEfficiency &item1, const KnapsackItemWithEfficiency &item2)
uint32_t OneBit32(int pos)
uint64_t OneBit64(int pos)
KnapsackItem * KnapsackItemPtr
int MostSignificantBitPosition64(uint64_t n)
KnapsackItemWithEfficiency(int _id, int64_t _profit, int64_t _weight, int64_t _profit_max)