Find fastest path - algorithm

I have a list of trains with arrival timings at various stations. I need to find the fastest path between two railway stations if possible. In order to do it, I am even allowed to change the train once throughout the journey. However, If I change the train I must also add up the waiting time while boarding another train. Below is the pseudocode which I have written, but I am facing issues in completing it. Especially while changing the train and keeping the count of waiting hours. Can anyone help me with this and correct my pseudo-code.
function findFastestPath(source_station,dest_staion,waiting_time_at_current_station,travel_time_in_previous_train):
for each train_schedule in TrainList:
while train_schedule is not over:
if source_station equals train_schedule[station]
Then
if dest_staion in train_schedule
Then
if dest_staion[departure_time] > source_station[departure_time]
Then
duration = dest_staion[departure_time] - source_station[departure_time] + waiting_time_at_current_station + travel_time_in_previous_train
if mintime > duration
Then
mintime = duration
else:
travel_time_in_previous_train = next_station_to_source_station[departure_time]-source_station[departure_time]
waiting_time_at_current_station = next_train_departure_time - next_station_to_source_station[departure_time]
call findFastestPath(next_station_to_source_station, dest_staion, waiting_time_at_current_station, travel_time_in_previous_train)
return mintime
end function
for each source_station in Travel_Time_Matrix:
for each dest_staion in Travel_Time_Matrix:
call findFastestPath(source_station,dest_staion,0,0)

FindFastestPathInSameTrain(src,dest):
For x in range(Len(TrainList)): //TrainList is a two dimentional array which has each train schedule in each row
IF src and dest in TrainList[x]:
IF dest(time) > src(time): // Here we are checking if destination station is not before source station in train
duration = src->departuretTime - dest->departuretTime // calculating duration
IF min > duration: // min value will be updated if as we find fastest fast in other trains
min = duration
else:
NewTrainList.add(TrainList[x]) // if line 2 is not satisfied we will add that train in NewTrainList which is 2d array
return min // by default min value will be 1000, and it will be returned in case of no direct train
FindFastestPathInSecondTrain(src,dest): // now we will work on trains which dont has both source and destination station
For x in range(Len(NewTrainList)): // we will iterate NewTrainList
IF src in NewTrainList[x]: // if we find our Source station in NewTrainList
For n in range(NewTrainList[x].index(src)+1,len(NewTrainList[x])): // we will iterate that trains remaining station as src in a new extended function
FindFastestPathInSecondTrainExtension(NewTrainList[n],dest,src->departuretTime)
// this comment is for line 16 : Just as our FindFastestPathInSameTrain(), but here we will send origin departuretTime so as to capture time spent in prev train + waiting time at connecting station.
FindFastestPathInSecondTrainExtension(newsrc,dest,src->departuretTime): // This function will work similar to FindFastestPathInSameTrain()
For x in range(Len(NewTrainList)):
IF newsrc and dest in NewTrainList[x]:
IF dest->departuretTime > newsrc->departuretTime
secondDuration = (dest->departuretTime - newsrc->departuretTime) + (newsrc->departuretTime - src->departuretTime)
IF min > secondDuration
min = secondDuration
return min
For each source_station in Travel_Time_Matrix: // Iterating all the source_station
For each dest_staion in Travel_Time_Matrix: // Iterating all the dest_station
T1 = FindFastestPathInSameTrain(source_station,dest_station)
T2 = FindFastestPathInSecondTrain(source_station,dest_station)
IF T1 < T2
write T1
ELSE:
write T2

Related

Google Directions API sum of steps duration inconsistent with total leg duration

