I'm trying to find the best way to calculate the box size needed for shipping.
I have 3 shipping containers with different sizes. I have the product's width, length, depth, and mass defined in the database.
I would like to know how to find the smallest amount of boxes needed to ship, and also the smallest dimensions of those boxes given the number of items in the cart.
My current 'idea' is to find the maximum width of the entire products array, the select a box according to it, and then split the order as needed... this doesn't seem like it would work.
My Box sizes are:
- 8 x 6 x 6 = 228 cubic inches
- 10 x 8 x 8 = 640 cubic inches
- 12.5 x 12.5 x 12.5 = 1953.125 cubic inches
A product is defined as such:
[Product] => Array
(
[STOCK_CODE] => 010003
[Product_Slug] => GABA_010003
[ItemName] => GABA
[WHOLESALE_PRICE] => 17.47
[RETAIL_PRICE] => 24.95
[Brand] =>
[ProductLine] =>
[image_name] => 705077000440
[MASS] => 0.313
[Height] => 4.625
[Width] => 2.375
[Depth] => 2.375
[cubic_inches] => 26.087890625
)
I've looked into knapsack problem, packing problem, etc and can't find a way to do this. Any help would be GREAT.
function shipping(){
$this->CartProduct->unbindModel(
array('belongsTo' => array('User'))
);
//find all cart products by current logged in user
$cartItems = $this->CartProduct->find('all', array('conditions' => array('CartProduct.user_id' => $this->Auth->user('id'))));
$i = 0;
//get the max width, height, depth
$maxHeight = 0;
$maxWidth = 0;
$maxDepth = 0;
foreach($cartItems as $c){
$cartItems[$i]['Product']['cubic_inches'] = $c['Product']['Height'] * $c['Product']['Width'] * $c['Product']['Depth'];
$cartItems[$i]['CartProduct']['total_cubic_inches'] = ($c['Product']['Height'] * $c['Product']['Width'] * $c['Product']['Depth']) * $c['CartProduct']['qty'];
if($c['Product']['Height'] > $maxHeight)
{
$maxHeight = $c['Product']['Height'];
}
if($c['Product']['Width'] > $maxWidth)
{
$maxWidth = $c['Product']['Width'];
}
if($c['Product']['Depth'] > $maxDepth)
{
$maxDepth = $c['Product']['Depth'];
}
$i++;
}
//possible containers
//8 x 6 x 6 = 228 ci
//10 x 8 x 8 = 640 ci
//12.5 x 12.5 x 12.5 = 1953.125
$possibleContainers = array(
1 => array(
'Height' => 8,
'Width' => 6,
'Depth' => 6,
'Cubic' => 228),
2 => array(
'Height' => 10,
'Width' => 8,
'Depth' => 8,
'Cubic' => 640),
3 => array(
'Height' => 12.5,
'Width' => 12.5,
'Depth' => 12.5,
'Cubic' => 1953.125)
);
$max = array(
'Height' => $maxHeight,
'Width' => $maxWidth,
'Depth' => $maxDepth,
);
pr($cartItems);
pr($possibleContainers);
die();
}
As for getting a optimal answer, that's NP-Hard... http://en.wikipedia.org/wiki/Bin_packing_problem
The greedy algorithm shown on Wikipedia, while it can be quite far off, might actually do for your case.
However as an estimate you could just sum up the volumes of the items and then apply a inefficiency factor and then use the smallest box(s) you can.
Alternately you could sort the items into decreasing volume and then see how much you can get into the current set of boxes, creating a new box when you can't fit the item in. Not sure how you would handle different box sizes though. You could also have a case where it changes the box size rather than creating a new box.
Food for thought.
Here is a low tech but possible solution:
We just ran into the same issue. I decided to take our box sizes and then give each product a percentage for how much space it took in each box size. Our products are free form and can be squished a bit so if yours are absolute in size you may need to reduce the percentages to account for products being put in the box at different angles ect... Also for us we are able to always put things in the boxes as the same angle to each other so this also helps make the below method work better.
This assumes there are 3 box sizes:
Product A
Box A = 48% (2 fit in a box)
Box B = 30% (3 fit in a box)
Box C = 12% (8 fit in a box)
Product B
Box A = 24%
Box B = 15%
Box C = 7%
Then just have your code add up those percentages for your cart items for box A, B and C ... obviously if any are below 100% everything should fit and if you start from top to bottom the first one to reach less than 100% will fit your products and be the smallest box. And if you run into any scenarios when packing that wont fit just slightly reduce the percentage you entered for that product.
For multiple box shipments you just need to decide what you want to do as for as combinations. The above works best for single box shipments but with some additional logic could easily work well for multiple box shipments.
Related
it's possible overwrite the values of each star?
i need define step = 3.75, min = 0 and max = 15, and prevent from selecting half a star.
the values I want are:
star 1 => 0; star 2 => 3.75; star 3 => 7.5; star 4 => 11,25; star 5
=> 15.
form.php
echo $form->field($model, 'rating')->widget(StarRating::classname(), [
'pluginOptions' => [
'stars' => 5,
'step' => 3.75,
'min' => 0,
'max' => 15,
]
]);
but when i make this, the selection of each star not display correctly, half of star is selected.
I thought you only want to display the client side (with manual numbers). And you will not get numbers from DB
the correct way is to overwrite the star-rating file. Which should be referred to the Kartik forum.
You must overwrite that the first star has a zero score (highlight first star = 0)
However, you can use the following code in your model.
public function beforeSave($insert)
{
// if ($insert) { // only for Save (No Update)
if (!empty($this->Your_field)) {
$this->setAttribute('Your_field', $this->Your_field-3.75);
}
// }
return parent::beforeSave($insert);
}
Instead of your_field, enter your field name (field of the rate).
You have made the settings incorrectly.
Refer the plugin documentation and demos for details.
quotation krajee:
The logic for highlighting stars depends on the stars, min, max, and
step configurations. The percentage of each star to be highlighted for
each step, will be evaluated using the following expression:
STAR_HIGHLIGHT_PERCENT = (max - min) * step * 100 / stars
For example:
If min = 0, max = 5, step = 0.5, and stars = 5, then
STAR_HIGHLIGHT_PERCENT will evaluate to 50% of each star for each
step.
If min = 1, max = 5, step = 0.5, and stars = 5, then
STAR_HIGHLIGHT_PERCENT will evaluate to 40% of each star for each
step.
So, for example 2 above, the stars will not be completely highlighted
as desired. It is therefore important you set the configuration of
stars, min, max, and step correctly.
Refer the plugin documentation and demos for details.
for You:
'stars' => 4,
'step' => 3.75,
'min' => 0,
'max' => 15,
or
echo StarRating::widget(['name' => 'rating',
'pluginOptions' => [
'stars' => 5,
'step' => 3.75,
'min' => 0,
'max' => 18.75,
'starCaptions' => new JsExpression("function(val){return val-3.75 + ' hearts';}")
]
]);
1
If your field values are integers 1 to 5
To display half stars and full stars, You must use round numbers.
After putting the numbers in the following formula:
(max - min) * step * 100 / stars
Equal to: 50% or 100%
And to display only a full star, Equal to: 100%
2
However, it is better that the rate and step are equal.
For example, if step is 2, is better than max equal to 10 (5 Stars)
3
All of this varies according to the value of your field:
for example:
values: .5, 1, 1.5 , ... 5
Default (stars=5,max=5,step=0.5)
values: 1, 2, 3 , ... 5
(stars=5,max=5,step=1)
values: 1, 2, 3 , ... 12
(stars=6,max=12,step=1) => Half star = one point
values: 2, 4, 6 , ... 12
(stars=6,max=12,step=1) => one star = 2 point
Say I have the following measures:
80
180
200
240
410
50
110
I can store each combination of numbers to a maximum of 480 per unit. How can I calculate the least number units required so all measures are spread in the most efficient way?
I've tagged PHP but it can be in JS too, or even pseudo language.
I know I'm supposed to tell what I did already but I'm quite stuck on how to approach this. The first thing that comes to mind is recursion but I'm no math expert to see how this can be done efficient...
Any help is greatly appreciated.
To further elaborate: I'm trying to calculate the number of skirtings I have to order, based on the different lengths I need for the walls. Each skirting has a length of 480cm and I want to know the best way to spread them so I have to buy the least number of skirtings. It's not so much about ordering a skirting extra, but the puzzle to figure it out is an interesting one (at least to me)
Update with solution
Despite people trying to close the question I've started fiddling with the Bin Packing Problem description and following the idea of sorting all items from largest to smallest and then fit them in the best possible way I created this small class that might help others in the future:
<?php
class BinPacker {
private $binSize;
public function __construct($binSize) {
$this->binSize = $binSize;
}
public function pack($elements) {
arsort($elements);
$bins = [];
$handled = [];
while(count($handled) < count($elements)) {
$bin = [];
foreach($elements as $label => $size) {
if(!in_array($label, $handled)) {
if(array_sum($bin) + $size < $this->binSize) {
$bin[$label] = $size;
$handled[] = $label;
}
}
}
$bins[] = $bin;
}
return $bins;
}
public function getMeta($bins) {
$meta = [
'totalValue' => 0,
'totalWaste' => 0,
'totalBins' => count($bins),
'efficiency' => 0,
'valuePerBin' => [],
'wastePerBin' => []
];
foreach($bins as $bin) {
$value = array_sum($bin);
$binWaste = $this->binSize - $value;
$meta['totalValue'] += $value;
$meta['totalWaste'] += $binWaste;
$meta['wastePerBin'][] = $binWaste;
$meta['valuePerBin'][] = $value;
}
$meta['efficiency'] = round((1 - $meta['totalWaste'] / $meta['totalValue']) * 100, 3);
return $meta;
}
}
$test = [
'Wall A' => 420,
'Wall B' => 120,
'Wall C' => 80,
'Wall D' => 114,
'Wall E' => 375,
'Wall F' => 90
];
$binPacker = new BinPacker(488);
$bins = $binPacker->pack($test);
echo '<h2>Meta:</h2>';
var_dump($binPacker->getMeta($bins));
echo '<h2>Bin Configuration</h2>';
var_dump($bins);
Which gives an output:
Meta:
array (size=6)
'totalValue' => int 1199
'totalWaste' => int 265
'totalBins' => int 3
'efficiency' => float 77.898
'valuePerBin' =>
array (size=3)
0 => int 420
1 => int 465
2 => int 314
'wastePerBin' =>
array (size=3)
0 => int 68
1 => int 23
2 => int 174
Bin Configuration
array (size=3)
0 =>
array (size=1)
'Wall A' => int 420
1 =>
array (size=2)
'Wall E' => int 375
'Wall F' => int 90
2 =>
array (size=3)
'Wall B' => int 120
'Wall D' => int 114
'Wall C' => int 80
While the data set is relatively small a rather high inefficiency rate is met. But in my own configuration where I entered all wall and ceiling measures I've reached an efficiency of 94.212% (n=129 measures).
(Note: the class does not check for ambigious labels, so if for example you define Wall A twice the result will be incorrect.)
Conclusion: for both the ceiling and the wall skirtings I can order one less skirting than my manual attempt to spread them efficiently.
Looks to me like a variation on the Bin Packing Problem where you're trying to pick the combination of elements that make up 480 (or just under). This is a fairly computationally hard problem and depending on how efficient/accurate it needs to be, might be overkill trying to get it exact.
A rough heuristic could be just to sort the measures, keep adding the smallest ones into a unit until the next one makes you go over, then add to a new unit and repeat.
Honestly, i have a testing calculate how many containers of a given size and weight capacity it takes to pack the specified goods. But i'm really bad at algorithm, so i post this to looking some hint from you guys. Any help is appreciate. Thank you guys !
I have three container sizes and their capacities like this :
Container Size : Large
Maximum Volume (m3) : 76
Maximum Weight (kg) : 29,600
Container Size : Medium
Maximum Volume (m3) : 67.3
Maximum Weight (kg) : 27,397
Container Size : Small
Maximum Volume (m3) : 33
Maximum Weight (kg) : 22,100
Given a total weight and volume for goods in a shipment, the algorithm must output the most efficient combination of containers to pack the goods in. The system should use larger containers where possible, but empty space should be minimised.
For input of volume 213m3 and weight 22,421kg, the expected output is:
array(
‘L’ => array(
‘quantity’ => 2,
‘volume’ => 152,
‘weight’ => 16000
),
‘M’ => array(
‘quantity’ => 1,
‘volume’ => 61,
‘weight’ => 6421
),
‘S’ => array(
‘quantity’ => 0,
‘volume’ => 0,
‘weight’ => 0
),
)
For input of volume 182m3 and weight 19,158kg, the expected output is:
array(
‘L’ => array(
‘quantity’ => 2,
‘volume’ => 152,
‘weight’ => 16000
),
‘M’ => array(
‘quantity’ => 0,
‘volume’ => 0,
‘weight’ => 0
),
‘S’ => array(
‘quantity’ => 1,
‘volume’ => 30,
‘weight’ => 3158
),
)
I can't understand how it work ....
So please hint me.
Thank you.
The problem you are looking to solve is a well known NP-complete problem called as the knapsack problem. Here is the wikipedia link describing that problem :-
The best recommended way would be some sort of heuristics. Here's a paper that describes some useful heuristics to solve the knapsack problem.
Edit :- To explain a bit further, NP-complete problems are a known category of problems for which no efficient solution exists. i.e., there is no algorithm that is both fast and correct. Hence you have to use some kind of heuristic approximation algorithms to solve it that are both fast and reasonably correct.
What kind of heuristic would be best suited for your problem is probably out of the scope of stackoverflow. I've given you some resources and you can search more on this rather well studied problem.
I have an array of scores and each score is weighted for importance. I would like to get an overall score out of 100 depending on the score and the weight giving to each item.
$array_one = array(
array(
'score' => 1.23,
'max' => 10,
'weight' => 10
),
array(
'score' => 56.78,
'max' => 100,
'weight' => 20
),
array(
'score' => 7.56,
'max' => 10,
'weight' => 20
),
array(
'score' => 4.67,
'max' => 10,
'weight' => 30
)
);
So if an item has a larger weight, it is giving a bigger percentage in the score out of 100. i.e if I get 4 scores equaling 50% of the max value in the range, but they all increase in weight 10,20,30,40, the second score has 10% more weight than the first, the third 20% more weight than the first, and the 4th 30% more weight than the first and so on. Also, some weights will be the same 10,20,20,30 or 10,10,10,10 etc. I hope this makes sense. So in summary each items score should be a percentage of the max value, then scores are weighted against 100. Expected results should return one overall score for all 4 items.
Sum the scores divided by max multiplied by their weight. Take the sum of weights and divide by your max score (100). Then, divide the first number by the second number. This will be the total weighted score mapped to the [0, 100] range.
I have the following values from a database call that I want to apply some logic to. I thought I could originally use PHP's max however this doesn't appear to be the case.
I have three suppliers of a product. They might not all stock the item I am displaying, and they all offer a different margin, on a product by product basis though, so that is why I can't just say generally supplier 1 is better than supplier 2 etc.
$supplier1Live = 1
$supplier2Live = 1
$supplier3Live = 0
$marginSupplier1 = 20
$marginSupplier2 = 40
$martinSupplier3 = 50
In this example I would want to use Supplier 2 as they stock the product supplier2Live = 1 and also have the better margin than the other supplier who stocks the product (supplier1)
My mind however is drawing a complete blank in how to code this?
I thought I could add it to an array giving:
$array = array(
"supplier1" => array(
"live" => 1,
"margin" => 20
),
"supplier2" => array(
"live" => 1,
"margin" => 40
),
"supplier3" => array(
"live" => 0,
"margin" => 50
)
);
And run something on that, but not sure what to.
Filter the array using array_filter (filter by live==1), and then find the maximum out of the resultant array (maximum on the "margin" value)
Like this, if I understand correctly
$array = array(
"supplier1" => array(
"live" => 1,
"margin" => 20
),
"supplier2" => array(
"live" => 1,
"margin" => 40
),
"supplier3" => array(
"live" => 0,
"margin" => 50
)
);
$res = array_filter($array,function($v){return $v["live"];});
$supplier = array_reduce($res, function($a, $b){
return $a["margin"]>$b["margin"]?$a:$b;
});
print_r($supplier);
Try something like this:
$best_supplier = null;
$best_supplier_margin = null;
foreach($array as $name => $supplier) {
if($supplier['live']) {
if($supplier['margin'] > $best_supplier_margin || is_null($best_supplier_margin)) {
$best_supplier = $name;
$best_supplier_margin = $supplier['margin'];
}
}
}
if(is_null($best_supplier)) throw new Exception('No suppliers are live!');
echo $best_supplier;
So you basically want to find the max of supplierXLive * marginSupplierX?
You can also implement a custom compare function and provide it to PHPs usort() function