Algorithm for seating firefighters in vehicle seats - algorithm

I’m doing a project which involves placing volunteer firefighters, in seats when they arrive at the fire station to take the trucks.
Also some firefighters has drivers license some haven’t and some have a team leader education.
So my idea is to follow them by GPS and send the server a integer of distance to fire station and which education types each has.
Then every 5 sec, run the algorithm and based on the new GPS coordinates change their seats, until 1 is close to the station, and then mark is seat as taken.
This should happen until there is no empty seats or not anymore firefighters to call in or all called firefighters has arrived
The tricky thing I want help with (besides if my idea is the optimal), is to seat the firefighters most optimal.
I was thinking to make a prioritization list of possible roles.
And a prioritization list of vehicles which had to leave the station.
Then take the highest prioritized vehicle and the highest prioritized role, and fill it in with the closest firefighter which has the education.
But then if he is a driver but already set to the teamleaders seat, and only 1 more driver is coming, and there were more teamleaders coming, and two vehicles had to leave, it would be a wrong solution as the second vehicle couldn’t leave.
Again if Drivers and not teamleaders is the highest priority, then if the closest is set as driver, but is also the only one coming with a teamleader education, but more drivers are coming, then it would also be wrong.
Any ideas for the algorithm to work? Or does anybody knows an algorithm for this?

You're right, this type of greedy approach won't necessarily give you an optimal solution. Have you looked into/heard of Linear Programming or more specifically Integer programming: http://en.wikipedia.org/wiki/Integer_programming
In short integer programming is one technique for solving these types of scheduling problems. The problem has some objective you wish to maximise or minimise and is subject to various (linear) constraints. Integer because we can't have half a volunteer.
From your description the objective function could be to minimise the number of undeployed trucks and the total wait time. You could have different costs for different trucks to capture the different vehicle priorities.
Constraints would include each vehicle needing at least one driver, at least one team leader and that a volunteer can only be assigned to one vehicle. There might be other constraints you haven't described as well, such as no one can wait at the base for more than 20 minutes.
If you search for integer programming and scheduling you should find some code examples. You'll likely need a solver to assist. Wiki has a fairly comprehensive list, the choice will come down to your programming language preference and budget: http://en.wikipedia.org/wiki/Linear_programming#Solvers_and_scripting_.28programming.29_languages

