PHP Random Team Schedule Generator - Round Robin Scheduler - php

After getting negative feedback from asking this question in a new question... here is my revised question. Yes it is the same project I am working on, but I was unclear that I needed to basically have a Round Robin type of scheduler.
I'm working on a Round Robin Style Hockey League Scheduler, and need some help.
The overall goal is for the end admin user to be able to punch in 3 variables and have it perform a round Robin style schedule until the WEEKS counter has been hit. Below is an example of the amount of teams and the amount of weeks games are played.
$Teams = array('team1','team2','team3','team4','team5','team6','team7','team8');
$Weeks = 16;
The goal is to have it loop 16 times, making 4 games a week, having each team playing 1 time a week. The round robin algorithm should have teams playing different teams each week until all possibles combinations have been made, but not exceeding 16 weeks. In the event that we only have 4 teams or less teams than possible combinations, we would need to have the round robin start over again until the weeks number was hit.
EDIT:
I am about 90% into what I needed this script to do... but I am stuck on one thing. I need help with merging a multi-dimensional array.
First are the Tiers. Next are the Weeks (all are week 1). Then are the Games for the team match up.
Array
(
[1] => Array
(
[1] => Array
(
[1] => Array
(
[home] => Whalers
[visitor] => Lumberjacks
)
[2] => Array
(
[home] => Team America
[visitor] => Wolfpack
)
)
)
[2] => Array
(
[1] => Array
(
[1] => Array
(
[home] => Warriors
[visitor] => Litchfield Builders
)
[2] => Array
(
[home] => Icemen
[visitor] => Nighthawks
)
)
)
[3] => Array
(
[1] => Array
(
[1] => Array
(
[home] => The Freeze
[visitor] => Devils Rejects
)
[2] => Array
(
[home] => Cobras
[visitor] => New Haven Raiders
)
[3] => Array
(
[home] => Crusaders
[visitor] => Whalers
)
[4] => Array
(
[home] => Blizzard
[visitor] => CT Redlines
)
)
)
)
I want the end result to drop the tier and merge all same weeks games together to look like the following:
Array
(
[1] => Array
(
[1] => Array
(
[home] => Whalers
[visitor] => Lumberjacks
)
[2] => Array
(
[home] => Team America
[visitor] => Wolfpack
)
[3] => Array
(
[home] => Warriors
[visitor] => Litchfield Builders
)
[4] => Array
(
[home] => Icemen
[visitor] => Nighthawks
)
[5] => Array
(
[home] => The Freeze
[visitor] => Devils Rejects
)
[6] => Array
(
[home] => Cobras
[visitor] => New Haven Raiders
)
[6] => Array
(
[home] => Crusaders
[visitor] => Whalers
)
[8] => Array
(
[home] => Blizzard
[visitor] => CT Redlines
)
)
)

Maybe something like this?
<?php
$teams = array(
'Team 1',
'Team 2',
'Team 3',
'Team 4',
'Team 5',
'Team 6',
'Team 7',
'Team 8'
);
function getMatches($teams) {
shuffle($teams);
return call_user_func_array('array_combine', array_chunk($teams, sizeof($teams) / 2));
}
for ($i = 0; $i < 14; $i += 1) {
print_r(getMatches($teams));
}
I didn't really get how you define the schedule, so if you can explain this a bit, I'll try to help.

