Skip to content

Commit 7b3ebca

Browse files
committed
Move weight redistibution sanity checking into a pre-check rather than a post-check now that the target weight only includes item weight
1 parent 87e2bbc commit 7b3ebca

File tree

4 files changed

+44
-59
lines changed

4 files changed

+44
-59
lines changed

CHANGELOG.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [3.5.2] - 2020-02-02
4+
### Changed
5+
- Further optimisation when packing a large number of items
6+
37
## [3.5.1] - 2020-01-30
48
### Changed
59
- Optimisation when packing a large number of identical items
@@ -89,6 +93,10 @@
8993
### Removed
9094
- HHVM support now that project has a stated goal of no longer targeting PHP7 compatibility
9195

96+
## [2.6.5] - 2020-02-02
97+
### Changed
98+
- Further optimisation when packing a large number of items
99+
92100
## [2.6.4] - 2020-01-30
93101
### Changed
94102
- Optimisation when packing a large number of identical items
@@ -369,8 +377,9 @@ Initial release
369377
- Experimental code to get a feel for how calculations can best be implemented
370378
- Only works if all items fit into a single box (so not production ready at all)
371379

372-
[Unreleased]: https://github.com/dvdoug/BoxPacker/compare/3.5.1...master
380+
[Unreleased]: https://github.com/dvdoug/BoxPacker/compare/3.5.2...master
373381

382+
[3.5.2]: https://github.com/dvdoug/BoxPacker/compare/3.5.1...3.5.2
374383
[3.5.1]: https://github.com/dvdoug/BoxPacker/compare/3.5.0...3.5.1
375384
[3.5.0]: https://github.com/dvdoug/BoxPacker/compare/3.4.1...3.5.0
376385
[3.4.1]: https://github.com/dvdoug/BoxPacker/compare/3.4.0...3.4.1
@@ -385,6 +394,7 @@ Initial release
385394
[3.1.0]: https://github.com/dvdoug/BoxPacker/compare/3.0.1...3.1.0
386395
[3.0.1]: https://github.com/dvdoug/BoxPacker/compare/3.0.0...3.0.1
387396
[3.0.0]: https://github.com/dvdoug/BoxPacker/compare/2.4.2...3.0.0
397+
[2.6.5]: https://github.com/dvdoug/BoxPacker/compare/2.6.4...2.6.5
388398
[2.6.4]: https://github.com/dvdoug/BoxPacker/compare/2.6.3...2.6.4
389399
[2.6.3]: https://github.com/dvdoug/BoxPacker/compare/2.6.2...2.6.3
390400
[2.6.2]: https://github.com/dvdoug/BoxPacker/compare/2.6.1...2.6.2

src/WeightRedistributor.php

