00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <algorithm>
00017 #include <string>
00018 #include <vector>
00019
00020 #include "base/integral_types.h"
00021 #include "base/logging.h"
00022 #include "base/scoped_ptr.h"
00023 #include "base/stringprintf.h"
00024 #include "base/mathutil.h"
00025 #include "constraint_solver/constraint_solver.h"
00026 #include "util/string_array.h"
00027
00028 namespace operations_research {
00029
00030
00031
00032 namespace {
00033 class Deviation : public Constraint {
00034 public:
00035 Deviation(Solver* const solver,
00036 IntVar* const* vars,
00037 int size,
00038 IntVar* const deviation_var,
00039 int64 total_sum)
00040 : Constraint(solver),
00041 vars_(new IntVar*[size]),
00042 size_(size),
00043 deviation_var_(deviation_var),
00044 total_sum_(total_sum),
00045 scaled_vars_assigned_value_(new int64[size]),
00046 scaled_vars_min_(new int64[size]),
00047 scaled_vars_max_(new int64[size]),
00048 scaled_sum_max_(0),
00049 scaled_sum_min_(0),
00050 maximum_(new int64[size]),
00051 overlaps_sup_(new int64[size]),
00052 active_sum_(0),
00053 active_sum_rounded_down_(0),
00054 active_sum_rounded_up_(0),
00055 active_sum_nearest_(0) {
00056 CHECK_GT(size, 0);
00057 CHECK_NOTNULL(vars);
00058 CHECK_NOTNULL(deviation_var);
00059 memcpy(vars_.get(), vars, size_ * sizeof(*vars));
00060 }
00061
00062 virtual ~Deviation() {}
00063
00064 virtual void Post() {
00065 Solver* const s = solver();
00066 Demon* const demon = s->MakeConstraintInitialPropagateCallback(this);
00067 for (int i = 0; i < size_; ++i) {
00068 vars_[i]->WhenRange(demon);
00069 }
00070 deviation_var_->WhenRange(demon);
00071 s->AddConstraint(s->MakeSumEquality(vars_.get(), size_, total_sum_));
00072 }
00073
00074 virtual void InitialPropagate() {
00075 const int64 delta_min = BuildMinimalDeviationAssignment();
00076 deviation_var_->SetMin(delta_min);
00077 PropagateBounds(delta_min);
00078 }
00079
00080 virtual string DebugString() const {
00081 return StringPrintf("Deviation([%s], deviation_var = %s, sum = %lld)",
00082 DebugStringArray(vars_.get(), size_, ", ").c_str(),
00083 deviation_var_->DebugString().c_str(),
00084 total_sum_);
00085 }
00086
00087 virtual void Accept(ModelVisitor* const visitor) const {
00088 visitor->BeginVisitConstraint(ModelVisitor::kDeviation, this);
00089 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
00090 vars_.get(),
00091 size_);
00092 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
00093 deviation_var_);
00094 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument,
00095 total_sum_);
00096 visitor->EndVisitConstraint(ModelVisitor::kDeviation, this);
00097 }
00098
00099 private:
00100
00101
00102
00103 int64 BuildMinimalDeviationAssignment() {
00104 RepairGreedySum(BuildGreedySum(true));
00105 int64 minimal_deviation = 0;
00106 for (int i = 0; i < size_; ++i) {
00107 minimal_deviation += abs(scaled_vars_assigned_value_[i] - total_sum_);
00108 }
00109 return minimal_deviation;
00110 }
00111
00112
00113
00114
00115
00116
00117 void PropagateBounds(int64 min_delta) {
00118 PropagateBounds(min_delta, true);
00119 PropagateBounds(min_delta, false);
00120 }
00121
00122
00123
00124
00125
00126
00127 void PropagateBounds(int64 min_delta, bool upper_bound) {
00128
00129 const int64 greedy_sum = BuildGreedySum(upper_bound);
00130
00131 RepairSumAndComputeInfo(greedy_sum);
00132
00133 PruneVars(min_delta, upper_bound);
00134 }
00135
00136
00137 void ComputeData(bool upper_bound) {
00138 scaled_sum_max_ = 0;
00139 scaled_sum_min_ = 0;
00140 for (int i = 0; i < size_; ++i) {
00141 scaled_vars_max_[i] =
00142 size_ * (upper_bound ? vars_[i]->Max() : -vars_[i]->Min());
00143 scaled_vars_min_[i] =
00144 size_ * (upper_bound ? vars_[i]->Min() : -vars_[i]->Max());
00145 scaled_sum_max_ += scaled_vars_max_[i];
00146 scaled_sum_min_ += scaled_vars_min_[i];
00147 }
00148 active_sum_ = (!upper_bound ? -total_sum_ : total_sum_);
00149
00150 active_sum_rounded_down_ =
00151 size_ * MathUtil::FloorOfRatio<int64>(active_sum_, size_);
00152
00153 active_sum_rounded_up_ = active_sum_rounded_down_ + size_;
00154 active_sum_nearest_ =
00155 (active_sum_rounded_up_ - active_sum_ <=
00156 active_sum_ - active_sum_rounded_down_) ?
00157 active_sum_rounded_up_ :
00158 active_sum_rounded_down_;
00159 }
00160
00161
00162 int64 BuildGreedySum(bool upper_bound) {
00163
00164 ComputeData(upper_bound);
00165
00166
00167 DCHECK_GE(size_ * active_sum_, scaled_sum_min_);
00168 DCHECK_LE(size_ * active_sum_, scaled_sum_max_);
00169
00170 int64 sum = 0;
00171
00172 overlaps_.clear();
00173 for (int i = 0; i < size_; ++i) {
00174 if (scaled_vars_min_[i] >= active_sum_) {
00175 scaled_vars_assigned_value_[i] = scaled_vars_min_[i];
00176 } else if (scaled_vars_max_[i] <= active_sum_) {
00177 scaled_vars_assigned_value_[i] = scaled_vars_max_[i];
00178 } else {
00179
00180
00181 scaled_vars_assigned_value_[i] = active_sum_nearest_;
00182 if (active_sum_ % size_ != 0) {
00183 overlaps_.push_back(i);
00184 }
00185 }
00186 sum += scaled_vars_assigned_value_[i];
00187 }
00188 DCHECK_EQ(0, active_sum_rounded_down_ % size_);
00189 DCHECK_LE(active_sum_rounded_down_, active_sum_);
00190 DCHECK_LT(active_sum_ - active_sum_rounded_down_, size_);
00191
00192 return sum;
00193 }
00194
00195 bool Overlap(int var_index) const {
00196 return scaled_vars_min_[var_index] < active_sum_ &&
00197 scaled_vars_max_[var_index] > active_sum_;
00198 }
00199
00200
00201 void RepairGreedySum(int64 greedy_sum) {
00202
00203 const int64 scaled_total_sum = size_ * active_sum_;
00204
00205 const int64 delta = greedy_sum > scaled_total_sum ? -size_ : size_;
00206
00207
00208
00209
00210 for (int j = 0;
00211 j < overlaps_.size() && greedy_sum != scaled_total_sum;
00212 j++) {
00213 scaled_vars_assigned_value_[overlaps_[j]] += delta;
00214 greedy_sum += delta;
00215 }
00216
00217 for (int i = 0; i < size_ && greedy_sum != scaled_total_sum; ++i) {
00218 const int64 old_scaled_vars_i = scaled_vars_assigned_value_[i];
00219 if (greedy_sum < scaled_total_sum) {
00220
00221
00222 scaled_vars_assigned_value_[i] += scaled_total_sum - greedy_sum;
00223 scaled_vars_assigned_value_[i] =
00224 std::min(scaled_vars_assigned_value_[i], scaled_vars_max_[i]);
00225 } else {
00226
00227
00228 scaled_vars_assigned_value_[i] -= (greedy_sum - scaled_total_sum);
00229 scaled_vars_assigned_value_[i] =
00230 std::max(scaled_vars_assigned_value_[i], scaled_vars_min_[i]);
00231 }
00232
00233 greedy_sum += scaled_vars_assigned_value_[i] - old_scaled_vars_i;
00234 }
00235 }
00236
00237
00238
00239 void ComputeMaxWhenNoRepair() {
00240 int num_overlap_sum_rounded_up = 0;
00241 if (active_sum_nearest_ == active_sum_rounded_up_) {
00242 num_overlap_sum_rounded_up = overlaps_.size();
00243 }
00244 for (int i = 0; i < size_; ++i) {
00245 maximum_[i] = scaled_vars_assigned_value_[i];
00246 if (Overlap(i) &&
00247 active_sum_nearest_ == active_sum_rounded_up_ &&
00248 active_sum_ % size_ != 0) {
00249 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
00250 } else {
00251 overlaps_sup_[i] = num_overlap_sum_rounded_up;
00252 }
00253 }
00254 }
00255
00256
00257
00258
00259 int ComputeNumOverlapsVariableRoundedUp() {
00260 if (active_sum_ % size_ == 0) {
00261 return 0;
00262 }
00263 int num_overlap_sum_rounded_up = 0;
00264 for (int i = 0; i < size_; ++i) {
00265 if (scaled_vars_assigned_value_[i] > scaled_vars_min_[i] &&
00266 scaled_vars_assigned_value_[i] == active_sum_rounded_up_) {
00267 num_overlap_sum_rounded_up++;
00268 }
00269 }
00270 return num_overlap_sum_rounded_up;
00271 }
00272
00273
00274
00275
00276 bool CanPushSumAcrossMean(int64 greedy_sum, int64 scaled_total_sum) const {
00277 return (greedy_sum > scaled_total_sum &&
00278 active_sum_nearest_ == active_sum_rounded_up_) ||
00279 (greedy_sum < scaled_total_sum &&
00280 active_sum_nearest_ == active_sum_rounded_down_);
00281 }
00282
00283
00284
00285 void RepairSumAndComputeInfo(int64 greedy_sum) {
00286 const int64 scaled_total_sum = size_ * active_sum_;
00287
00288
00289
00290 if (greedy_sum == scaled_total_sum) {
00291 ComputeMaxWhenNoRepair();
00292 } else {
00293
00294 if (CanPushSumAcrossMean(greedy_sum, scaled_total_sum)) {
00295 const int64 delta = greedy_sum > scaled_total_sum ? -size_ : size_;
00296 for (int j = 0;
00297 j < overlaps_.size() && greedy_sum != scaled_total_sum;
00298 ++j) {
00299 scaled_vars_assigned_value_[overlaps_[j]] += delta;
00300 greedy_sum += delta;
00301 }
00302 }
00303
00304 const int num_overlap_sum_rounded_up =
00305 ComputeNumOverlapsVariableRoundedUp();
00306
00307 if (greedy_sum == scaled_total_sum) {
00308
00309 for (int i = 0; i < size_; ++i) {
00310 if (Overlap(i) && num_overlap_sum_rounded_up > 0) {
00311 maximum_[i] = active_sum_rounded_up_;
00312 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
00313 } else {
00314 maximum_[i] = scaled_vars_assigned_value_[i];
00315 overlaps_sup_[i] = num_overlap_sum_rounded_up;
00316 }
00317 }
00318 } else if (greedy_sum > scaled_total_sum) {
00319
00320
00321
00322 for (int i = 0; i < size_; ++i) {
00323 maximum_[i] = scaled_vars_assigned_value_[i];
00324 overlaps_sup_[i] = 0;
00325 }
00326 } else {
00327 for (int i = 0; i < size_; ++i) {
00328 if (Overlap(i) && num_overlap_sum_rounded_up > 0) {
00329 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
00330 } else {
00331 overlaps_sup_[i] = num_overlap_sum_rounded_up;
00332 }
00333
00334 if (scaled_vars_assigned_value_[i] < scaled_vars_max_[i]) {
00335 maximum_[i] =
00336 scaled_vars_assigned_value_[i] + scaled_total_sum - greedy_sum;
00337 } else {
00338 maximum_[i] = scaled_vars_assigned_value_[i];
00339 }
00340 }
00341 }
00342 }
00343 }
00344
00345
00346 void PruneVars(int64 min_delta, bool upper_bound) {
00347
00348 const int64 increase_down_up = (active_sum_rounded_up_ - active_sum_) -
00349 (active_sum_ - active_sum_rounded_down_);
00350 for (int var_index = 0; var_index < size_; ++var_index) {
00351
00352 if (scaled_vars_max_[var_index] != scaled_vars_min_[var_index] &&
00353 maximum_[var_index] < scaled_vars_max_[var_index]) {
00354 const int64 new_max = ComputeNewMax(var_index,
00355 min_delta,
00356 increase_down_up);
00357 PruneBound(var_index, new_max, upper_bound);
00358 }
00359 }
00360 }
00361
00362
00363 int64 ComputeNewMax(int var_index,
00364 int64 min_delta,
00365 int64 increase_down_up) {
00366 int64 maximum_value = maximum_[var_index];
00367 int64 current_min_delta = min_delta;
00368
00369 if (overlaps_sup_[var_index] > 0 &&
00370 (current_min_delta +
00371 overlaps_sup_[var_index] * (size_ - increase_down_up) >=
00372 deviation_var_->Max())) {
00373 const int64 delta = deviation_var_->Max() - current_min_delta;
00374 maximum_value += (size_ * delta) / (size_ - increase_down_up);
00375 return MathUtil::FloorOfRatio<int64>(maximum_value, size_);
00376 } else {
00377 if (maximum_value == active_sum_rounded_down_ &&
00378 active_sum_rounded_down_ < active_sum_) {
00379 DCHECK_EQ(0, overlaps_sup_[var_index]);
00380 current_min_delta += size_ + increase_down_up;
00381 if (current_min_delta > deviation_var_->Max()) {
00382 DCHECK_EQ(0, maximum_value % size_);
00383 return maximum_value / size_;
00384 }
00385 maximum_value += size_;
00386 }
00387 current_min_delta +=
00388 overlaps_sup_[var_index] * (size_ - increase_down_up);
00389 maximum_value += size_ * overlaps_sup_[var_index];
00390
00391 const int64 delta = deviation_var_->Max() - current_min_delta;
00392 maximum_value += delta / 2;
00393 return MathUtil::FloorOfRatio<int64>(maximum_value, size_);
00394 }
00395 }
00396
00397
00398 void PruneBound(int var_index, int64 bound, bool upper_bound) {
00399 if (upper_bound) {
00400 vars_[var_index]->SetMax(bound);
00401 } else {
00402 vars_[var_index]->SetMin(-bound);
00403 }
00404 }
00405
00406 scoped_array<IntVar*> vars_;
00407 const int size_;
00408 IntVar* const deviation_var_;
00409 const int64 total_sum_;
00410 scoped_array<int64> scaled_vars_assigned_value_;
00411 scoped_array<int64> scaled_vars_min_;
00412 scoped_array<int64> scaled_vars_max_;
00413 int64 scaled_sum_max_;
00414 int64 scaled_sum_min_;
00415
00416 std::vector<int> overlaps_;
00417 scoped_array<int64> maximum_;
00418 scoped_array<int64> overlaps_sup_;
00419
00420 int64 active_sum_;
00421 int64 active_sum_rounded_down_;
00422 int64 active_sum_rounded_up_;
00423 int64 active_sum_nearest_;
00424 };
00425 }
00426
00427 Constraint* Solver::MakeDeviation(const std::vector<IntVar*>& vars,
00428 IntVar* const deviation_var,
00429 int64 total_sum) {
00430 return RevAlloc(new Deviation(this,
00431 vars.data(),
00432 vars.size(),
00433 deviation_var,
00434 total_sum));
00435 }
00436 }