I'm using the Directions API with 'transit' mode and as a sanity check I compare the total distance and duration of one (there is only one) leg and the sum of its steps. I get that the distances add up, but the duration does not, given that I have some transit mode of transport (i.e. not just walking). Below illustrates the check:
from = (59.294420, 18.102213)
to = (59.297550, 18.087510)
departure_time = 1629957600
response = make_request(from, to, departure_time, 'transit', api_key, ...)
leg = response['routes'][0]['legs'][0]
total_distance_meters = leg['distance']['value']
total_travel_time_seconds = leg['duration']['value']
acc_distance = 0
acc_duration = 0
for i_step, step in enumerate(leg['steps']):
print(f"step {i_step+1}, distance={step['distance']['text']}, travel time={step['duration']['text']}, mode={step['travel_mode']}")
acc_distance += step['distance']['value']
acc_duration += step['duration']['value']
print(f"Sanity check: Total distance={total_distance_meters}, accumulated distance={acc_distance}")
print(f"Sanity check: Total duration={total_travel_time_seconds}, accumulated duration={acc_duration}")
# --> Output:
# step 1, distance=68 m, travel time=1 min, mode=WALKING
# step 2, distance=0.8 km, travel time=2 mins, mode=TRANSIT
# step 3, distance=0.4 km, travel time=5 mins, mode=WALKING
# Sanity check: Total distance=1268, accumulated distance=1268
# Sanity check: Total duration=478, accumulated duration=477
If I give an origin-destination pair with a single step of walking only, the durations are equal. With an origin-destination pair of longer distance and more steps, I get larger discrepancy between the total and accumulated numbers.
Is there some 'hidden' duration somewhere, or am I missing something?

Minimum number of train station stops