Pop one off, randomize, pop another. There's your game. If one is left over, some random team has to be a workhorse and play two games this week:
for ($week=1; $i<=$totalWeeksPlayed; $i++)
{
$games = 0;
$temp = $teams;
while (count($temp) > 1)
{
$team = array_shift($temp);
shuffle($temp);
$opponent = array_shift($temp);
$game[$week][$games] = $team . ' vs' . $opponent;
$games++;
}
if (count($temp) == 1)
{
$workhorses = $teams;
unset($workhorses[array_search($temp[0], $teams));
shuffle($workhorses);
$team = $temp[0];
$opponent = array_shift($workhorses);
$game[$week][$games] = $team . ' vs' . $opponent;
$games++;
}
}

Question below copied from above.
Correct me if I get this wrong, but if all teams have to play on the same regular basis, is it even possible to have all teams play the same amount of matches, if there is an odd number of teams? – Yoshi May 3 '11 at 15:05
Michelle,
The number of teams you are trying to pair-up (in this case 8 teams for 16 weeks) can be a daunting task, and is just the beginning of the "scheduling process". Once the correct, balanced team pairings have been determined, it's just the beginning of putting a schedule together for distribution. Next, a list of the 4 weekly time slots includes the; day of the week, start time and location name for each time slot for the whole 16 week season. Comment: What would be most helpful for you is to get an 8 team scheduling matrix that has balanced opponent, and home & away status. It makes a big difference in the quality of a schedule. It's important to evenly distribute early and late time slots, equal home & away status, and equal team distribution with opponents. Most of the balance is accomplished by using a balanced team pair matrix.
After 35 years of teaching, coaching and scheduling sports in the Boston area, I can offer the following information. Creating league or game schedules for sports organizations seems to be a never ending task shared by many and is repeated over and over as the ages of the participants grow and those running the leagues change, the learning curve associated with creating schedules is significant for those who take over.
With that said, I am amazed at how many highly educated mathematical wizards are involved with trying to come up with the perfect solution (algorithm) that will solve one's scheduling problem. Myself and a friend (who is a mathematical/programmer genius) over a period of 3 years created software that perfectly balances all the important components when creating schedules for 4 to 22 teams. What we learned is that there is no algorithm that can handle all the possible variables that are added to the normal variables to create balanced schedules. What I am saying is there are just as many "what ifs" as there are mathematical permutations and combinations that deal with just creating a team matrix listing opponents and home & visitor status of games.
For example: Let's create a perfectly balanced schedule for an 9 team division playing 4 games a week. After nine weeks all teams have played played 8 games, all have had 1 bye, all have played two times in each of the 4 time slots, and all have been scheduled as the home team 4 times and the visitor team 4 times.
What more could anybody want? Well now comes the fun. Because the 4 time slots you chose has 2 games each Saturday, and 2 games each Sunday, the first problem pops up (after the schedules are created, published and passed out, when 2 coaches from 2 different teams call and say they work Saturdays, can you move our games to Sundays? Sure, I can do that. I'll just make the changes, re-publish and re-distribute the schedules.
After the new schedules are distributed, several days later, a different coach calls up and says, "Hey, you moved some of my games to Saturday, I work Saturdays.., move them back". The phone rings again. This time a it's a coach from another team and says they are in a tournament the 5th week of the schedule and can't play that week. Finally the last call comes in from yet another coach. He says the parents of one of his players teaches CCD classes Sunday afternoons, and half of his team is in the CCD class and wants to move all our Sunday games to Saturday. Enough!
My point is no matter what you do or how perfect a schedule is, the best solution is to find out as many of the player/coach team limitations or restrictions before you assign the playing days and time slots to any schedule. These unpredictable variables makes a mess out of a perfect schedule. People do get angry and complain when undesirable schedules are distributed. When a schedule is bad enough, some parents won't sign their youngster to play the following year. This happens when you have a young family with two or three small children, and Dad's work limits his ability to be there. When there is an early morning game, I think you can see the difficulty for Mom's when it all falls on her shoulders.
For those that are new to scheduling, stay with it. It gets better over time after you gain
a little experience dealing with the problems. If you purchase a scheduling software program to calculate team pairs, be careful. Insist on seeing a full single round robin of the schedule they create. Check for the things described above (about balance and distribution).
Bob R

Given a table of teams, say
team (
teamname
);
And a table of fixtures
fixture (
date;
);
With the relationship decomposed by 'playing'
playing (
fixture_date.
teamname
);
Then it's would simply be a matter of iterating through each date, then team and selecting a team at random who does not already have a fixture for that date, and who have not played the selected team (or not played the selected team recently).
Although a simpler solution would be to have team[n] (where n is 0....number of teams -1) play team[(n+(number of teams)) % X] for varying values of X.

Related

php mysql check if vendor has 3 low consecutive ratings

on my ratings table for my software i have 4 fields.
id autoincrement
rvid vendor id
ratedate date of rating
rating the actual numeric rating
I have done alot with it over the last few months but this time im stumped and i cant get a clear picture in my head of the best way to do this. What i am trying to do is find out if the vendor has had 3 low 'consecutive' ratings. If their last three ratings have been < 3 then i want to flag them.
I have been playing with this for a few hours now so i thought i would ask (not for the answer) but for some path direction just to push me forward, im stuck in thought going in circles here.
I have tried GROUP BY and several ORDER BY but those attempts did not go well and so i am wondering if this is not a mysql answer but a php answer. In other words maybe i just need to take what i have so far and just move to the php side of things via usort and the like and do it that way.
Here is what i have so far i did select id as well at first thinking that was the best way to get the last consective but then i had a small breakthrough that if they have had 3 in a row the id does not matter, so i took it out of the query.
$sql = "SELECT `rvid`, `rating` FROM `vendor_ratings_archive` WHERE `rating` <= '3' ORDER BY `rvid` DESC";
which give me this
Array
(
[0] => Array
(
[rvid] => 7
[rating] => 2
)
[1] => Array
(
[rvid] => 5
[rating] => 1
)
[2] => Array
(
[rvid] => 5
[rating] => 0
)
[3] => Array
(
[rvid] => 5
[rating] => 3
)
)
this is just just samples i tossed in the fields, and there are only 4 rows here where as in live it will be tons of rows. But basically this tells me that these are the vendors that have low ratings in the table. And that is where i get stumpted. I can only do one sort in the query so that is why i am thinking that i need to take this and move to the php side to finish it off.
I think i need to sort the elements by rvid with php first i think, and then see if three elements in a row are the same vender (rvid).
Hope that makes sense. My brain hurts lol...
update - here is all of the table data using *
Array
(
[0] => Array
(
[id] => 7
[rvid] => 7
[ratedate] => 2016-05-01
[rating] => 2
)
[1] => Array
(
[id] => 8
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 1
)
[2] => Array
(
[id] => 6
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 0
)
[3] => Array
(
[id] => 5
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 3
)
)
Here's one way you can begin approaching this - completely in SQL:
Get the last rating for the vendor. ORDER BY date DESC, limit 1.
Get the second to last rating for the vendor. ORDER BY date DESC, limit 1, OFFSET 1.
Then write a query that does a LEFT join of the first two tables. You will have a dataset that has three columns:
vendor id
last rating
second to last rating
Then you can write an expression that says "if column1 is <3 and column2 < 3, then this new column is true"
You should be able to extend this to three columns relatively easily.
Here is what a came up with to solve this riddle. I think explaining it on here helped as well as Alex also helped as he keyed my brain on using the date. I first started looking at using if statment inside of the query and actually that got my brain out of the box and then it hit me what to do.
It is not perfect and certainly could use some trimming to reduce the code, but i understand it and it seems to work, so that is par for me on this course.
the query...
$sql = "SELECT `rvid`, `ratedate`,`rating` FROM `vendor_ratings_archive` WHERE `rating` <= '3' ORDER BY `ratedate`, `rvid` DESC";
which gives me this
Array
(
[0] => Array
(
[rvid] => 7
[ratedate] => 2016-05-01
[rating] => 2
)
[1] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 1
)
[2] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 0
)
[3] => Array
(
[rvid] => 5
[ratedate] => 2016-05-01
[rating] => 3
)
)
notice how vendor (rvid) 5 is grouped together which is an added plus.
next a simple foreach to load a new array
foreach($results as $yield)
{
$rvidarray[] = $yield['rvid'];
}//close foreach
which gives me this
Array
(
[0] => 7
[1] => 5
[2] => 5
[3] => 5
)
then we count the array values to group dups
$rvidcounter = array_count_values($rvidarray);
which results in this
Array(
[7] => 1
[5] => 3
)
so now vender 7 as 1 low score and vendor 5 has 3 low scores and since they were already sorted by date i know that its consecutive. Well it sounds good anyway lol ")
then we create our final array with another foreach
foreach($rvidcounter as $key => $value)
{
//anything 3 or over is your watchlist
if($value > 2)
{
$watchlist[] = $key; //rvid number stored
}
}//close foreach
which gives me this
Array
(
[0] => 5
)
this was all done in a service function. So the final deal is everyone in this array has over 3 consecutive low ratings and then i just use a returned array back in my normal php process file and grab the name of each vender by id and pass that to the html and print out the list.
done...
please feel free to improve on this if you like. I may or may not use it because the above code makes sense to me. Something more complicated may not make sense to me 6 mos from now lol But it would be interesting to see what someone comes up with to shorten the process a bit.
Thanks so much and Happy Coding !!!!!
Dave :)
You could do it in SQL like that:
SET #rvid = -1;
SELECT DISTINCT rvid FROM
(
SELECT
rvid,
#neg := rating<3, /* 0 or 1 for addition in next line */
#count := IF(#rvid <> rvid , #neg, #count+#neg) AS `count`, /* set or add */
#rvid := rvid /* remember last row */
FROM
testdb.venrate
ORDER BY
rvid, datetime desc
) subq
WHERE count>=3
;
You set a variable to a non existing id. In each chronologically sorted row you check if rating is too low, that results in 1 (too low) or 0 (ok). If rvid is not equal to the last rvid, it means a new vender section is beginning. On begin of section set the value 0 or 1, else add this value. Finally store the current row's rvid for comparison in next row process.
The code above is looking for 3 consecutive low ratings (low means a value less than 3) over all the time.
A small modification checks if all the latest 3 ratings has been equal to or less than 3:
SET #rvid = -1;
SELECT DISTINCT
rvid
FROM
(
SELECT
rvid,
#high_found := rating>3 OR (#rvid = rvid AND #high_found) unflag,
#count := IF(#rvid <> rvid , 1, #count+1) AS `count`,
#rvid := rvid /* remember last row */
FROM
testdb.venrate
ORDER BY
rvid, datetime desc
) subq
WHERE count=3 AND NOT unflag
;