How about something like this..
as you noticed the only conflicting situation is when you have a firefighter that can be driver and leader.. because the can only occupy one position.. but may be blocking another..
so don't start with them.. first start with the ones that have ether divers license OR Leaders education.. because those already have a predefined seat (the only they can take)..
after those are filled.. assign the ones that can do ether jobs, for the seats that are missing or replace some if they are nearer.
after having a queue of drivers and a queue of leaders.. sort them by distance to the firehouse and assign to trucks in pairs.. then fill the rest of the truck.. removing from a queue by order of ETA..
not sure if it will always give the best solution.. but seems quite optimal.. to to you wish some code? C#?
the question made me curious so. here is the code for what I was talking.. eve if you don't use it
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace FF
{
class Program
{
[Flags]
public enum Skill
{
None = 0,
Driver = 1,
TeamLeader = 2,
}
public class FireFighter : IComparable<FireFighter>
{
public int ID;
public Skill Skills;//the skills that he has
public Skill Assigned;//the one that he will be deployed with
public int ETA;
public override string ToString()
{
return ID + "(" + Skills + ")" + " # " + ETA + " minutes as " + this.Assigned;
}
int IComparable<FireFighter>.CompareTo(FireFighter other)
{
return this.ETA.CompareTo(other.ETA);
}
}
public class Truck
{
public int ID;
public int Capacity;
public List<FireFighter> Crew = new List<FireFighter>();
}
static int TotalStationTrucks = 8;
static List<Truck> Trucks = new List<Truck>();
static List<FireFighter> Team = new List<FireFighter>();
static Random Rnd = new Random();
static void Main(string[] args)
{
//create the sample data
int nAvailableSeats = 0;
for (int i = 0; i < TotalStationTrucks; i++)
{
Truck oTruck = new Truck();
oTruck.ID = i;
nAvailableSeats += oTruck.Capacity = Rnd.Next(4, 7);//seats between 4 - 6
Trucks.Add(oTruck);
}
for (int i = 0; i < nAvailableSeats * 2; i++)//add twice the amount of FF we need for all trucks
{
FireFighter oFireFighter = new FireFighter();
oFireFighter.ID = i;
oFireFighter.ETA = Rnd.Next(Int16.MaxValue);
Team.Add(oFireFighter);
}
for (int i = 0; i < Trucks.Count * 2; i++)//add twice the drivers we need
{
FireFighter oFireFighter = Team[Rnd.Next(Team.Count)];
oFireFighter.Skills |= Skill.Driver;
}
for (int i = 0; i < Trucks.Count * 2; i++)//add twice the leaders we need
{
FireFighter oFireFighter = Team[Rnd.Next(Team.Count)];
oFireFighter.Skills |= Skill.TeamLeader;
}
for (int i = 0; i < Trucks.Count * 2; i++)//add twice the multitaskers we need
{
FireFighter oFireFighter = Team[Rnd.Next(Team.Count)];
oFireFighter.Skills = (Skill.TeamLeader|Skill.Driver);
}
//Truck that are going to be deployed
int nTrucksToDeploy = Rnd.Next(2, Trucks.Count);
// distribute firefighter by ETA to minimize truck deploy
//*******************************************************
//Sort by ETA
Team.Sort();
//get top the ones that can only drive
List<FireFighter> oSelectedDrivers = Team.FindAll(delegate(FireFighter item) { return item.Skills == Skill.Driver; }).GetRange(0, nTrucksToDeploy);
oSelectedDrivers.ForEach(delegate(FireFighter item) { item.Assigned = Skill.Driver; });
//get top the ones that can only lead
List<FireFighter> oSelectedLeaders = Team.FindAll(delegate(FireFighter item) { return item.Skills == Skill.TeamLeader; }).GetRange(0, nTrucksToDeploy);
oSelectedLeaders.ForEach(delegate(FireFighter item) { item.Assigned = Skill.TeamLeader; });
//put them on a list of already assigned
List<FireFighter> oAssigned = new List<FireFighter>();
oAssigned.AddRange(oSelectedDrivers);
oAssigned.AddRange(oSelectedLeaders);
//get the ones that can do ether job
List<FireFighter> oMultitaskers = Team.FindAll(delegate(FireFighter item) { return item.Skills == (Skill.Driver | Skill.TeamLeader); });
//sort by ETA
oMultitaskers.Sort();
oAssigned.Sort();
//put a multitaskers in the queue if he is gonna arrive earlier than the most delayed
while (oMultitaskers[0].ETA < oAssigned[oAssigned.Count - 1].ETA)
{
FireFighter oIsOut = oAssigned[oAssigned.Count - 1];
FireFighter oIsIn = oMultitaskers[0];
//swap tasks
oIsIn.Assigned = oIsOut.Assigned;
oIsOut.Assigned = Skill.None;
//remove from respective queues
oAssigned.RemoveAt(oAssigned.Count - 1);
oMultitaskers.RemoveAt(0);
//Add the multitasker to queue
//and if you are questioning if the could get a better solution by choosing another assignment
//that as no influence.. the optmizing condition is removing the slowest that was on queue
oAssigned.Add(oIsIn);
//the out guy is not added for two reasons
//1st is not a multitasker
//2nd and most importante.. he was the one with the highest ETA, we will NEVER gain by replacing another driver with this one
//oMultitaskers.Add(oIsOut);
oMultitaskers.Sort();
oAssigned.Sort();
}
//start filling the trucks take one of each from top, wich means the first truck will have the earliest departure
for (int i = 0; i < nTrucksToDeploy; i++)
{
int nDriverIndex = oAssigned.FindIndex(delegate(FireFighter item) { return item.Assigned == Skill.Driver; });
Trucks[i].Crew.Add(oAssigned[nDriverIndex]);
oAssigned.RemoveAt(nDriverIndex);
int nLeaderIndex = oAssigned.FindIndex(delegate(FireFighter item) { return item.Assigned == Skill.TeamLeader; });
Trucks[i].Crew.Add(oAssigned[nLeaderIndex]);
oAssigned.RemoveAt(nLeaderIndex);
}
//now fill the rest of the crew.. also ordered by ETA
List<FireFighter> oUnassigned = Team.FindAll(delegate(FireFighter item) { return item.Assigned == Skill.None; });
oUnassigned.Sort();
for (int i = 0; i < nTrucksToDeploy; i++)
{
while (Trucks[i].Crew.Count < Trucks[i].Capacity)
{
Trucks[i].Crew.Add(oUnassigned[0]);
oUnassigned.RemoveAt(0);
}
}
//dump truck data
Trace.WriteLine(String.Format("{0} trucks to be deployed",nTrucksToDeploy));
for (int i = 0; i < nTrucksToDeploy; i++)
{
Trace.WriteLine(new String('-', 20));
Truck oTruck = Trucks[i];
oTruck.Crew.Sort();
Trace.WriteLine(String.Format("truck {0} estimated time of departure: {1}",oTruck.ID,oTruck.Crew[oTruck.Crew.Count-1].ETA));
for (int j = 0; j < oTruck.Crew.Count; j++)
{
FireFighter oFireFighter = oTruck.Crew[j];
Trace.WriteLine(String.Format("{0}({1})\t # {2} minutes as \t{3}", oFireFighter.ID, oFireFighter.Skills, oFireFighter.ETA, oFireFighter.Assigned));
}
}
Trace.WriteLine(new String('#', 20));
//list the team
for (int i = 0; i < Team.Count; i++)
{
FireFighter oFireFighter = Team[i];
Trace.WriteLine(String.Format("{0}({1})\t # {2} minutes as \t{3}", oFireFighter.ID, oFireFighter.Skills, oFireFighter.ETA, oFireFighter.Assigned));
}
}
}
}

