I am working on an algorithm that optimizes utilitization of a vehicle. What I have is a list of paths the vehicle is planned to take and between which times (time windows) it has to be at a certain location.
I can also calculate the time it takes to get from one location to the next, but I have to take into account that the speed is not fixed - the driver can drive faster or slower between the points.
Let's say Vehicle1 has to take customer from A between 10am and 12am to B before 3pm. If I add another customer that has to go from C between 11am and 12am to D before 1pm. The time windows could change.
The durations at normal speed are A <- 10 min -> C <- 1h -> D <- 1.5h -> B
. The driver can go .1x slower or faster (so from A-C he takes 10 * 1.1 = 11min if driving slowly and 10 * 0.9 = 9min if driving fast).
To calculate this iteratively I first make a pass forward to increase the driving window as much as possible and then reduce the window by making an intersection with the passenger windows. Then I make a pass going backwards, increasing the driving window again and making the intersection with the passenger windows and the previous window.
Here is the pseudo code:
for i=1 to locations.size:
previous = locations[i-1]
current = locations[i]
duration = calculateDuration(previous, current)
drivingWindow = previous.window.postpone(fast(duration), slow(duration))
actualWindow = intersectWithPassengers(drivingWindow, passengers)
current.window = actualWindow
for i=locations.size-2 to including 0:
next = locations[i+1]
current = locations[i]
duration = calculateDuration(current, next)
drivingWindow = next.window.prepone(slow(duration), fast(duration))
intersectedWithPassengers = intersectWithPassengers(drivingWindow, passengers)
current.window = current.window.intersection(intersectedWithPassengers)
Meaning of different functions and methods:
calculateDuration is probably clear
window.postpone(x, y) returns new window [a+x, b+y]
window.prepone(x, y) returns new window [a-x, b-y]
intersectWithPassengers is probably clear
If any of the windows don't intersect that means that the new passenger cannot be picked up. This algorithm keeps the windows as big as they can be, so more passengers can be picked up by the vehicle.
Algorithm goes as follows:
- A[10, 12], C[11, 12], D[, 13], B[, 15] // what we start with
- A[10, 12], C[11, 12], D[, 13], B[, 15] // driving: [10:09, 12:11], passengers: [11, 12]
- A[10, 12], C[11, 12], D[11:54, 13], B[, 15] // driving: [11:54, 13:06], passengers: [, 13]
- A[10, 12], C[11, 12], D[11:54, 13], B[13:15, 14:39] // driving: [13:15, 14:39], passengers: [, 15]
- A[10, 12], C[11, 12], D[11:54, 13], B[13:15, 14:39] // driving: [11:36, 13:18], passengers: [, 13], current: [11:54, 13]
- A[10, 12], C[11, 12], D[11:54, 13], B[13:15, 14:39] // driving: [10:48, 12:06], passengers: [11, 12], current: [11, 12]
- A[10:49, 11:51], C[11, 12], D[11:54, 13], B[13:15, 14:39] // driving: [10:49, 11:51], passengers: [10, 12], current: [10, 12]
While this algorithm works, and it is optimal, the code seems ugly to me and not really clear what it does and why, but I cannot seem to find a cleaner way to do it. How would you approach this if you were doing FP or something close to FP?