Loop on Codeigniter

I have a table that contains an unknown number of elements. These represent business sectors.
Array ([0] => 1 [1] => 3 [2] => 6 [3] => 7)
In this case this activity is linked to topics 1, 3, 6 and 7.
I Have some members who have indicated their fields of interest when they register. Interest fields correspond to sectors of activities.
When a new activity is announced, I would like to send an email to members who have checked at least one common interest with the activity. But I have no idea how to retrieve the email of all those members().
In the database the interests of members is stored like this: 246
The first member would be interested in sectors 2, 4 and 6.
I know how to do if there is only one business sector for the activity (example if value = 1).
function readMemberInterest($value) {
$datas = $this->db->select("email")
->from($this->table)
->like('interet',$value,'both');
return $datas;
}
But I do not know where and how to place my LOOP and my OR (is for example value = Array ([0] => 1 1 => 3 [2] => 6 [3] => 7) ).
Briefly, I want to retrieve email members who have at least one common interest with activity. Any tips?
I hope to be clear, my English is not so great ...
Thanks

PHP - Ranking in different dates starting from multidimensional array

I'm storing in a multi-dimensional array the progressive score of n players in different days.
So I've something like
Array
(
[0] => Array
(
[day] => 2014-10-01
[player] => John
[score] => 1500
)
[1] => Array
(
[day] => 2014-10-02
[player] => John
[score] => 1510
)
[2] => Array
(
[day] => 2014-10-01
[player] => Mary
[score] => 1400
)
[3] => Array
(
[day] => 2014-10-02
[player] => Mary
[score] => 1600
)
)
What I need a day-by-day rank for a given player, comparing his score with all other players's one, for each day.
So the input will be player's id (in this case "John") and the output should be a JSON object like
[{"day"="2014-10-01", "rank": 1}, {"day"="2014-10-02", "rank": 2}]
What's the best way to approach this problem?
To give you an idea of the dimension of my data, consider that I've the scores of 100 players in 100 different days (so I've a table of approximately 10.000 records).
EDIT (27/11/14): the multi-dimensional array is the result of a pre-processing on data stored in
a MySQL database.
This database contains information about each game played in this form:
|Date |WinnerId|LoserId|PtsEarned|PtsLost|
|2014-10-01|John |Mary |10 |-10 |
|2014-10-02|Mary |John |20 |-20 |
So I don't know If I can easily use the method suggested in the "duplicate" answer.
With my first approach, it takes about 7-8 seconds to output the result I need... so I think It's not the most efficient way to face the problem...
EDIT (28/11/14): I tried ranking data directly in MySql, and it takes about 2 seconds to give me back the ranking position of one player in a specific day; repeating the same query 100 times is a way too long process...
With a different approach I manage to create a php array with "pre-processed" data in the same time (2 seconds), but I miss the last step, i.e. how to do a rank using php functions on a multi-dimensional array (my first question). Any idea?

