Reputation:
I'm ripping my hair out on the following so I'm kindly asking for help here.
I have an array of durations in minutes whose total is one day (1440 min.)
I have another array with a value which represent a protection level : 0 to 5.
var levels = new int[3];
levels[0] = 0;
levels[1] = 5;
levels[2] = 0;
var minutes = new int[3];
minutes[0] = 658; // 10 hours and 58 minutes
minutes[1] = 1; // 1 minute
minutes[2] = 781; // 13 hours and 1 minutes
In plain English we should read: Level 0 duration is 10 hours and 58 minutes, then Level 1 duration is 1 minute and finally we're back to level 0 for 13 hours and 1 minute. End of the day ;)
Of couse this is a simple example, this could be more richer than that, but I hope this helps understand the problem I'm trying to solve which is exposed below.
I need to compute an average of protection level for each hour of the day (from 0 to 23)\
The final result set would be an array of tuples with 6 items
var finalResult = new (int level0percent, int level1percent, int level2percent, int level3percent, int level4percent, int level5percent)[24];
With the data in this example that would mean:
A this point we have 2 minutes left (4%) to be given to Hour 10.
Still 2% (1 minute) to consume on the next level, which is 0 for now.
Then the remaining is now 13 hours for level 0 at index 11 to 23.
The tricky part for me is to process, in this case, the hour at index 10. May be I should restructure the data in a form that would be simplier to process, but I can't figure it out.
Any help appreciated.
PS : If Linq could be of any help, please do not hesitate to use it.
EDIT : Fixed the minutes array.
EDIT 2 : This what I've tried so far...
A level class to hold an intermediary result set:
public class Levels
{
public int level0 { get; set; }
public int level1 { get; set; }
public int level2 { get; set; }
public int level3 { get; set; }
public int level4 { get; set; }
public int level5 { get; set; }
}
The processing code so far... May not compile and/or work:
// Construct the final result to be sent back to the client
var averagesOn24HoursByHourNew = Enumerable.Range(0, 24).Select(i => new Levels()).ToArray();
// This index will allow us to advance each hour of the day (from 0 to 23)
int lastIndexOnAveragesOn24HoursByHourNew = 0;
// Used to gather the remaining minutes for incomplete hours
int lastMinuteLeftOnLastHour = 0;
for (var indexLevel = 0; indexLevel < levels.Count; indexLevel++)
{
if (levels[indexLevel] == 0)
{
int durationInMinutes = (int)minutes[indexLevel];
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[lastIndexOnAveragesOn24HoursByHourNew].level0 += lastMinuteLeftOnLastHour * 100 / 60;
nbMinutesLeft -= lastMinuteLeftOnLastHour;
lastMinuteLeftOnLastHour = 0;
}
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
averagesOn24HoursByHourNew[index].level0 += 100;
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level0 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
else
{
lastMinuteLeftOnLastHour = 60 - nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else if (levels[indexLevel] == 1)
{
int durationInMinutes = (int)minutes[indexLevel];
// A duration of zero means less than one minute
// set it to one minute
if (durationInMinutes == 0)
{
durationInMinutes = 1;
}
else if (durationInMinutes == 1439)
{
// FIXME Add Missing minute if needed
durationInMinutes = 1440;
}
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[index].level1 += lastMinuteLeftOnLastHour * 100 / 60;
lastMinuteLeftOnLastHour = 0;
}
else
{
averagesOn24HoursByHourNew[index].level1 += 100;
}
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level1 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else if (levels[indexLevel] == 2)
{
int durationInMinutes = (int)minutes[indexLevel];
// A duration of zero means less than one minute
// set it to one minute
if (durationInMinutes == 0)
{
durationInMinutes = 1;
}
else if (durationInMinutes == 1439)
{
// FIXME Add Missing minute if needed
durationInMinutes = 1440;
}
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[index].level2 += lastMinuteLeftOnLastHour * 100 / 60;
lastMinuteLeftOnLastHour = 0;
}
else
{
averagesOn24HoursByHourNew[index].level2 += 100;
}
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level2 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else if (levels[indexLevel] == 3)
{
int durationInMinutes = (int)minutes[indexLevel];
// A duration of zero means less than one minute
// set it to one minute
if (durationInMinutes == 0)
{
durationInMinutes = 1;
}
else if (durationInMinutes == 1439)
{
// FIXME Add Missing minute if needed
durationInMinutes = 1440;
}
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[index].level3 += lastMinuteLeftOnLastHour * 100 / 60;
lastMinuteLeftOnLastHour = 0;
}
else
{
averagesOn24HoursByHourNew[index].level3 += 100;
}
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level3 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else if (levels[indexLevel] == 4)
{
int durationInMinutes = (int)minutes[indexLevel];
// A duration of zero means less than one minute
// set it to one minute
if (durationInMinutes == 0)
{
durationInMinutes = 1;
}
else if (durationInMinutes == 1439)
{
// FIXME Add Missing minute if needed
durationInMinutes = 1440;
}
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[index].level4 += lastMinuteLeftOnLastHour * 100 / 60;
lastMinuteLeftOnLastHour = 0;
}
else
{
averagesOn24HoursByHourNew[index].level4 += 100;
}
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level4 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else if (levels[indexLevel] == 5)
{
int durationInMinutes = (int)minutes[indexLevel];
// A duration of zero means less than one minute
// set it to one minute
if (durationInMinutes == 0)
{
durationInMinutes = 1;
}
else if (durationInMinutes == 1439)
{
// FIXME Add Missing minute if needed
durationInMinutes = 1440;
}
var nbMinutesLeft = durationInMinutes % 60;
var nbHoursAtOneHundred = durationInMinutes / 60;
int index = 0;
for (index = lastIndexOnAveragesOn24HoursByHourNew; index < nbHoursAtOneHundred + lastIndexOnAveragesOn24HoursByHourNew; index++)
{
#if DEBUG
Debug.Assert(index <= 23);
#endif
if (lastMinuteLeftOnLastHour != 0)
{
averagesOn24HoursByHourNew[index].level5 += lastMinuteLeftOnLastHour * 100 / 60;
lastMinuteLeftOnLastHour = 0;
}
else
{
averagesOn24HoursByHourNew[index].level5 += 100;
}
datiCountForOneHour[index] += 1;
}
#if DEBUG
// When th whole day is processed the index will be 24
Debug.Assert(index < 25);
#endif
if (nbMinutesLeft != 0)
{
averagesOn24HoursByHourNew[index].level5 += nbMinutesLeft * 100 / 60;
if (lastMinuteLeftOnLastHour != 0 && lastMinuteLeftOnLastHour > nbMinutesLeft)
{
lastMinuteLeftOnLastHour -= nbMinutesLeft;
}
}
lastIndexOnAveragesOn24HoursByHourNew = index;
}
else
{
throw new InvalidOperationException("Invalid protection level found in stats");
}
}
Upvotes: 0
Views: 112
Reputation: 17436
You can try it this way. Probably not the most elegant solution, but gets the job done and can be a basis for further optimization. For explanation see comments in the code.
using System;
using System.Linq;
public class Program
{
class percentages {
public double l0;
public double l1;
public double l2;
public double l3;
public double l4;
public double l5;
public string ToString() {
return $"{l0:F2} {l1:F2} {l2:F2} {l3:F2} {l4:F2} {l5:F2} ";
}
}
public static void Main()
{
var levels = new int[]{0,5,0};
var minutes = new int[]{658, 1, 781};
var plist = new percentages[24];
//sum up the minutes to total duration on this day
//ie minutes will become [658, 659, 1440]
for (int i = 1; i < minutes.Length; i++)
minutes[i] += minutes[i-1];
//lindex is the current index in the levels/minutes array
var lindex = 0;
//pindex is the current index in the percentages array
var pindex = 0;
var p = new percentages();
//a constant of how much 1 minute contributes in percent of an hour
var m1 = 100.0 / 60;
//check every minute of the day
for (int m = 1; m <= 1440; m++) {
//increment the respective level by 1 minute
switch(levels[lindex]) {
case 0: p.l0 += m1; break;
case 1: p.l1 += m1; break;
case 2: p.l2 += m1; break;
case 3: p.l3 += m1; break;
case 4: p.l4 += m1; break;
case 5: p.l5 += m1; break;
}
//if the minutes reached a level change increment the index in the level/minute array
if (m == minutes[lindex]) lindex++;
//if the minutes reached a full hour, add the current percentages to the list, increment index and reset for the next hour
if (m % 60 == 0){
plist[pindex++] = p;
Console.WriteLine(p.ToString());
p = new percentages();
}
}
}
}
Be aware, there are no sanity checks whatsoever. This will break if levels
and minutes
don't have the same length or if the sum of minutes
is not 1440
. For further use, you should add these checks.
Upvotes: 1
Reputation: 103
I suggest you create another array level_at_min
where the index is a minute in the day.
level_at_min
will be of size 1440 (I show an algorithm to do it later).
In your example you will have
level_at_min[0] = 0
...
level_at_min[657] = 0
level_at_min[658] = 5
level_at_min[659] = 0
...
level_at_min[1439] = 0
If you have that array, you can create an array of lists (of length 60) levels_at_hour
,
that will give for your example
levels_at_hour[0] = [0,0, ..., 0]
levels_at_hour[1] = [0,0, ..., 0]
....
levels_at_hour[11] = [0,..., 5,0]
...
levels_at_hour[23] = [0,0,...,0]
With this you can compute the average protection level at each hour, or the percentage of each protection level at each hour, whatever you want.
Algorithm to create level_at_min
: (n
the length of array level
s and array minutes
)
minute_counter = 0
for each block in 0 to n-1
for each minute in minute_counter to (minute_counter+minutes[block])
level_at_min[min] = levels[block]
minute_counter = minute_counter + minutes[block]
Algorithm to create levels_at_hour
:
for each hour in 0 to 23:
for each minute in i*60 to (i+1)*60
add level_at_min[minute] to the list levels_at_hour[hour]
Upvotes: 0