Cargo Algorithm in PHP - php

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.

Related

Algorithm to find best combination of numbers - Bin Packing Prob lem

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.

Compound Indexes on MongoDB

Sorry for my english, I need help on mongodb indexes. I have a capped collection (size: 10GB) with some fields for my application logs.
Example structure: Logs[_id, userId, sum, type, time, response, request]. I have created compound index: [userId,time,type]. I get two arrays are grouped records by userId for today, where 'type' is "null" and "1". And my two query example:
$group = array(
array(
'$match' => array(
'userId' => $userId,
'time' => array(
'$gt' => date("Y-m-d")
),
'type' => array('$ne' => null)
)
),
array(
'$group' => array(
"_id" => '$userId',
"total" => array('$sum' => '$sum'),
"count" => array('$sum' => 1)
),
)
);
$results = $collections->aggregate($group);
$group = array(
array(
'$match' => array(
'userId' => $userId,
'time' => array(
'$gt' => date("Y-m-d")
),
'type' => 1
)
),
array(
'$group' => array(
"_id" => '$userId',
"count" => array('$sum' => 1)
),
)
);
$results2 = $collections->aggregate($group);
If current user has more 100000 documents on collection for today - the speed of my query is very slow (more 10 sec). Give me some advices on creating the right index, please :) Thanks.
Based on the explain that you posted, the correct index is being used (BtreeCursor), it is using only the index (i.e. it is a covered index query - indexOnly is true) and nothing is being matched (n = 0) in this case. So, that all checks out generally, though $ne as a clause in the first example is not going to be very efficient.
However the main issue based on the explain is likely the fact that the index does not appear to be fully in memory. There are 13 yields listed and the most common reason for a query like this to yield is when it has to fault to disk to page something in. Since, as mentioned previously, it is only using the index, those yields imply faults to disk for the index and hence indicate that the whole index is not in memory.
If you re-run the query immediately after this it should be faster (assuming the index can actually fit into available memory) because the index will have been paged in by the first run. If it is still slow on the second run and showing yields, then you either don't have enough memory to hold the index in memory or something else is evicting it from memory and you essentially have memory contention causing performance problems.

PHPExcel Background Color Logic