+23-16
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private function equaliseWeight(PackedBox &$boxA, PackedBox &$boxB, float $targe
116116
$underWeightBoxItems = $underWeightBox->getItems()->asItemArray();
117117

118118
foreach ($overWeightBoxItems as $key => $overWeightItem) {
119-
if ($overWeightItem->getWeight() + $underWeightBox->getItemWeight() > $targetWeight) {
119+
if (!static::wouldRepackActuallyHelp($overWeightBoxItems, $overWeightItem, $underWeightBoxItems, $targetWeight)) {
120120
continue; // moving this item would harm more than help
121121
}
122122

@@ -142,16 +142,14 @@ private function equaliseWeight(PackedBox &$boxA, PackedBox &$boxB, float $targe
142142
continue; //this should never happen, if we can pack n+1 into the box, we should be able to pack n
143143
}
144144

145-
if (static::didRepackActuallyHelp($boxA, $boxB, $newHeavierBoxes->top(), $newLighterBoxes->top())) {
146-
$this->boxesQtyAvailable[$boxA->getBox()] = $this->boxesQtyAvailable[$boxA->getBox()] + 1;
147-
$this->boxesQtyAvailable[$boxB->getBox()] = $this->boxesQtyAvailable[$boxB->getBox()] + 1;
148-
$this->boxesQtyAvailable[$newHeavierBoxes->top()->getBox()] = $this->boxesQtyAvailable[$newHeavierBoxes->top()->getBox()] - 1;
149-
$this->boxesQtyAvailable[$newLighterBoxes->top()->getBox()] = $this->boxesQtyAvailable[$newLighterBoxes->top()->getBox()] - 1;
150-
$underWeightBox = $boxB = $newLighterBoxes->top();
151-
$boxA = $newHeavierBoxes->top();
145+
$this->boxesQtyAvailable[$boxA->getBox()] = $this->boxesQtyAvailable[$boxA->getBox()] + 1;
146+
$this->boxesQtyAvailable[$boxB->getBox()] = $this->boxesQtyAvailable[$boxB->getBox()] + 1;
147+
$this->boxesQtyAvailable[$newHeavierBoxes->top()->getBox()] = $this->boxesQtyAvailable[$newHeavierBoxes->top()->getBox()] - 1;
148+
$this->boxesQtyAvailable[$newLighterBoxes->top()->getBox()] = $this->boxesQtyAvailable[$newLighterBoxes->top()->getBox()] - 1;
149+
$underWeightBox = $boxB = $newLighterBoxes->top();
150+
$boxA = $newHeavierBoxes->top();
152151

153-
$anyIterationSuccessful = true;
154-
}
152+
$anyIterationSuccessful = true;
155153
}
156154

157155
return $anyIterationSuccessful;
@@ -178,14 +176,23 @@ private function doVolumeRepack(iterable $items, Box $currentBox): PackedBoxList
178176
* boxes, or sometimes the box used for the now lighter set of items actually weighs more when empty causing
179177
* an increase in total weight.
180178
*/
181-
private static function didRepackActuallyHelp(PackedBox $oldBoxA, PackedBox $oldBoxB, PackedBox $newBoxA, PackedBox $newBoxB): bool
179+
private static function wouldRepackActuallyHelp(array $overWeightBoxItems, Item $overWeightItem, array $underWeightBoxItems, float $targetWeight): bool
182180
{
183-
$oldList = new PackedBoxList();
184-
$oldList->insertFromArray([$oldBoxA, $oldBoxB]);
181+
$overWeightItemsWeight = array_sum(array_map(static function (Item $item) {return $item->getWeight(); }, $overWeightBoxItems));
182+
$underWeightItemsWeight = array_sum(array_map(static function (Item $item) {return $item->getWeight(); }, $underWeightBoxItems));
183+
184+
if ($overWeightItem->getWeight() + $underWeightItemsWeight > $targetWeight) {
185+
return false;
186+
}
185187

186-
$newList = new PackedBoxList();
187-
$newList->insertFromArray([$newBoxA, $newBoxB]);
188+
$oldVariance = static::calculateVariance($overWeightItemsWeight, $underWeightItemsWeight);
189+
$newVariance = static::calculateVariance($overWeightItemsWeight - $overWeightItem->getWeight(), $underWeightItemsWeight + $overWeightItem->getWeight());
188190

189-
return $newList->getWeightVariance() < $oldList->getWeightVariance();
191+
return $newVariance < $oldVariance;
192+
}
193+
194+
private static function calculateVariance(int $boxAWeight, int $boxBWeight)
195+
{
196+
return ($boxAWeight - (($boxAWeight + $boxBWeight) / 2)) ** 2; //don't need to calculate B and ÷ 2, for a 2-item population the difference from mean is the same for each box
190197
}
191198
}

tests/WeightRedistributorTest.php

-32
Original file line numberDiff line numberDiff line change
@@ -64,36 +64,4 @@ public function testWeightDistributionWorks(): void
6464

6565
self::assertEquals(0, $packedBoxes->getWeightVariance());
6666
}
67-
68-
/**
69-
* Test a case where a weight balancing repack is actually 1 box less than before the repack.
70-
* Not ideal that this happens, but while it does it can be used for code coverage.
71-
*/
72-
public function testACaseWhereABoxIsEliminated(): void
73-
{
74-
// first no repack case
75-
$packer = new Packer();
76-
$packer->setMaxBoxesToBalanceWeight(0);
77-
$packer->addBox(new TestBox('Option 1', 230, 300, 240, 160, 230, 300, 240, 15000));
78-
$packer->addBox(new TestBox('Option 2', 370, 375, 60, 140, 364, 374, 40, 3000));
79-
80-
$packer->addItem(new TestItem('Item 1', 220, 310, 12, 679, true), 4);
81-
$packer->addItem(new TestItem('Item 2', 210, 297, 5, 242, true), 4);
82-
83-
$packedBoxes = $packer->pack();
84-
85-
self::assertCount(3, $packedBoxes);
86-
87-
// and the repack case
88-
$packer = new Packer();
89-
$packer->addBox(new TestBox('Option 1', 230, 300, 240, 160, 230, 300, 240, 15000));
90-
$packer->addBox(new TestBox('Option 2', 370, 375, 60, 140, 364, 374, 40, 3000));
91-
92-
$packer->addItem(new TestItem('Item 1', 220, 310, 12, 679, true), 4);
93-
$packer->addItem(new TestItem('Item 2', 210, 297, 5, 242, true), 4);
94-
95-
$packedBoxes = $packer->pack();
96-
97-
self::assertCount(2, $packedBoxes);
98-
}
9967
}

tests/data/expected.csv