PHP array and foreach help

I need some help with an array and a foreach loop,
I have an array that is data returned from a database, basically it is a list of titles and images associated with it, it looks a little like this,
Array
(
[0] => Array
(
[contentImageId] => 28
[contentImageName] => yellow.png
[contentImageType] => .png
[contentImagePath] => /var/www/bangmarketing.bang/media/uploads/contentImages/7/yellow.png
[isHeadlineImage] => 1
[contentImageDateUploaded] => 1265388055
[contentId] => 7
[dashboardUserId] => 0
[contentTitle] => Another Blog
[contentAbstract] => <p>This is another blog and it is shit becuase this does not work</p>
[contentBody] => <p>ioasfihfududfhdufhuishdfiudshfiudhsfiuhdsiufhusdhfuids</p>
[contentOnline] => 1
[contentAllowComments] => 0
[contentDateCreated] => 1265388034
[categoryId] => 1
[categoryTitle] => blogsss
[categoryAbstract] => <p>asdsdsadasdsadfdsgdgdsgdsgssssssssssss</p>
[categorySlug] => blog
[categoryIsSpecial] => 0
[categoryOnline] => 1
[categoryDateCreated] => 1266588327
)
[1] => Array
(
[contentImageId] => 31
[contentImageName] => sophie.jpg
[contentImageType] => .jpg
[contentImagePath] => /var/www/bangmarketing.bang/media/uploads/contentImages/9/sophie.jpg
[isHeadlineImage] => 1
[contentImageDateUploaded] => 1265713423
[contentId] => 9
[dashboardUserId] => 0
[contentTitle] => Bang appoints a new Senior Designer
[contentAbstract] => <p>Bang has appointed a new Senior Designer in the wake of a number of major client wins.</p>
[contentBody] => <p>After recently signing up clients including the National Trust and Propaganda, Bang has hired Sophie Smith to join its creative team.</p>
<p>Sophie brings over twelve years of extensive design experience in Bang's core market sectors, including social housing and public sector brands.</p>
<p>Sophie has significant experience of branding and strategic design projects for a broad range of clients, including HBoS, Harvey's, TKMaxx and Places for People.</p>
<p>Warren Watts Creative Director at Bang commented; "Sophie has already proven an invaluable addition to our growing studio. In only her second week, she helped produce the creative that led to the National Trust account win".</p>
[contentOnline] => 1
[contentAllowComments] => 0
[contentDateCreated] => 1265713155
[categoryId] => 8
[categoryTitle] => News
[categoryAbstract] => <p>The world at Bang Marketing moves fast, keep up to date w
[categorySlug] => news
[categoryIsSpecial] => 0
[categoryOnline] => 1
[categoryDateCreated] => 1265283717
)
[2] => Array
(
[contentImageId] => 32
[contentImageName] => selectadna.jpg
[contentImageType] => .jpg
[contentImagePath] => /var/www/bangmarketing.bang/media/uploads/contentImages/10/selectadna.jpg
[isHeadlineImage] => 1
[contentImageDateUploaded] => 1265713937
[contentId] => 10
[dashboardUserId] => 0
[contentTitle] => Bang client experiences growth
[contentAbstract] => <p>Selectamark, the UK's leading asset protection specialist recorded its best figures for five years in 2008.</p>
[contentBody] => <p>The results were released during an extremely challenging economic climate, and just over 12 months into the organisation's relationship with Bang.</p>
<p>Bang has been working on developing Selectamark's range of product brands, which include the UK's leading asset tracking and labelling system Selectamark, and second generation forensic marking system, SelectaDNA. Despite gloomy national economic forecasts, Selectamark has enjoyed an extremely successful start to 2009 – picking up several major new clients in February, and with discussions taking place to expand operations beyond the UK and Netherlands to Germany and New Zealand.</p>
<p>Chris Garratt, Marketing Director at Bang Marketing Ltd explained "Selectamark adopted a well planned, coordinated and consistent strategy in 2008, and the results speak for themselves.</p>
<p>"Due to the success last year and the forecasts for increased levels of acquisitive crime we're hoping Selectamark's key products will enjoy similar growth in 2009".</p>
<p>Selectamark Security Systems provides DNA Forensic Marking products (SelectaDNA) and Visible Marking solutions (Selectamark and Logomark) to prevent burglaries, theft from void properties, theft of boilers, and theft of valuable metals such as lead from roofs. Selectamark have supplied these Secured By Design crime prevention products to Police, local authorities and housing associations for 25 years.</p>
<p>The Selectamark system has now marked over 25 million items, and is patented and distributed world-wide. Visit www.selectadna.co.uk for product details.</p>
<p>James Brown, Sales Director at Selectamark said "We have been absolutely delighted with the work Bang has done for us. In a short space of time, the Bang team has created a wide range of extremely fresh and professional-looking sales and marketing material for us to use, as well as devising a detailed marketing strategy which has now been implemented. We would have no hesitation in recommending Bang to other organisations."</p>
[contentOnline] => 1
[contentAllowComments] => 0
[contentDateCreated] => 1265713888
[categoryId] => 8
[categoryTitle] => News
[categoryAbstract] => <p>The world at Bang Marketing moves fast, keep up to date w
[categorySlug] => news
[categoryIsSpecial] => 0
[categoryOnline] => 1
[categoryDateCreated] => 1265283717
)
)
As you can see the contentTitle changes, everytime but sometime is returns the same content title, I am looking for a way to add a divide between the returned data, if the content Title value changes?
Is this possible? and how? I am really struggling here.
Thanks
As you loop through, you can have a temporary variable to store the last displayed title
$lastTitle='';
foreach ($array as $record)
{
if ($lastTitle != $record['contentTitle'])
echo '<hr>';
$lastTitle = $record['contentTitle');
// display content from record
}
If I understand you correctly, you want to display a divider every time the contentTitle is different from the value it had in the last iteration.
You're making a kind of gallery here, aren't you?
If so, I would go about it like this:
$lastTitle = '';
foreach($yourArray as $value)
{
if(!$lastTitle || $lastTitle != $value['contentTitle'])
{
echo '<h2>' . $value['contentTitle'] . '</h2><hr />'; // Display divider
// Do some other stuff
}
$lastTitle = $value['contentTitle'];
// Display image or whatever you want to do
}