I received this interview question and got stuck on it:
There are an infinite number of train stops starting from station number 0.
There are an infinite number of trains. The nth train stops at all of the k * 2^(n - 1) stops where k is between 0 and infinity.
When n = 1, the first train stops at stops 0, 1, 2, 3, 4, 5, 6, etc.
When n = 2, the second train stops at stops 0, 2, 4, 6, 8, etc.
When n = 3, the third train stops at stops 0, 4, 8, 12, etc.
Given a start station number and end station number, return the minimum number of stops between them. You can use any of the trains to get from one stop to another stop.
For example, the minimum number of stops between start = 1 and end = 4 is 3 because we can get from 1 to 2 to 4.
I'm thinking about a dynamic programming solution that would store in dp[start][end] the minimum number of steps between start and end. We'd build up the array using start...mid1, mid1...mid2, mid2...mid3, ..., midn...end. But I wasn't able to get it to work. How do you solve this?
Clarifications:
Trains can only move forward from a lower number stop to a higher number stop.
A train can start at any station where it makes a stop at.
Trains can be boarded in any order. The n = 1 train can be boarded before or after boarding the n = 3 train.
Trains can be boarded multiple times. For example, it is permitted to board the n = 1 train, next board the n = 2 train, and finally board the n = 1 train again.
I don't think you need dynamic programming at all for this problem. It can basically be expressed by binary calculations.
If you convert the number of a station to binary it tells you right away how to get there from station 0, e.g.,
station 6 = 110
tells you that you need to take the n=3 train and the n=2 train each for one station. So the popcount of the binary representation tells you how many steps you need.
The next step is to figure out how to get from one station to another.
I´ll show this again by example. Say you want to get from station 7 to station 23.
station 7 = 00111
station 23 = 10111
The first thing you want to do is to get to an intermediate stop. This stop is specified by
(highest bits that are equal in start and end station) + (first different bit) + (filled up with zeros)
In our example the intermediate stop is 16 (10000). The steps you need to make can be calculated by the difference of that number and the start station (7 = 00111). In our example this yields
10000 - 00111 = 1001
Now you know, that you need 2 stops (n=1 train and n=4) to get from 7 to 16.
The remaining task is to get from 16 to 23, again this can be solved by the corresponding difference
10111 - 10000 = 00111
So, you need another 3 stops to go from 16 to 23 (n= 3, n= 2, n= 1). This gives you 5 stops in total, just using two binary differences and the popcount. The resulting path can be extracted from the bit representations 7 -> 8 -> 16 -> 20 -> 22 -> 23
Edit:
For further clarification of the intermediate stop let's assume we want to go from
station 5 = 101 to
station 7 = 111
the intermediate stop in this case will be 110, because
highest bits that are equal in start and end station = 1
first different bit = 1
filled up with zeros = 0
we need one step to go there (110 - 101 = 001) and one more to go from there to the end station (111 - 110 = 001).
About the intermediate stop
The concept of the intermediate stop is a bit clunky but I could not find a more elegant way in order to get the bit operations to work. The intermediate stop is the stop in between start and end where the highest level bit switches (that's why it is constructed the way it is). In this respect it is the stop at which the fastest train (between start and end) operates (actually all trains that you are able to catch stop there).
By subtracting the intermediate stop (bit representation) from the end station (bit representation) you reduce the problem to the simple case starting from station 0 (cf. first example of my answer).
By subtracting the start station from the intermediate stop you also reduce the problem to the simple case, but assume that you go from the intermediate stop to the start station which is equivalent to the other way round.
First, ask if you can go backward. It sounds like you can't, but as presented here (which may not reflect the question as you received it), the problem never gives an explicit direction for any of these trains. (I see you've now edited your question to say you can't go backward.)
Assuming you can't go backward, the strategy is simple: always take the highest-numbered available train that doesn't overshoot your destination.
Suppose you're at stop s, and the highest-numbered train that stops at your current location and doesn't overshoot is train k. Traveling once on train k will take you to stop s + 2^(k-1). There is no faster way to get to that stop, and no way to skip that stop - no lower-numbered trains skip any of train k's stops, and no higher-numbered trains stop between train k's stops, so you can't get on a higher-numbered train before you get there. Thus, train k is your best immediate move.
With this strategy in mind, most of the remaining optimization is a matter of efficient bit twiddling tricks to compute the number of stops without explicitly figuring out every stop on the route.
I will attempt to prove my algorithm is optimal.
The algorithm is "take the fastest train that doesn't overshoot your destination".
How many stops this is is a bit tricky.
Encode both stops as binary numbers. I claim that an identical prefix can be neglected; the problem of going from a to b is the same as the problem of going from a+2^n to b+2^n if 2^n > b, as the stops between 2^n and 2^(n+1) are just the stops between 0 and 2^n shifted over.
From this, we can reduce a trip from a to b to guarantee that the high bit of b is set, and the same "high" bit of a is not set.
To solve going from 5 (101) to 7 (111), we merely have to solve going from 1 (01) to 3 (11), then shift our stop numbers up 4 (100).
To go from x to 2^n + y, where y < 2^n (and hence x is), we first want to go to 2^n, because there are no trains that skip over 2^n that do not also skip over 2^n+y < 2^{n+1}.
So any set of stops between x and y must stop at 2^n.
Thus the optimal number of stops from x to 2^n + y is the number of stops from x to 2^n, followed by the number of stops from 2^n to 2^n+y, inclusive (or from 0 to y, which is the same).
The algorithm I propose to get from 0 to y is to start with the high order bit set, and take the train that gets you there, then go on down the list.
Claim: In order to generate a number with k 1s, you must take at least k trains. As proof, if you take a train and it doesn't cause a carry in your stop number, it sets 1 bit. If you take a train and it does cause a carry, the resulting number has at most 1 more set bit than it started with.
To get from x to 2^n is a bit trickier, but can be made simple by tracking the trains you take backwards.
Mapping s_i to s_{2^n-i} and reversing the train steps, any solution for getting from x to 2^n describes a solution for getting from 0 to 2^n-x. And any solution that is optimal for the forward one is optimal for the backward one, and vice versa.
Using the result for getting from 0 to y, we then get that the optimal route from a to b where b highest bit set is 2^n and a does not have that bit set is #b-2^n + #2^n-a, where # means "the number of bits set in the binary representation". And in general, if a and b have a common prefix, simply drop that common prefix.
A local rule that generates the above number of steps is "take the fastest train in your current location that doesn't overshoot your destination".
For the part going from 2^n to 2^n+y we did that explicitly in our proof above. For the part going from x to 2^n this is trickier to see.
First, if the low order bit of x is set, obviously we have to take the first and only train we can take.
Second, imagine x has some collection of unset low-order bits, say m of them. If we played the train game going from x/2^m to 2^(n-m), then scaled the stop numbers by multiplying by 2^m we'd get a solution to going from x to 2^n.
And #(2^n-x)/2^m = #2^n - x. So this "scaled" solution is optimal.
From this, we are always taking the train corresponding to our low-order set bit in this optimal solution. This is the longest range train available, and it doesn't overshoot 2^n.
QED
This problem doesn't require dynamic programming.
Here is a simple implementation of a solution using GCC:
uint32_t min_stops(uint32_t start, uint32_t end)
{
uint32_t stops = 0;
if(start != 0) {
while(start <= end - (1U << __builtin_ctz(start))) {
start += 1U << __builtin_ctz(start);
++stops;
}
}
stops += __builtin_popcount(end ^ start);
return stops;
}
The train schema is a map of powers-of-two. If you visualize the train lines as a bit representation, you can see that the lowest bit set represents the train line with the longest distance between stops that you can take. You can also take the lines with shorter distances.
To minimize the distance, you want to take the line with the longest distance possible, until that would make the end station unreachable. That's what adding by the lowest-set bit in the code does. Once you do this, some number of the upper bits will agree with the upper bits of the end station, while the lower bits will be zero.
At that point, it's simply a a matter of taking a train for the highest bit in the end station that is not set in the current station. This is optimized as __builtin_popcount in the code.
An example going from 5 to 39:
000101 5 // Start
000110 5+1=6
001000 6+2=8
010000 8+8=16
100000 16+16=32 // 32+32 > 39, so start reversing the process
100100 32+4=36 // Optimized with __builtin_popcount in code
100110 36+2=38 // Optimized with __builtin_popcount in code
100111 38+1=39 // Optimized with __builtin_popcount in code
As some have pointed out, since stops are all multiples of powers of 2, trains that stop more frequently also stop at the same stops of the more-express trains. Any stop is on the first train's route, which stops at every station. Any stop is at most 1 unit away from the second train's route, stopping every second station. Any stop is at most 3 units from the third train that stops every fourth station, and so on.
So start at the end and trace your route back in time - hop on the nearest multiple-of-power-of-2 train and keep switching to the highest multiple-of-power-of-2 train you can as soon as possible (check the position of the least significant set bit - why? multiples of powers of 2 can be divided by two, that is bit-shifted right, without leaving a remainder, log 2 times, or as many leading zeros in the bit-representation), as long as its interval wouldn't miss the starting point after one stop. When the latter is the case, perform the reverse switch, hopping on the next lower multiple-of-power-of-2 train and stay on it until its interval wouldn't miss the starting point after one stop, and so on.
We can figure this out doing nothing but a little counting and array manipulation. Like all the previous answers, we need to start by converting both numbers to binary and padding them to the same length. So 12 and 38 become 01100 and 10110.
Looking at station 12, looking at the least significant set bit (in this case the only bit, 2^2) all trains with intervals larger than 2^2 won't stop at station 4, and all with intervals less than or equal to 2^2 will stop at station 4, but will require multiple stops to get to the same destination as the interval 4 train. We in every situation, up until we reach the largest set bit in the end value, we need to take the train with the interval of the least significant bit of the current station.
If we are at station 0010110100, our sequence will be:
0010110100 2^2
0010111000 2^3
0011000000 2^6
0100000000 2^7
1000000000
Here we can eliminate all bits smaller than the lest significant set bit and get the same count.
00101101 2^0
00101110 2^1
00110000 2^4
01000000 2^6
10000000
Trimming the ends at each stage, we get this:
00101101 2^0
0010111 2^0
0011 2^0
01 2^0
1
This could equally be described as the process of flipping all the 0 bits. Which brings us to the first half of the algorithm: Count the unset bits in the zero padded start number greater than the least significant set bit, or 1 if the start station is 0.
This will get us to the only intermediate station reachable by the train with the largest interval smaller than the end station, so all trains after this must be smaller than the previous train.
Now we need to get from station to 100101, it is easier and obvious, take the train with an interval equal to the largest significant bit set in the destination and not set in the current station number.
1000000000 2^7
1010000000 2^5
1010100000 2^4
1010110000 2^2
1010110100
Similar to the first method, we can trim the most significant bit which will always be set, then count the remaining 1's in the answer. So the second part of the algorithm is Count all the set significant bits smaller than the most significant bit
Then Add the result from parts 1 and 2
Adjusting the algorithm slightly to get all the train intervals, here is an example written in javascript so it can be run here.
function calculateStops(start, end) {
var result = {
start: start,
end: end,
count: 0,
trains: [],
reverse: false
};
// If equal there are 0 stops
if (start === end) return result;
// If start is greater than end, reverse the values and
// add note to reverse the results
if (start > end) {
start = result.end;
end = result.start;
result.reverse = true;
}
// Convert start and end values to array of binary bits
// with the exponent matched to the index of the array
start = (start >>> 0).toString(2).split('').reverse();
end = (end >>> 0).toString(2).split('').reverse();
// We can trim off any matching significant digits
// The stop pattern for 10 to 13 is the same as
// the stop pattern for 2 to 5 offset by 8
while (start[end.length-1] === end[end.length-1]) {
start.pop();
end.pop();
}
// Trim off the most sigificant bit of the end,
// we don't need it
end.pop();
// Front fill zeros on the starting value
// to make the counting easier
while (start.length < end.length) {
start.push('0');
}
// We can break the algorithm in half
// getting from the start value to the form
// 10...0 with only 1 bit set and then getting
// from that point to the end.
var index;
var trains = [];
var expected = '1';
// Now we loop through the digits on the end
// any 1 we find can be added to a temporary array
for (index in end) {
if (end[index] === expected){
result.count++;
trains.push(Math.pow(2, index));
};
}
// if the start value is 0, we can get to the
// intermediate step in one trip, so we can
// just set this to 1, checking both start and
// end because they can be reversed
if (result.start == 0 || result.end == 0) {
index++
result.count++;
result.trains.push(Math.pow(2, index));
// We need to find the first '1' digit, then all
// subsequent 0 digits, as these are the ones we
// need to flip
} else {
for (index in start) {
if (start[index] === expected){
result.count++;
result.trains.push(Math.pow(2, index));
expected = '0';
}
}
}
// add the second set to the first set, reversing
// it to get them in the right order.
result.trains = result.trains.concat(trains.reverse());
// Reverse the stop list if the trip is reversed
if (result.reverse) result.trains = result.trains.reverse();
return result;
}
$(document).ready(function () {
$("#submit").click(function () {
var trains = calculateStops(
parseInt($("#start").val()),
parseInt($("#end").val())
);
$("#out").html(trains.count);
var current = trains.start;
var stopDetails = 'Starting at station ' + current + '<br/>';
for (index in trains.trains) {
current = trains.reverse ? current - trains.trains[index] : current + trains.trains[index];
stopDetails = stopDetails + 'Take train with interval ' + trains.trains[index] + ' to station ' + current + '<br/>';
}
$("#stops").html(stopDetails);
});
});
label {
display: inline-block;
width: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label>Start</label> <input id="start" type="number" /> <br>
<label>End</label> <input id="end" type="number" /> <br>
<button id="submit">Submit</button>
<p>Shortest route contains <span id="out">0</span> stops</p>
<p id="stops"></p>
Simple Java solution
public static int minimumNumberOfStops(int start, final int end) {
// I would initialize it with 0 but the example given in the question states :
// the minimum number of stops between start = 1 and end = 4 is 3 because we can get from 1 to 2 to 4
int stops = 1;
while (start < end) {
start += findClosestPowerOfTwoLessOrEqualThan(end - start);
stops++;
}
return stops;
}
private static int findClosestPowerOfTwoLessOrEqualThan(final int i) {
if (i > 1) {
return 2 << (30 - Integer.numberOfLeadingZeros(i));
}
return 1;
}
NOTICE: Reason for current comments under my answer is that first I wrote this algorithm completely wrong and user2357112 awared me from my mistakes. So I completely removed that algorithm and wrote a new one according to what user2357112 answered to this question. I also added some comments into this algorithm to clarify what happens in each line.
This algorithm starts at procedure main(Origin, Dest) and it simulate our movements toward destination with updateOrigin(Origin, Dest)
procedure main(Origin, Dest){
//at the end we have number of minimum steps in this variable
counter = 0;
while(Origin != Dest){
//we simulate our movement toward destination with this
Origin = updateOrigin(Origin, Dest);
counter = counter + 1;
}
}
procedure updateOrigin(Origin, Dest){
if (Origin == 1) return 2;
//we must find which train pass from our origin, what comes out from this IF clause is NOT exact choice and we still have to do some calculation in future
if (Origin == 0){
//all trains pass from stop 0, thus we can choose our train according to destination
n = Log2(Dest);
}else{
//its a good starting point to check if it pass from our origin
n = Log2(Origin);
}
//now lets choose exact train which pass from origin and doesn't overshoot destination
counter = 0;
do {
temp = counter * 2 ^ (n - 1);
//we have found suitable train
if (temp == Origin){
//where we have moved to
return Origin + 2 ^ ( n - 1 );
//we still don't know if this train pass from our origin
} elseif (temp < Origin){
counter = counter + 1;
//lets check another train
} else {
n = n - 1;
counter = 0;
}
}while(temp < origin)
}

Detecting Conflicts on a Timeline, Part 2: Isolate "True" Overlaps

This is a continuation of my original question about a Timeline-Scheduler Algorithm for plotting overlapping time conflicts: PART 1: Detecting Conflicts on a Scheduler Timeline (Algorithm)
I was given the correct algorithm, shown below, to split up "conflicting" events on 24-hr timeline such that each item in the conflict group occupies N% of the window.
My current problem (PART 2) is that conflicting events are treated as a group and always divided equally, but the real goal is to only isolate "true conflicts" which are not necessarily the whole group.
Consider the following picture.
Here, the original algorithm from Part 1 gave a 3-way split for the events
12:30am - 1:30am
1:00am - 2:30am
2:00am - 4:00am
But this result is slightly incorrect. There are only 2 overlaps, and there should be 2 columns shown. Event #3 can be brought over to Column 1 since it doesn't conflict with Event #1. The only conflict (a max 2-way split) is that #1 conflicts with #2, and #3 also conflicts with #2. As the gray arrow shows, there should be 2 columns for this case.
Original Conflict-Detection Algorithm from Part 1:
* 1) First sort all events by StartTime
* 2) Initialize "lastMaxEndTime" to EndTime of First Event (#1)
* 3) LOOP: For each Event: look at Current Event and Next Event (n+1)
* If Next Event Exists
* if (lastMaxEndTime > NextEvent StartTime) --> CONFLICT!
* - set Overlap mode
* - push conflicting Current Event's StartTime into conflict array
* - UPDATE: lastMaxEndTime = MAX(lastMaxEndTime, NextEvent EndTime)
* else --> NO CONFLICT
* - if we are in Overlap Mode, this is the last overlap
* - push this final conflicting Current Event's StartTime into conflict array
* - draw overlaps now
* - reset Overlap Mode and clear conflict array
* - else
* - this is a normal event, draw at 100%
* - UPDATE: lastMaxEndTime = endTimeNext
*
* Else (No Next Event, this is the last event)
* - if we are in Overlap Mode, this is the last overlap
* - push this final conflicting Current Event's StartTime into conflict array
* - draw overlaps now
* - reset Overlap Mode and clear conflict array
* - else
* - this is a normal event, draw at 100%
Or, a slightly different view of this pseudocode from Patrick's answer,
// first event is the current event
lastMaxEndTime = CurrentEvent EndTime
if NextEvent exists {
// if the maximum end time considered in
// the conflicting component currently
// under consideration extends beyond the
// the next event's start time, then this
// and everything that "conflicts" with it
// is also defined to "conflict" with NextEvent
if (lastMaxEndTime > NextEvent StartTime) { // CONFLICT!
overlappingMode = true;
overlappingEvents.add(currentEvent); // Add to array
lastMaxEndTime = max(lastMaxEndTime, NextEvent EndTime)
}
else { // NO CONFLICT
if (overlappingMode is TRUE) {
// Resolve Now
redrawOverlappingEvents(overlappingEvents);
// Reset
overlappingMode = false;
EMPTY overlappingEvents;
}
// everything that starts earlier than me,
// ends before I start. so start over
lastMaxEndTime = NextEvent EndTime
}
}
You need to partition the events into "lanes", sequences of non-overlapping events. This is generally easy with a "greedy" algorithm. Consider the events in order. For each event, place that event in the first "lane" (vertical column on your chart) where there is no overlap. If the current event overlaps with all columns, then place it into a new column.
Prune's answer is correct. Here is a proof.
In the base case of one event, the algorithm obviously gives an optimal solution of one lane with no overlaps.
Assume the algorithm gives an optimal solution for all numbers of events up to and including k.
We must show that the algorithm gives a correct result for k + 1 events. After k of these k + 1 events, the algorithm has built a schedule with optimally many lanes and no overlaps. It must now place the (k + 1)st event in some lane. Suppose that this event fits into some lane with no overlaps. In that case, place the event there, and the number of lanes must still be optimal (adding more events cannot result in needing fewer lanes). What if the (k + 1)st event overlaps with events in every existing lane?
The only way the (k + 1)st element can overlap with events in all existing lanes is if all existing lanes' latest running events overlap with each other. To see this must be true, consider that the start times are in ascending sorted order, so if any two of the existing lanes' latest running events didn't overlap with each other, the (k + 1)st event wouldn't overlap with the one of the two which finished earlier. But if we have a set of L + 1 events which all overlap with each other, we must have at least L + 1 lanes; one more than L, the optimal number of lanes given k events; and this is what the algorithm guarantees by placing the (k + 1)st element in a new lane in this instance.
Here is an alternative idea - you could fill up the lanes backwards using iterations of optimal event scheduling run in reverse; that is, add events with the latest start time to each lane while avoiding conflicts. This will give you as many non-overlapping events as possible in the first lane. Repeat the process iteratively on new lanes until you run out of events.
(Optimal event scheduling adds events to the schedule by choosing earliest stop time first, and then eliminating remaining events whose start times occur before the stop time chosen during the round. We can imagine time flowing in reverse and using latest start time while eliminating events with stop times after the chosen start time, taking the already sorted list in reverse order. Also, this iterated application of the optimal method should really be proven optimal in its own right, if it even is, but I digress.)
I tried to implement this algorithm here.
I am considering a double-array lanes[x][y] where e.g.
lanes[0] = ["event1", "event4", "event7"]
lanes[1] = ["event2"]
etc.
Algorithm:
// Organize overlapping events into lanes where each lane has non-overlapping events from the conflict group
var lanes = [];
for (var i = 0; i < overlappingEventIDs.length; i++) {
var currlane = 0;
var laneFound = false;
for (var j = 0; j < lanes.length; j++) {
if (!laneFound) {
var conflictInLaneFound = false;
for (var k = 0; k < lanes[j].length; k++) {
if (!laneFound) {
var testEventID = lanes[j][k];
var testEventStartTime = getTime(testEventID.startTime);
var testEventEndTime = getTime(testEventID.endTime);
var thisStartTime = getTime(overlappingEventIDs[i].startTime);
var thisEndTime = getTime(overlappingEventIDs[i].endTime);
if (thisStartTime < testEventEndTime) {
conflictInLaneFound = true;
}
}
}
if (!conflictInLaneFound) {
// Found a lane for this event, Lane #j
lanes[j].push(overlappingEventIDs[i]);
laneFound = true;
}
else {
// Increment currlane
currlane++;
}
}
}
if (!laneFound) { // Need to put this in a new lane
if (lanes[currlane] == undefined) {
lanes.push([]);
}
lanes[currlane].push(overlappingEventIDs[i]);
}
}