+10-10
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@
244244
0d1983784732e45a55b1040a82db07c1,1,0,48.1,1,0,48.1
245245
0d36d441ceb0caec57d77547730409a8,2,2601,25.4,1,0,7.6
246246
0d3c2338df7cc72f5fd90c4b315d4ff0,2,1410156.3,34.8,1,0,21.1
247-
0d41f4783eeeb20337d3d1f7f058cfe2,2,38025,41.5,1,0,12.4
247+
0d41f4783eeeb20337d3d1f7f058cfe2,3,333578,27.7,1,0,12.4
248248
0d66c19661114ec65fb923367d0988fd,1,0,26.5,1,0,26.5
249249
0d6b0374f997b7d434c9a1dc10da2d46,1,0,32.8,1,0,32.8
250250
0d786a2440a68c348fa1761614cdf441,1,0,44.3,1,0,44.3
@@ -393,7 +393,7 @@
393393
1672e9897bfb16f7f644365da8d6992c,1,0,26.4,1,0,26.4
394394
167db38fa514a30e70f167f70d19f214,1,0,16.2,1,0,16.2
395395
168065302d10d2f4b683e3d5e7475594,2,536556.3,32.7,1,0,19.8
396-
1680b70ba864c313d28c9386ce54ac25,4,5476556.5,45.9,3,4493.6,38.5
396+
1680b70ba864c313d28c9386ce54ac25,4,5476556.5,45.9,3,6570.9,38.5
397397
1694662b95ac32661ee2b45826bb54f6,1,0,42.4,1,0,42.4
398398
16a2283358cddda00bf2e47e5ad746d3,1,0,24,1,0,24
399399
16a59c72d05be053c19e4b32ceafaef0,1,0,29.2,1,0,29.2
@@ -507,7 +507,7 @@
507507
1bb2791024268994225848fbb9cbc3d3,1,0,35.3,1,0,35.3
508508
1bb54c648802eecd24f028e8b6142ae7,4,42137.7,42.2,1,0,25.3
509509
1bc4006c4dd0d71e507869bb2733a0ba,2,1936,31.7,1,0,9.5
510-
1bd069f31bde829dfe0cd46d82ea770e,2,27060.3,42.3,1,0,12.7
510+
1bd069f31bde829dfe0cd46d82ea770e,3,313990.2,28.2,1,0,12.7
511511
1bd0f6a2ed9e36c28d630bbce77c1d61,3,0,41.8,1,0,18.8
512512
1bde839ae62529764cef435c02aa18fc,1,0,33.6,1,0,33.6
513513
1be064ca4930df0249218f93619f4cde,1,0,22.8,1,0,22.8
@@ -816,7 +816,7 @@
816816
2f40c5cd89da22e706e4c2893c2dccb3,1,0,16.4,1,0,16.4
817817
2f419dab07b92225fa941d15b83f8314,1,0,33.2,1,0,33.2
818818
2f465e9076de95b96b927208b46705f2,1,0,40.4,1,0,40.4
819-
2f537fcb5df6f785082d9548928e3d9b,7,1759160.5,41,2,88209,46.7
819+
2f537fcb5df6f785082d9548928e3d9b,7,1744064.5,41,2,88209,46.7
820820
2f59969c2f5d7f75ec3b78396d271b87,1,0,38.3,1,0,38.3
821821
2f5f3f5bdd74b62412f2f6245d78f6ef,1,0,32.6,1,0,32.6
822822
2f777d3848d7dc6920ae742578dcb053,2,16512.3,30.8,1,0,9.2
@@ -1010,7 +1010,7 @@
10101010
3a37c4a592a4be22a8d8d5e4ee80d7e9,1,0,39.7,1,0,39.7
10111011
3a3dc364bfebd251baf6c465c212ed1c,1,0,15.6,1,0,15.6
10121012
3a3f7d42355f64485a59cb25fd4f6eb2,2,1205604,36.2,1,0,21.9
1013-
3a41ed4b08d3bbc912a4f4aaecee9dd0,3,14816216.2,79.9,3,463611.6,44.4
1013+
3a41ed4b08d3bbc912a4f4aaecee9dd0,3,14816216.2,79.9,3,440547.6,44.4
10141014
3a505cd384cc4eb05934e959fad8185e,2,16900,31.3,1,0,9.4
10151015
3a5f312bc61069dfbada3bea35595687,1,0,38.8,1,0,38.8
10161016
3a8f892aefcf1c1de6b1b126fc6b22e2,1,0,35.1,1,0,35.1
@@ -1037,15 +1037,15 @@
10371037
3bd6564e4bffc131afe76760e02db797,3,7224.9,36.7,1,0,16.5
10381038
3bda69c3fe4e30d6944a02d696661fe3,1,0,29.2,1,0,29.2
10391039
3bdea76452dfd0a2dd349e05688ceea7,1,0,36.2,1,0,36.2
1040-
3bec6daa3c92aab45d0db63b688bad9e,5,90020,34.3,2,3543806.3,18.1
1040+
3bec6daa3c92aab45d0db63b688bad9e,5,89240,34.3,2,3543806.3,18.1
10411041
3becb246809835f192b15039849f3528,1,0,27.5,1,0,27.5
10421042
3bee0a82a9098c92945daac991fca070,2,12.3,23.7,1,0,7.1
10431043
3c1b74fc52f870503d59c3327da6d203,2,607620.3,40,1,0,24.2
10441044
3c29ccbdb900b4ade8f05884d9bafb1c,1,0,38.1,1,0,38.1
10451045
3c2bc39429c8d1a0db6369149f454d65,2,2916,39.5,1,0,11.9
10461046
3c47276e13a1bf073b7f2987f09b21d9,1,0,48,1,0,48
10471047
3c4f92d98c4e7792e46c0269daab7d6d,2,400,28.5,1,0,8.5
1048-
3c51cd3b2b4b213f076ba27e90d04225,3,6253450.7,48.3,2,835396,25.1
1048+
3c51cd3b2b4b213f076ba27e90d04225,3,6251648,48.3,2,835396,25.1
10491049
3c583eb716f36fa0ebe89e3573c733c0,1,0,28.5,1,0,28.5
10501050
3c58eba0522266abecca9b20aad46677,1,0,17.5,1,0,17.5
10511051
3c63070dfe31a39510ed0fdc001cd339,1,0,41.2,1,0,41.2
@@ -2790,7 +2790,7 @@ a3ce775d26ac8be3439ad24b24289f7e,2,87025,25.4,1,0,7.6
27902790
a3dddf09e71c503bc692f33ed332e5d1,1,0,27.5,1,0,27.5
27912791
a3e3c130372bee11d670de991210c9af,1,0,77.1,1,0,77.1
27922792
a3f95b935375913e85f66e3cb6480a9d,1,0,48.5,1,0,48.5
2793-
a403a7d8ed1e7ba344e9a814dc938960,2,1521,44.8,2,1521,32.6
2793+
a403a7d8ed1e7ba344e9a814dc938960,2,1521,44.8,2,1521,44.8
27942794
a413a12acaf8fc5b8cc5118d76c0ebce,1,0,36.5,1,0,36.5
27952795
a4158880f12e267d4120f1c99e90b04b,1,0,5.8,1,0,5.8
27962796
a41d7001551662ca358cf917b4fa71f5,1,0,46.3,1,0,46.3
@@ -2877,7 +2877,7 @@ a8b400d8aef6c488703249becd9b293b,3,18288456.2,62.7,2,11448072.3,32.6
28772877
a8baa80336a3f7799f67d75b56706f88,1,0,26.8,1,0,26.8
28782878
a8ce07dfcf68918f62fe006fdd4e2123,1,0,18.3,1,0,18.3
28792879
a8d355a97fe9359f998901899125564b,1,0,29,1,0,29
2880-
a8df0a7e00e3627fef599719af67e8d0,7,3914818.8,53.1,3,11164.2,29.3
2880+
a8df0a7e00e3627fef599719af67e8d0,7,3914818.8,53.1,3,3914.9,24
28812881
a8f4cfc856ccdba7fac54d706cae29a3,1,0,75.9,1,0,75.9
28822882
a8f9f349cc3a30cb703a9158e52647f3,3,36440.9,31.5,1,0,23.8
28832883
a90a1512baefad9a2c28efde1435ac06,2,961,38.2,1,0,11.4
@@ -3512,7 +3512,7 @@ cee89834047195f849ace58d7900c0b8,1,0,18.5,1,0,18.5
35123512
cefae75274dc3e1ffeaff5635eee0f62,1,0,26.2,1,0,26.2
35133513
cf2c2f8e58511b48193186d0a858d7ff,1,0,33.2,1,0,33.2
35143514
cf3a7710faef1cc4d63d1fda9cdeac25,1,0,31,1,0,31
3515-
cf4d33f33f88d177476780cf0f4d4136,3,71846,39.6,1,0,17.8
3515+
cf4d33f33f88d177476780cf0f4d4136,3,69568.7,39.6,1,0,17.8
35163516
cf52f354e99094e4f1280c80ab97f289,1,0,66.1,1,0,66.1
35173517
cf5d3efee1d5996ff670eccefdd8c8c4,1,0,34.2,1,0,34.2
35183518
cf6c9e450532cd18a8ee0e3186336947,1,0,37.3,1,0,37.3

0 commit comments

Comments
 (0)