Related

Paper cut algorithm

I want to create a function to determine the most number of pieces of paper on a parent paper size
The formula above is still not optimal. If using the above formula will only produce at most 32 cut/sheet.
I want it like below.
This seems to be a very difficult problem to solve optimally. See http://lagrange.ime.usp.br/~lobato/packing/ for a discussion of a 2008 paper claiming that the problem is believed (but not proven) to be NP-hard. The researchers found some approximation algorithms and implemented them on that website.
The following solution uses Top-Down Dynamic Programming to find optimal solutions to this problem. I am providing this solution in C#, which shouldn't be too hard to convert into the language of your choice (or whatever style of pseudocode you prefer). I have tested this solution on your specific example and it completes in less than a second (I'm not sure how much less than a second).
It should be noted that this solution assumes that only guillotine cuts are allowed. This is a common restriction for real-world 2D Stock-Cutting applications and it greatly simplifies the solution complexity. However, CS, Math and other programming problems often allow all types of cutting, so in that case this solution would not necessarily find the optimal solution (but it would still provide a better heuristic answer than your current formula).
First, we need a value-structure to represent the size of the starting stock, the desired rectangle(s) and of the pieces cut from the stock (this needs to be a value-type because it will be used as the key to our memoization cache and other collections, and we need to to compare the actual values rather than an object reference address):
public struct Vector2D
{
public int X;
public int Y;
public Vector2D(int x, int y)
{
X = x;
Y = y;
}
}
Here is the main method to be called. Note that all values need to be in integers, for the specific case above this just means multiplying everything by 100. These methods here require integers, but are otherwise are scale-invariant so multiplying by 100 or 1000 or whatever won't affect performance (just make sure that the values don't overflow an int).
public int SolveMaxCount1R(Vector2D Parent, Vector2D Item)
{
// make a list to hold both the item size and its rotation
List<Vector2D> itemSizes = new List<Vector2D>();
itemSizes.Add(Item);
if (Item.X != Item.Y)
{
itemSizes.Add(new Vector2D(Item.Y, Item.X));
}
int solution = SolveGeneralMaxCount(Parent, itemSizes.ToArray());
return solution;
}
Here is an example of how you would call this method with your parameter values. In this case I have assumed that all of the solution methods are part of a class called SolverClass:
SolverClass solver = new SolverClass();
int count = solver.SolveMaxCount1R(new Vector2D(2500, 3800), new Vector2D(425, 550));
//(all units are in tenths of a millimeter to make everything integers)
The main method calls a general solver method for this type of problem (that is not restricted to just one size rectangle and its rotation):
public int SolveGeneralMaxCount(Vector2D Parent, Vector2D[] ItemSizes)
{
// determine the maximum x and y scaling factors using GCDs (Greastest
// Common Divisor)
List<int> xValues = new List<int>();
List<int> yValues = new List<int>();
foreach (Vector2D size in ItemSizes)
{
xValues.Add(size.X);
yValues.Add(size.Y);
}
xValues.Add(Parent.X);
yValues.Add(Parent.Y);
int xScale = NaturalNumbers.GCD(xValues);
int yScale = NaturalNumbers.GCD(yValues);
// rescale our parameters
Vector2D parent = new Vector2D(Parent.X / xScale, Parent.Y / yScale);
var baseShapes = new Dictionary<Vector2D, Vector2D>();
foreach (var size in ItemSizes)
{
var reducedSize = new Vector2D(size.X / xScale, size.Y / yScale);
baseShapes.Add(reducedSize, reducedSize);
}
//determine the minimum values that an allowed item shape can fit into
_xMin = int.MaxValue;
_yMin = int.MaxValue;
foreach (var size in baseShapes.Keys)
{
if (size.X < _xMin) _xMin = size.X;
if (size.Y < _yMin) _yMin = size.Y;
}
// create the memoization cache for shapes
Dictionary<Vector2D, SizeCount> shapesCache = new Dictionary<Vector2D, SizeCount>();
// find the solution pattern with the most finished items
int best = solveGMC(shapesCache, baseShapes, parent);
return best;
}
private int _xMin;
private int _yMin;
The general solution method calls a recursive worker method that does most of the actual work.
private int solveGMC(
Dictionary<Vector2D, SizeCount> shapeCache,
Dictionary<Vector2D, Vector2D> baseShapes,
Vector2D sheet )
{
// have we already solved this size?
if (shapeCache.ContainsKey(sheet)) return shapeCache[sheet].ItemCount;
SizeCount item = new SizeCount(sheet, 0);
if ((sheet.X < _xMin) || (sheet.Y < _yMin))
{
// if it's too small in either dimension then this is a scrap piece
item.ItemCount = 0;
}
else // try every way of cutting this sheet (guillotine cuts only)
{
int child0;
int child1;
// try every size of horizontal guillotine cut
for (int c = sheet.X / 2; c > 0; c--)
{
child0 = solveGMC(shapeCache, baseShapes, new Vector2D(c, sheet.Y));
child1 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X - c, sheet.Y));
if (child0 + child1 > item.ItemCount)
{
item.ItemCount = child0 + child1;
}
}
// try every size of vertical guillotine cut
for (int c = sheet.Y / 2; c > 0; c--)
{
child0 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X, c));
child1 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X, sheet.Y - c));
if (child0 + child1 > item.ItemCount)
{
item.ItemCount = child0 + child1;
}
}
// if no children returned finished items, then the sheet is
// either scrap or a finished item itself
if (item.ItemCount == 0)
{
if (baseShapes.ContainsKey(item.Size))
{
item.ItemCount = 1;
}
else
{
item.ItemCount = 0;
}
}
}
// add the item to the cache before we return it
shapeCache.Add(item.Size, item);
return item.ItemCount;
}
Finally, the general solution method uses a GCD function to rescale the dimensions to achieve scale-invariance. This is implemented in a static class called NaturalNumbers. I have included the rlevant parts of this class below:
static class NaturalNumbers
{
/// <summary>
/// Returns the Greatest Common Divisor of two natural numbers.
/// Returns Zero if either number is Zero,
/// Returns One if either number is One and both numbers are >Zero
/// </summary>
public static int GCD(int a, int b)
{
if ((a == 0) || (b == 0)) return 0;
if (a >= b)
return gcd_(a, b);
else
return gcd_(b, a);
}
/// <summary>
/// Returns the Greatest Common Divisor of a list of natural numbers.
/// (Note: will run fastest if the list is in ascending order)
/// </summary>
public static int GCD(IEnumerable<int> numbers)
{
// parameter checks
if (numbers == null || numbers.Count() == 0) return 0;
int first = numbers.First();
if (first <= 1) return 0;
int g = (int)first;
if (g <= 1) return g;
int i = 0;
foreach (int n in numbers)
{
if (i == 0)
g = n;
else
g = GCD(n, g);
if (g <= 1) return g;
i++;
}
return g;
}
// Euclidian method with Euclidian Division,
// From: https://en.wikipedia.org/wiki/Euclidean_algorithm
private static int gcd_(int a, int b)
{
while (b != 0)
{
int t = b;
b = (a % b);
a = t;
}
return a;
}
}
Please let me know of any problems or questions you might have with this solution.
Oops, forgot that I was also using this class:
public class SizeCount
{
public Vector2D Size;
public int ItemCount;
public SizeCount(Vector2D itemSize, int itemCount)
{
Size = itemSize;
ItemCount = itemCount;
}
}
As I mentioned in the comments, it would actually be pretty easy to factor this class out of the code, but it's still in there right now.