Sliding window operation with arbitrary slide distance and no loops (matlab)

I am trying to completely vectorize a sliding window operation that involves an arbitrary slide distance in order to minimize the run time for this code.
I have two vectors: (1) A time vector which records in sample number a series of event times and (2) A channel vector which indicates on which channel each event time was recorded. So:
time = [1,13,58,96,1002];
channel = [1,1,1,2,2];
Which means, for example, that an event was detected at sample number 1 on channel 1. I want to calculate a sliding event count with an arbitrary slide length. For example, if there were only one channel, it would look something like this:
binary = sparse(1,time,1,1,max(time));
nx = max(time); %length of sequence
nwind = <some window size>;
noverlap = <some size smaller than nwind>;
ncol = fix((nx-noverlap)/(nwind-noverlap)); %number of sliding windows
colindex = 1 + (0:(ncol-1))*(nwind-noverlap); %starting index of each
idx = bsxfun(#plus, (1:nwind)', colindex)-1;
eventcounts=sum(binary(idx),1);
I was wondering whether (1) anyone has an idea how to expand this for multiple channels without adding a loop? and (2) perhaps there is an even faster way of making the calculation in general?
Thanks a lot for any ideas. :)
Here is a sample solution without vectorization:
fs = 100; %number of samples in a window
lastwin = max(time);
slide = fs/2; %slide by a half a window
winvec = 0:slide:lastwin;
for x=1:max(channel)
t = histcounts(time(channel==x),winvec);
t = conv(t, [1,1], 'same'); %convolve to get window size of fs
eventcounts(x,:) = t;
end
Ideally, the script would return an [MxN] array, called eventcounts, where M is the total number of channels and N is the total number of windows numel(winvec). In each position (i,j), eventcounts(i,j) would contain the number of events for channel i and window j.