I have a very confusing issue with PHPExcel. I have 800 students. I'm generated a spreadsheet which lists how much praise (on a daily basis for the current month) that the student has has.
For instance, it may look like this:
+---------------+-----+-----+-----+-----+
| Student Name | 1st | 2nd | 3rd | 4th | ...
+---------------+-----+-----+-----+-----+
| Test Student | 2 | 0 | 3 | 7 |
+---------------+-----+-----+-----+-----+
I want to change the background color of the cells which are greater (or equal to) 5. I use a loop to loop over the students, and days. This is my code:
for($d=1; $d<=$daysInCMonth; $d++)
{
$phpExcel
->getSheetByName('Monthly Leaderboard')
->setCellValue($alphabetArray[($d+7)] . ($recordCount+5), $record['monthlyReport'][$MonthlyReportKeys[($d-1)]]);
if($record['monthlyReport'][$MonthlyReportKeys[($d-1)]]>=5)
{
$cellId = $alphabetArray[($d+7)] . ($recordCount+5);
$phpExcel
->getSheetByName('Monthly Leaderboard')
->getStyle($cellId)
->applyFromArray(
array('fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID,'color' => array('rgb' => '000000'))
));
}
}
To help understand the code, the initial for loop loops through from 1 up until the number of days in the current month (IE 30 for June). It then sets cells value as the number of points for each given day.
This all works perfectly. Now, the if condition will catch cells which have a value of greater (or equal to) 5.
The code $alphabetArray[($d+7)] . ($recordCount+5) grabs the current cell ID in the iteration. I know this works fine as well, because if I echo it to the screen, the first output is T5 which is a cell greater than 5.
If I implicitly specify T5 as the cell to color, it works fine. However, if I try to use the value of $cellId to dynamically color all cells for my condition, none of the cells are colored.
I know the cell ID is 100% correct, I know the coloring statement is correct (as it does color cells if I refer to them specifically). It just doesn't want to play dynamically.
Any ideas?
Thanks
Phil
This is quite an old question now, but I found it after having the same problem. After digging into the code I found something that does work. So thought I would add it in here for any future finder.
For conditional coloring of the background the method of just setting the color of the fill doesn't seem to work. e.g.
'fill' => array(
'type' => PHPExcel_Style_Fill::FILL_SOLID,
'color' => array(
'rgb' => 'FFC7CE'
),
)
The above works perfectly well when applied directly to a cell, but when used in a conditional styling. If just does nothing. However if you change it to
'fill' => array(
'type' => PHPExcel_Style_Fill::FILL_SOLID,
'startcolor' => array(
'rgb' => 'FFC7CE'
),
'endcolor' => array(
'rgb' => 'FFC7CE'
),
)
The background colors as expected. It looks like the conditional coloring of a background needs the start and end colors specified.
$headerStyle = array(
'fill' => array(
'type' => PHPExcel_Style_Fill::FILL_SOLID,
'color' => array('rgb'=>'00B4F2'),
),
'font' => array(
'bold' => true,
)
);
$borderStyle = array('borders' =>
array('outline' =>
array('style' => PHPExcel_Style_Border::BORDER_THICK,
'color' => array('argb' => '000000'), ),),);
//HEADER COLOR
$objPHPExcel->getActiveSheet()->getStyle('A1:'.'V1')->applyFromArray($headerStyle);
//SET ALIGN OF TEXT
$objPHPExcel->getActiveSheet()->getStyle('A1:V1')->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
$objPHPExcel->getActiveSheet()->getStyle('B2:V'.$row)->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
//BORDER TO CELL
$objPHPExcel->getActiveSheet()->getStyle('A1:'.'V1')->applyFromArray($borderStyle);
$borderColumn = (intval($column) -1 );
$objPHPExcel->getActiveSheet()->getStyle('A1:'.'V'.$borderColumn)->applyFromArray($borderStyle);

PHP: How will array depth influence performance?

Now I know there is some related questions on this topic but this is somewhat unique.
I have two array structures :
array(
[0] => array(
'stat1' => 50,
'stat2' => 12,
'stat3' => 0,
'country_name' => 'United States'
),
[1] => array(
'stat1' => 40,
'stat2' => 38,
'stat3' => 15,
'country_name' => 'Ireland'
),
[2] => array(
'stat1' => 108,
'stat2' => 0,
'stat3' => 122,
'country_name' => 'Autralia'
)
)
and the second
array(
'stat1' => array(
'countries' => array(
'United States' => 50,
'Ireland' => 40,
'Autralia' => 108,
)
)
),
'stat2' => array(
'countries' => array(
'United States' => 12,
'Ireland' => 38,
)
)
),
etc...
The second array can go even to level 4 or 5 if you add the cities of those respective countries. Further to note is that the second array structure will have no 0 data fields (note that in the second one australia is not there because it is 0) but the first structure will have a whole whack of zeros. Also note that the second structure will have duplicates i.e. 'United States'
My question is this: How does these array structures compare when they are json_encode() and used in a POST ajax request? Will the shallow depth array, with it's whack of zeros be faster or will the better structured array be better in terms of size?
I have done some testing and for a finite dataset the difference in the output data - (I outputted the data into a text file) between the two is insignificant really.
Array structure 1 - All city and country data outputs to 68kb
Array structure 2 - All city and country data outputs to 71kb
So there is a slight difference but it seems that the difference is insignificantly small when taking into account that the data is in JSON format and used in an AJAX request to the google visualization geomap API.
I haven't tested the micro times in loading difference but for a user to look at a loading .gif image for 0.0024microseconds (i'm shooting a random time for the sake of argument) does not make a big dent in usability either way. Thanx all for you comments

Calculate a rough estimate for shipping box size

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.

Categories