Gin Rummy - Algorithm for determining optimal melding

A similar question to this has been asked here, but in my question, rather than being restricted to melds of size 3, melds can be any size.
In Gin Rummy, for any particular set of cards, cards can be grouped into either sets or runs. A set is a group of 3 or more cards that are all of the same rank (2-D, 2-C, 2-H or 7-D, 7-C, 7-H, 7-S). A run is a group of 3 or more cards with consecutive ranks and identical suits (A-D, 2-D, 3-C or 7-C, 8-C, 9-C, 10-C, J-C). Cards not belonging to a group are called "deadwood".
The goal of my algorithm is to find the optimal melding for a particular set of cards, which is one that minimizes the sum of the values of all the deadwood (The values of number cards are their associated numbers, the value of the ace is 1, and the value of face cards is 10.).
My original attempt at an algorithm worked on the assumption that for any run and group of sets that conflicted, either the run would exist or the group of sets would exist. Under this assumption, the algorithm could just calculate the sum of the values of the run and the sum of the values of all the sets, and keep whichever was greater. For example, if we had the groups
[2-D, 3-D, 4-D], [2-D, 2-C, 2-H], and [4-D, 4-H, 4-S]. The sum of the run's value would be 2 + 3 + 4 = 9, and the sum of the all the set's values would be 2 + 2 + 2 + 4 + 4 + 4 = 18. In this case, this would mean the two sets would be kept for the optimal melding and the run would not be used (3-D would be deadwood).
This assumption worked for groups of size 3, but fails with larger groups. For example, consider the following two groups:
[4-D, 5-D, 6-D, 7-D], [7-D, 7-H, 7-S]
The optimal grouping for this ends up being [4-D, 5-D, 6-D] and [7-D, 7-H, 7-S]. The conflicting set and part of the run is kept. I'm not sure how to create an algorithm, that isn't just brute force.
Any help or ideas would be appreciated.
EDIT
I'm realizing that my original algorithm doesn't even work for size 3 melds. In the case of the following groups:
[4-D, 5-D, 6-D], [4-C, 5-C, 6-C], [6-D, 6-C, 6-S]
The algorithm would look at the two runs individually, and conclude that they should be removed in favor of the set, but the optimal solution would be to keep both runs and remove the set.
Still looking for help in creating an algorithm that works in all edge cases.
My previous answer got deleted as I didn't really provide an explanation, and simply provided a link to a script with an algorithm for this problem. I realize why that isn't fit for an answer on stack overflow now. Here's my attempt at a complete answer.
The algorithm I show here is based on the approach found here: https://gist.github.com/yalue/2622575. The solution is a backtracking search as Paul Hankin suggested above.
The algorithm creates an object called MeldNode:
class MeldNode {
Cards cards;
MeldNode* parent;
double value;
MeldNode(Cards cards, MeldNode* parent) : cards(cards), parent(parent) {
value = sum of values of cards;
if (parent is not null){
value += parent->value;
}
}
}
Here value is equal to the sum of the card values of the cards provided and the value of the parent.
A function cleanMeldGroup is created which, given an array of melds and a meld, will return an array of melds with only melds that don't conflict with the given meld.
Melds cleanMeldGroup(Melds melds, Cards meldAvoid) {
Melds cleanMelds;
for (Cards meld : melds) {
bool clean = true;
for (Card cardA : meld) {
for (Card cardB : meldAvoid) {
if (cardA == cardB) {
clean = false;
}
}
}
if (clean) {
cleanMelds.push(meld);
}
}
return cleanMelds;
}
Next, a function getBestNode is created, which uses backtracking to find a meld node containing the data for the optimal melding combination.
MeldNode* getBestNode(Melds melds, MeldNode* rootNode) {
MeldNode* best = rootNode;
for (Meld meld : melds) {
MeldNode* node = new MeldNode(meld, rootNode);
MeldNode* newTree = getBestNode(cleanMeldGroup(melds, meld), node);
if (best is null || newTree->value > best->value){
best = newTree;
}
}
}
Note that as this is written now, this would result in memory leaks in c++. If necessary, take measures to free memory when the data has been used (You could free the memory of Nodes that aren't part of the best tree in this function, and then free the memory of Nodes that are part of the best tree after you use them).
Finally, the optimal melding can be determined as follows using the getOptimalMelding function.
Melds getOptimalMelding(Cards hand) {
Sort hand by rank and then by suit;
Melds possibleMelds;
// Find all possible runs
int runLength = 1;
for (int i = 0; i < hand.size(); i++) {
if (hand[i].suit == hand[i - 1].suit && hand[i].rank == hand[i - 1].rank + 1) {
runLength++;
} else {
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
runLength = 1;
}
}
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
// Find all possible sets
for (int i = 1; i <= 13; i++) {
Cards set;
for (Card card : hand) {
if (card.rank == i) {
set.push(card);
}
}
if (set.size() >= 3) {
possibleMelds.push(set);
}
if (set.size() == 4) {
for (Card card : set) {
Cards subset;
for (Card add : set) {
if (add != card) {
subset.push(add);
}
}
possibleMelds.push(subset);
}
}
}
// Find Optimal Melding Combination
MeldNode* bestNode = getBestNode(possibleMelds, null);
Melds optimalMelds;
while (bestNode is not null){
optimalMelds.push(bestNode.cards);
bestNode = bestNode->parent;
}
return optimalMelds;
Note that possibleMelds contains all possible melds of all sizes. For example, for the hand [2-D, 3-D, 4-D, 5-D, 5-H, 5-S, 5-C, 10-S, 9-C, 8-H], possibleMelds would contain the following groups:
[2-D, 3-D, 4-D],
[3-D, 4-D, 5-D],
[2-D, 3-D, 4-D, 5-D],
[5-D, 5-H, 5-S, 5-C],
[5-H, 5-S, 5-C],
[5-D, 5-S, 5-C],
[5-D, 5-H, 5-C],
[5-D, 5-H, 5-S]

Verification of algorithm for variant of gas station

I am studying this problem and I recognise this as a variant of the gas station problem. As a result, I use Greedy algorithm to solve this problem. I would like to ask if anyone helps me to point out my algorithm is correct or not, thanks.
My algorithm
var x = input.distance, cost = input.cost, c = input.travelDistance, price = [Number.POSITIVE_INFINITY];
var result = [];
var lastFill = 0, tempMinIndex = 0, totalCost = 0;
for(var i=1; i<x.length; i++) {
var d = x[i] - x[lastFill];
if(d > c){ //car can not travel to this shop, has to decide which shop to refill in the previous possible shops
result.push(tempMinIndex);
lastFill = tempMinIndex;
totalCost += price[tempMinIndex];
tempMinIndex = i;
}
//calculate price
price[i] = d/c * cost[i];
if(price[i] <= price[tempMinIndex])
tempMinIndex = i;
}
//add last station to the list and the total cost
if(lastFill != x.length - 1){
result.push(x.length - 1);
totalCost += price[price.length-1];
}
You can try out the algorithm at this link
https://drive.google.com/file/d/0B4sd8MQwTpVnMXdCRU0xZFlVRlk/view?usp=sharing
First, regarding to your solution.
There is a bug that ruins even at the most simple inputs. When you decided that the distance became too far and you should fulfil at some point before, you don't update distance and gas station charge you more that it should. The fix is simple:
if(d > c){
//car can not travel to this shop, has to decide which shop to refill
//in the previous possible shops
result.push(tempMinIndex);
lastFill = tempMinIndex;
totalCost += price[tempMinIndex];
tempMinIndex = i;
// Fix: update distance
var d = x[i] - x[lastFill];
}
Even with this fix, your algorithm fails on some input data, like this:
0 10 20 30
0 20 30 50
30
It should refill on every gasoline to minimize cost, but it simply fills on the last one.
After some research, I came up with solution. I'll try to explain it as simple as possible to make it language independent.
Idea
For every gas station G we will count cheapest way of filling. We'll do that recursively: for each gas station let's find all gas stations i from which we can reach G. For every i count cheapest filling possible and sum up with the cost of the filling at G given gasoline left. For start gas station cost is 0. More formally:
CostOfFilling(x), Capacity and Position(x) can be retrieved from input data.
So, the answer for the problem is simply BestCost(LastGasStation)
Code
Now, solution in javascript to make things clearer.
function calculate(input)
{
// Array for keeping calculated values of cheapest filling at each station
best = [];
var x = input.distance;
var cost = input.cost;
var capacity = input.travelDistance;
// Array initialization
best.push(0);
for (var i = 0; i < x.length - 1; i++)
{
best.push(-1);
}
var answer = findBest(x, cost, capacity, x.length - 1);
return answer;
}
// Implementation of BestCost function
var findBest = function(distances, costs, capacity, distanceIndex)
{
// Return value if it's already have been calculated
if (best[distanceIndex] != -1)
{
return best[distanceIndex];
}
// Find cheapest way to fill by iterating on every available gas station
var minDistanceIndex = findMinDistance(capacity, distances, distanceIndex);
var answer = findBest(distances, costs, capacity, minDistanceIndex) +
calculateCost(distances, costs, capacity, minDistanceIndex, distanceIndex);
for (var i = minDistanceIndex + 1; i < distanceIndex; i++)
{
var newAnswer = findBest(distances, costs, capacity, i) +
calculateCost(distances, costs, capacity, i, distanceIndex);
if (newAnswer < answer)
{
answer = newAnswer;
}
}
// Save best result
best[distanceIndex] = answer;
return answer;
}
// Implementation of MinGasStation function
function findMinDistance(capacity, distances, distanceIndex)
{
for (var i = 0; i < distances.length; i++)
{
if (distances[distanceIndex] - distances[i] <= capacity)
{
return i;
}
}
}
// Implementation of Cost function
function calculateCost(distances, costs, capacity, a, b)
{
var distance = distances[b] - distances[a];
return costs[b] * (distance / capacity);
}
Full workable html page with code is available here

Find the maximum height they can make by standing on each other?

Weights of n men and their strengths (max weight they can carry) are given. Height of all are same and given. Find the maximum height they can make by standing on each other?
That means, you have to place them by taking maximum number of men from them, such that no men is carrying weight more than his strength.
This question is bugging me. First I thought using greedy, by taking person of maximum strength first, but it is not giving correct answer. Then I tried to solve it, like knapsack, which is also not right. I am not able to come up with an efficient algorithm. Can anyone help?
First of all sorry by my english :)
Here is one way that you can think as a way to solve the problem.
Ok if you can supposed that each floor absorbs the whole weight in a uniform form, ( I mean there are no restriction like "one man can carry only the weight of two mens" or somethin like that..).
We will start with an hypothetical structure which has one man for each floor, and with that structure we will start to check the restrictions and arrange people.
We will check the lowest floor (first floor), and we will ask: Can this floor handle the weight of all the higher floors?
If the answer is no, we remove one men from the top of the tower and we add it to this floor, and we check again the weight condition on this floor.
If the answer is yes, we pass to check the next floor.
After that we will have an structure which meet the requirements.
And the C# code:
int amountOfMens = n;
float weight = w;
float strength = s;
float height = h;
int []mensInEachFloor;
public void MyAlg()
{
mensInEachFloor = new int[ amountOfMens ]; // the max height that we can achieve is the max amount of mens.
for(int i=0; i < mensInEachFloor.Length; i++ )
{
// we put one men on each floor, just to check if the highest heigth is achivable
mensInEachFloor[i] = 1;
}
// now we start to use our algorithm
// for each floor:
for(int i = 0; i < mensInEachFloor.Length; i++ )
{
// for each floor we will work on it until supports its designed weight
bool floorOk = false;
while(! floorOk)
{
// we check if the weigth of all the higher floors can be supported by this level
float weightToBeSupported = TotalWeightOfHigherFloors(i+1);
float weightThatCanBeSupported = WeightHandledByFloor(i);
if( weightToBeSupported > weightThatCanBeSupported )
{
// Remove one men from the top
RemoveOneManFromHighestFloor();
// add one men to this floor to help with the weight
AddOneManToFloor(i);
}
else
{
// we are ok on this floor :)
floorOk = true;
}
}
}
Debug.Log("The total heigth of the tower is : " + GetTowerHeight() );
}
private float TotalWeightOfHigherFloors(int startingFloor)
{
float totalWeight = 0;
for(int i= startingFloor; i< mensInEachFloor.Length; i++ )
{
totalWeight += mensInEachFloor[i] * weight;
}
return totalWeight;
}
private float WeightHandledByFloor(int floor)
{
return mensInEachFloor[floor] * strength;
}
private void RemoveOneManFromHighestFloor()
{
// we start to see from the top..
for(int i = mensInEachFloor.Length - 1 ; i >= 0; i-- )
{
// if on this floor are one or more mens..
if(mensInEachFloor[i] != 0)
{
// we remove from the floor
mensInEachFloor[i] = mensInEachFloor[i] - 1;
// and we are done
break;
}
}
}
private void AddOneManToFloor(int floor)
{
// Add one man to the selected floor
mensInEachFloor[floor] = mensInEachFloor[floor] + 1;
}
private float GetTowerHeight()
{
// We will count the number of floors with mens on it
float amountOfFloors = 0;
for(int i= 0; i< mensInEachFloor.Length; i++ )
{
// If there are more than zero mens
if( mensInEachFloor[i] > 0 )
{
// it means that it is a valid floor
amountOfFloors++;
}
}
// number of floors times height
return amountOfFloors * height;
}
Cheers !

implementing shuffle functioning in a music player

This was asked in an interview
"What is the most efficient way to implement a shuffle function in a music
player to play random songs without repetition"
I suggested link-list approach i.e. use a link-list, generate a random number and remove that item/song from the list ( this way , we ensure that no song is repeated )
then I suggested bit vector approach but he wasn't satisfied at all.
so what according to you is the best approach to implement such a function?
Below are some implementations. I also had difficulties during the interview but after the interview I saw that the solution is simple.
public class MusicTrackProgram {
// O(n) in-place swapping
public static List<MusicTrack> shuffle3(List<MusicTrack> input) {
Random random = new Random();
int last = input.size() - 1;
while (last >= 0) {
int randomInt = Math.abs(random.nextInt() % input.size());
// O(1)
MusicTrack randomTrack = input.get(randomInt);
MusicTrack temp = input.get(last);
// O(1)
input.set(last, randomTrack);
input.set(randomInt, temp);
--last;
}
return input;
}
// O(n) but extra field
public static List<MusicTrack> shuffle(List<MusicTrack> input) {
List<MusicTrack> result = new ArrayList<>();
Random random = new Random();
while (result.size() != input.size()) {
int randomInt = Math.abs(random.nextInt() % input.size());
// O(1)
MusicTrack randomTrack = input.get(randomInt);
if (randomTrack.isUsed) {
continue;
}
// O(1)
result.add(randomTrack);
randomTrack.isUsed = true;
}
return result;
}
// very inefficient O(n^2)
public static List<MusicTrack> shuffle2(List<MusicTrack> input) {
List<MusicTrack> result = new ArrayList<>();
Random random = new Random();
while (result.size() != input.size()) {
int randomInt = Math.abs(random.nextInt() % input.size());
// O(1)
MusicTrack randomTrack = input.get(randomInt);
// O(1)
result.add(randomTrack);
// O(n)
input.remove(randomTrack);
}
return result;
}
public static void main(String[] args) {
List<MusicTrack> musicTracks = MusicTrackFactory.generate(1000000);
List<MusicTrack> result = shuffle3(musicTracks);
result.stream().forEach(x -> System.out.println(x.getName()));
}
}
There is no perfect answer, I guess this sort of questions is aimed to start a discussion. Most likely your interviewer was wanting to hear about Fisher–Yates shuffle (aka Knuth shuffle).
Here is brief outline from wiki:
Write down the numbers from 1 through N.
Pick a random number k between one and the number of unstruck numbers remaining (inclusive).
Counting from the low end, strike out the kth number not yet struck out, and write it down elsewhere.
Repeat from step 2 until all the numbers have been struck out.
The sequence of numbers written down in step 3 is now a random permutation of the original numbers.
You should mention its inefficiencies and benefits, how you could improve this, throw in a few lines of code and discuss what and how you would test this code.
We can use link list and a queue for implementing a song search in mp3 player
We can extend this to following functionalities:
Add a new song
Delete a song
Randomly play a song
Add a song in play queue
Suppose initially we have 6 songs stored as link list
Link list has 2 pointers : start and end
totalSongCount=6
Randomly play a song:
We will generate a random number between 1 to totalSongCount. Let this be 4
We will remove the node representing song 4 and keep it after end pointer
we will decerement the totalSongCount (totalSongCount--).
Next time random number will be generated between 1 to 5 as we have decremented the totalSongCount , we can repeat the process
To add a new song, just add it to link list and make it as head pointer(add in beginning)
increment totalSongCount (totalSongCount++)
To delete a song , first find it and delete it
Also keep a track whether it is after end pointer , if it is not just decerement the totalSongCount (totalSongCount--)
The selected song can have two option:
Either play at that moment or
Add to a playlist (Seperate queue)
I think below solution should work
class InvalidInput extends Exception{
public InvalidInput(String str){
super(str);
}
}
class SongShuffler{
String songName[];
int cooldownPeriod;
Queue<String> queue;
int lastIndex ;
Random random;
public SongShuffler(String arr[], int k) throws InvalidInput{
if(arr.length < k)
throw new InvalidInput("Arr length should be greater than k");
songName = arr;
cooldownPeriod = k;
queue = new LinkedList<String>();
lastIndex = arr.length-1;
random = new Random();
}
public String getSong(){
if(queue.size() == cooldownPeriod){
String s = queue.poll();
songName[lastIndex+1] = s;
lastIndex++;
}
int ind = random.nextInt(lastIndex);
String ans = songName[ind];
queue.add(ans);
songName[ind] = songName[lastIndex];
lastIndex--;
return ans;
}
}

Resources