Solving State Space Response with Variable A matrix

I am trying to verify my RK4 code and have a state space model to solve the same system. I have a 14 state system with initial conditions, but the conditions change with time (each iteration). I am trying to formulate A,B,C,D matrices and use sys and lsim in order to compile the results for all of my states for the entire time span. I am trying to do it similar to this:
for t=1:1:5401
y1b=whatever
.
.
y14b = whatever
y_0 = vector of ICs
A = (will change with time)
B = (1,14) with mostly zeros and 3 ones
C = ones(14,1)
D = 0
Q = eye(14)
R = eye(1)
k = lqr(A,B,C,D)
A_bar = A - B*k
sys = ss(A_bar,B,C,D)
u = zeros(14,1)
sto(t,14) = lsim(sys,u,t,y_0)
then solve for new y1b-y14b from outside function
end
In other words I am trying to use sto(t,14) to store each iteration of lsim and end up with a matrix of all of my states for each time step from 1 to 5401. I keep getting this error message:
Error using DynamicSystem/lsim (line 85)
In time response commands, the time vector must be real, finite, and must contain
monotonically increasing and evenly spaced time samples.
and
Error using DynamicSystem/lsim (line 85)
When simulating the response to a specific input signal, the input data U must be a
matrix with as many rows as samples in the time vector T, and as many columns as
input channels.
Any helpful input is greatly appreciated. Thank you
For lsim to work, t has to contain at least 2 points.
Also, the sizes of B and C are flipped. You have 1 input and 1 output so u should be length of t in lsim by 1.
Lastly, it looks like you try to put all initials conditions at once in lsim with y_0 where you just want the part relevant to this iteration.
s = [t-1 t];
u = [0; 0];
if t==1
y0 = y_0;
else
y0 = sto(t-1,1:14);
end
y = lsim(sys, u, s, y0);
sto(t,1:14) = y(end,:);
I'm not sure I understood correctly your question but I hope it helps.

Resources