Find the best combination from a given set of multiple sets

Say you have a shipment. It needs to go from point A to point B, point B to point C and finally point C to point D. You need it to get there in five days for the least amount of money possible. There are three possible shippers for each leg, each with their own different time and cost for each leg:
Array
(
[leg0] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 5000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 5
[cost] => 1000
)
)
[leg1] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 3000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 3
[cost] => 1000
)
)
[leg2] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 4000
)
[FedEx] => Array
(
[days] => 1
[cost] => 3000
)
[Conway] => Array
(
[days] => 2
[cost] => 5000
)
)
)
How would you go about finding the best combination programmatically?
My best attempt so far (third or fourth algorithm) is:
Find the longest shipper for each leg
Eliminate the most "expensive" one
Find the cheapest shipper for each leg
Calculate the total cost & days
If days are acceptable, finish, else, goto 1
Quickly mocked-up in PHP (note that the test array below works swimmingly, but if you try it with the test array from above, it does not find the correct combination):
$shippers["leg1"] = array(
"UPS" => array("days" => 1, "cost" => 4000),
"Conway" => array("days" => 3, "cost" => 3200),
"FedEx" => array("days" => 8, "cost" => 1000)
);
$shippers["leg2"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$shippers["leg3"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$times = 0;
$totalDays = 9999999;
print "<h1>Shippers to Choose From:</h1><pre>";
print_r($shippers);
print "</pre><br />";
while($totalDays > $maxDays && $times < 500){
$totalDays = 0;
$times++;
$worstShipper = null;
$longestShippers = null;
$cheapestShippers = null;
foreach($shippers as $legName => $leg){
//find longest shipment for each leg (in terms of days)
unset($longestShippers[$legName]);
$longestDays = null;
if(count($leg) > 1){
foreach($leg as $shipperName => $shipper){
if(empty($longestDays) || $shipper["days"] > $longestDays){
$longestShippers[$legName]["days"] = $shipper["days"];
$longestShippers[$legName]["cost"] = $shipper["cost"];
$longestShippers[$legName]["name"] = $shipperName;
$longestDays = $shipper["days"];
}
}
}
}
foreach($longestShippers as $leg => $shipper){
$shipper["totalCost"] = $shipper["days"] * $shipper["cost"];
//print $shipper["totalCost"] . " <?> " . $worstShipper["totalCost"] . ";";
if(empty($worstShipper) || $shipper["totalCost"] > $worstShipper["totalCost"]){
$worstShipper = $shipper;
$worstShipperLeg = $leg;
}
}
//print "worst shipper is: shippers[$worstShipperLeg][{$worstShipper['name']}]" . $shippers[$worstShipperLeg][$worstShipper["name"]]["days"];
unset($shippers[$worstShipperLeg][$worstShipper["name"]]);
print "<h1>Next:</h1><pre>";
print_r($shippers);
print "</pre><br />";
foreach($shippers as $legName => $leg){
//find cheapest shipment for each leg (in terms of cost)
unset($cheapestShippers[$legName]);
$lowestCost = null;
foreach($leg as $shipperName => $shipper){
if(empty($lowestCost) || $shipper["cost"] < $lowestCost){
$cheapestShippers[$legName]["days"] = $shipper["days"];
$cheapestShippers[$legName]["cost"] = $shipper["cost"];
$cheapestShippers[$legName]["name"] = $shipperName;
$lowestCost = $shipper["cost"];
}
}
//recalculate days and see if we are under max days...
$totalDays += $cheapestShippers[$legName]['days'];
}
//print "<h2>totalDays: $totalDays</h2>";
}
print "<h1>Chosen Shippers:</h1><pre>";
print_r($cheapestShippers);
print "</pre>";
I think I may have to actually do some sort of thing where I literally make each combination one by one (with a series of loops) and add up the total "score" of each, and find the best one....
EDIT:
To clarify, this isn't a "homework" assignment (I'm not in school). It is part of my current project at work.
The requirements (as always) have been constantly changing. If I were given the current constraints at the time I began working on this problem, I would be using some variant of the A* algorithm (or Dijkstra's or shortest path or simplex or something). But everything has been morphing and changing, and that brings me to where I'm at right now.
So I guess that means I need to forget about all the crap I've done to this point and just go with what I know I should go with, which is a path finding algorithm.
Could alter some of the shortest path algorithms, like Dijkstra's, to weight each path by cost but also keep track of time and stop going along a certain path if the time exceeds your threshold. Should find the cheapest that gets you in under your threshold that way
Sounds like what you have is called a "linear programming problem". It also sounds like a homework problem, no offense.
The classical solution to a LP problem is called the "Simplex Method". Google it.
However, to use that method, you must have the problem correctly formulated to describe your requirements.
Still, it may be possible to enumerate all possible paths, since you have such a small set. Such a thing won't scale, though.
Sounds like a job for Dijkstra's algorithm:
Dijkstra's algorithm, conceived by Dutch computer scientist Edsger Dijkstra in 1959, 1 is a graph search algorithm that solves the single-source shortest path problem for a graph with non negative edge path costs, outputting a shortest path tree. This algorithm is often used in routing.
There are also implementation details in the Wikipedia article.
If I knew I only had to deal with 5 cities, in a predetermined order, and that there were only 3 routes between adjacent cities, I'd brute force it. No point in being elegant.
If, on the other hand, this were a homework assignment and I were supposed to produce an algorithm that could actually scale, I'd probably take a different approach.
As Baltimark said, this is basically a Linear programming problem. If only the coefficients for the shippers (1 for included, 0 for not included) were not (binary) integers for each leg, this would be more easily solveable. Now you need to find some (binary) integer linear programming (ILP) heuristics as the problem is NP-hard.
See Wikipedia on integer linear programming for links; on my linear programming course we used at least Branch and bound.
Actually now that I think of it, this special case is solveable without actual ILP as the amount of days does not matter as long as it is <= 5. Now start by choosing the cheapest carrier for first choice (Conway 5:1000). Next you choose yet again the cheapest, resulting 8 days and 4000 currency units which is too much so we abort that. By trying others too we see that they all results days > 5 so we back to first choice and try the second cheapest (FedEx 2:3000) and then ups in the second and fedex in the last. This gives us total of 4 days and 9000 currency units.
We then could use this cost to prune other searches in the tree that would by some subtree-stage result costs larger that the one we've found already and leave that subtree unsearched from that point on.
This only works as long as we can know that searching in the subtree will not produce a better results, as we do here when costs cannot be negative.
Hope this rambling helped a bit :).
This is a knapsack problem. The weights are the days in transit, and the profit should be $5000 - cost of leg. Eliminate all negative costs and go from there!
I think that Dijkstra's algorithm is for finding a shortest path.
cmcculloh is looking for the minimal cost subject to the constraint that he gets it there in 5 days.
So, merely finding the quickest way won't get him there cheapest, and getting there for the cheapest, won't get it there in the required amount of time.

Categories