Greedy algorithm correctness - greedy

I'm trying to solve the following problem:
CEO is checking on it's employees at a constant times every day (for
an example the number of minutes after a shift started> {5, 35,
120..}). Calculate when should employees work, so that every time the CEO checks on them they are working. Their work takes 3 minutes to
complete and it increases by another 3 minutes every time they start
working again (First time it takes 3 minutes, second time it takes 6
minutes, third time it takes 9 minutes..).
This is my algorithm in pseudocode:
int time = 2
for: every CEOcheckTime in 0 to CEOcheckTime.length
add (CEOcheckTimes-time) to results
time = time + 2
Ignoring the edge case, where the CEO checks on employees sooner that they can finish the work, is my algorithm correct? Thanks!

Ignoring the 'edge case' this seems fine except your time is being incremented by 2 rather than 3 as per the spec.
int time = 3
for: every CEOcheckTime in CEOcheckTimes
add (CEOcheckTimes-time) to results
time = time + 3
a similar just in time approach could be used here that would account for your edge case too.
for: every CEOcheckTime in CEOcheckTimes
if exists(time):
if last(results) + time < CEOcheckTime:
add (CEOcheckTimes) to results
time = time + 3
else:
add (CEOcheckTimes) to results
time = 3
edit: i have assumed that if work starts / ends at the same time as a check that this counts as working.

Related

In Google Sheets, how do I multiply a duration or interval constant? [duplicate]

This question already has answers here:
How to SUM duration in Google Sheets?
(5 answers)
Closed 2 months ago.
This post was edited and submitted for review 2 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I'm making calculations on production cost (in number of resources) and duration.
I have a process that takes 5 minutes. Using the Duration format, I would enter that as 00:05:00.
I want to queue up this process a certain number of times and calculate the total duration. The output should either be something like 16:35:00 or 5 02:15:00. A "d HH.mm.ss" format.
How, in Google Sheets, do I multiply a Duration by an integer to get a total Duration? To be clear, I am not doing a summation of a column of durations. I am taking a duration constant, such as 5 minutes or 25 minutes, and multiplying it by an integer representing the number of times the process will be run, consecutively.
All these attempts resulted in Formula Parse Error:
=(5*00:05:00)
=(112*00:05:00.000)
=(VALUE(C27)*00:05:00)
=MULTIPLY(VALUE(C27),00:05:00.000)
Well, blow me down. I came up with a workaround while I was trying different ways to fail. I assigned 00:05:00 to it's own cell with the Duration format, then referenced that cell in the formula.
I.E. =C27*J7 gives me 9:20:00 when C27 equates to 112 (it's a summation of it's own) and J7 is the cell holding 00:05:00.
Still doesn't give me days when it goes over 24 hours, and I'd rather have the duration value as a constant in the formula, but it's a step forward.
Would something like this work for you?? It's no longer a number, but if it's for expressing the amount in your desired format it may be useful:
=IF(ROUNDDOWN(W2*W3),ROUNDDOWN(W2*W3)&"d "&TEXT(W2*W3-ROUNDDOWN(W2*W3),"hh:mm:ss"),TEXT(W2*W3,"hh:mm:ss"))
Change the cell references, obviously
PS: If you want to have the value as a constant in your formula, you can try to change the cell reference with TIME function within your formula:
In both Excel and Google spreadsheet, DATE are represented in a number start counting from 1899/12/30,
which...
1 is equal to 1 day
1/24 is equal to 1 hour
1/24/60 is equal to 1 minute
1/24/60/60 is equal to 1 second
you can do like:
=TODAY()+1 which gives you tomorrow, or...
=TODAY()+12/24 which gives you "date of today" 12:00:00
and when you are done with the calculations, you can simply use a TEXT() to format the NUMBER back into DATE format, such as:
=TEXT(TODAY()+7 +13/24 +15/24/60,"yyyy-mm-dd hh:mm:ss")
will return the date of a week away from today at 01:15:00 p.m.
This date/time format doesn't requires a full date to work, you can get difference of two time format like this:
=TEXT(1/24/60 - 1/24/60/60,"hh:mm:ss")
since 1/24/60 is 1 min, and 1/24/60/60 is 1 second,
this formula returns 00:00:59, telling you that there is a 59 seconds diff. between 1 min and 1 sec.

Ruby on Rails, How to reset a counter every day?

I need to save a reference number every time I save a record of a certain model, the reference should be composed of 10 numbers, the first 8 numbers are related to the creator id and date, but the last 2 digits should be an incremental number starting at 00 and ending at 99, this count should be reset every single day.
For example:
Records created the same day:
SD01011800
GF01011801
MT01011802
...
GH01011899
------------------------------------------------------------------------------
Records created the next day:
SD02011800
GF02011801
MT02011802
...
GH02011899
Where the first 2 letters are the initials of a name, the next 2 are the current day, next 2 current month, next 2 current year, next 2 incremental number (from 0 to 99, reset daily)
Also every reference HAS TO be unique.
I'm missing the last two digits part, any idea on how to grant this ?
Thanks for reading.
Where the first 2 letters are the initials of a name, the next 2 are the current day, next 2 current month, next 2 current year, next 2 incremental number (from 0 to 99, reset daily).
As folks in the comments have pointed out, this assumes there is a maximum of 100 entries per day, and it will have problems in 2100. One is more pressing than the other. Maybe if you go over 100 you can start using letters?
Also every reference HAS TO be unique.
For globally unique identifiers UUIDs, Universally Unique IDentifiers, are generally the way to go. If you can change this requirement it would be both simpler (databases already support UUIDs), more robust (UUIDs aren't limited to 100 per day), and more secure (they don't leak information about the thing being identified).
Assuming you can't change the requirement, the next number can be gotten by adding up the number of existing rows that day.
select count(id)
from stuff
where date(created_at) == date(NOW());
However there is a problem if two processes both insert a new record at the same time and get the same next number. Probably highly unlikely if you're expecting only 100 a day, but still possible.
Process 1 Process 2 Time
select count(id) ... 1
select count(id) ... 2
insert into stuff ... 3
insert into stuff... 4
A transaction won't save you. You could get an exclusive lock on the whole table, but that's dangerous and slow. Instead you can use an advisory lock for this one operation. Just make sure all code which writes new records uses this same advisory lock.
Stuff.with_advisory_lock(:new_stuff_record) do
...
end
Alternatively, store the daily ID in a column. Add a database trigger to add 1 on insert. Set it back to 0 with a scheduled job at midnight.
I will assume your class is named Record and it has an attribute called reference_number.
If that is the case, you can use the following method to fetch the two last digits.
def fetch_following_two_last_digits
if Record.last.created_at < Time.current.beginning_of_day
"00"
else
(Record.last.reference_number.last(2).to_i + 1).to_s
end
end
Also assuming you never reach 100 records a day. Otherwise, you'd end up having three last digits.

How do I shift Google Sheets duration value from 35:55:00 to 0:35:55?

I have pasted multiple run duration values from Garmin into a Google Sheet. The longer runs (> 1 hour) copy/paste correctly. Eg: 2:10:35. The problem is shorter (< 1 hour) runs. Eg 35:55. The latter are being shown in Google Sheets as 35:55:00. Ie Google assumes 35:55 is 35 hours and 55 mins, not 35 mins and 55 seconds. So for my shorter sub 1 hour durations I need an easy way to convert 35:55:00 to 0:35:55.
As Tom Sharpe said, there is some room for interpretation in the data you have. But assuming that the duration of your runs is always between 10 minutes and 10 hours, we can disambiguate the values as follows:
=if(A1 > 10/24, A1/60, A1)
Numerically, the duration values are measured in days, so A1 > 10/24 means "more than 10 hours". In this case the value gets divided by 60.
Depending on your workout regime you may want to replace the threshold of 10 by another number; perhaps it's safer to say that the runs are always between 5 minutes and 5 hours.

SPSS - flag cases within a calendar month

I have a list of prisoners and when their prison term started (PrisonStart) and when it ended (PrisonEnd). If they're still in prison, PrisonEnd is blank.
I would like to flag prisoners who were in prison at least one full calendar month during a 6-month period (1/1/16 to 5/30/16).
compute PeriodBeg = date.mdy(01,01,16).
compute PeriodEnd = date.mdy(05,30,16).
formats PeriodBeg PeriodEnd (adate10).
execute.
Any suggestions for how best to go about this? Seems I might need to compare prisoners' start and end dates separately for each month during the 6-month period (like below), and then select any prisoner with at least one full month, but I'm wondering if there's a more efficient way.
if ((PrisonStart le [January 1, 2016]) and (PrisonEnd ge ([January 31, 2016]) | missing(PrisonEnd))) InPrisonJan = 1.
if ((PrisonStart le [February 1, 2016]) and (PrisonEnd ge ([February 28, 2016]) | missing(PrisonEnd))) InPrisonFeb = 1.
etc.
execute.
Some sample data below. The first two prisoners should be flagged as having been in prison for at least one full calendar month during the 6-month period (OneMonth = 1). The last three prisoners were not in prison for at lease one full calendar month during the 6-month period (OneMonth = 0).
data list list /PrisonerID (F8.0) PrisonStart (adate10) PrisonEnd (adate10) PeriodBeg (adate10) PeriodEnd (adate10).
begin data
1 10.3.14 7.12.16 1.1.16 5.30.16
2 2.9.16 4.1.16 1.1.16 5.30.16
3 5.2.16 10.11.16 1.1.16 5.30.16
4 12.1.13 2.8.14 1.1.16 5.30.16
5 1.7.16 1.20.16 1.1.16 5.30.16
6 1.1.17 3.2.17 1.1.16 5.30.16
end data.
The following syntax avoids mentioning the last day of each month separately, so it could be used to automate across any number of months. The trick to check if the date is the last day of the month, is by checking day "0" of NEXT month:
do repeat inpr=inPrison1 to inPrison5/ mon=1 to 5.
compute inpr=( PrisonStart<=date.dmy(1,mon,2016) and
(PrisonEnd>=date.dmy(0,mon+1,2016) or missing (PrisonEnd)) ).
end repeat.

Appointment Scheduling in ruby/rails

I am working on a call center software in rails and need to schedule appointments for agents that can handle calls for the customers. Having said the call center software, need to make sure that I schedule the appointments utilizing the entire agent's schedule as much as possible, leaving scope for least number of holes (where agent has no appointment).
Given an agent's schedule, for example 9:00AM to 5:30PM for a given day, with a lunch break of 30 minutes between 1:00PM - 1:30PM, I need to schedule appointments of varying length in duration, some 60 minutes and some 90 minutes.
And if some reason, lunch break is leaving some holes in the schedule, I should be able to move lunch break 30minutes +/-, so instead of 1:00PM - 1:30PM it could be moved between 1:30PM - 2:00PM or 12:30PM - 1:00PM.
I started off creating lunch breaks as a kind of appointment which will provide the flexibility of moving the starts_at and finishes_at attributes. And the appointments being either 60 minutes or 90 minutes, which are multiple of 30 minutes and lunch also being 30 minutes, I started off splitting agents schedule into slots of 30 minutes each.
So for a given agent on a given day, looking at his schedule I instantiated an array of slots each with a duration of 30 minutes and starts_at and finishes_at attributes be like 9:00AM - 9:30AM, 9:30AM - 10:00AM, etc.
I need some help on looping through this array of appointment slots and pull either 2 consecutive slots or 3 consecutive slots depending upon 60 or 90 minute duration appointment, keeping in mind that I should be able to move the lunch +/- 30 minutes.
Any help is much appreciated.
Looking at your problem:
Appointments are either 60 or 90 minutes long.
Lunch can vary between a 90 minute interval 12:30-2:00
And we want to minimize the amount of minutes that have no appointments.
Now, you have a time interval to fill, which is 9:00AM to 5:30pm. Assuming appointments fall between 9:00-5:30, we can use a greedy algorithm for interval scheduling based on earliest finish time (source) with your additional constraint.
Basically the algorithm is as follows (in pseudo)
Let R be the set of all appointments
Let R11 be the set of appointments from R before 12:30 that are compatible with 12:30-1:00 and R12 be the set of appointments from R after 1:00 that are compatible with 12:30-1:00
Let R21 be the set of appointments from R before 1:00 that are compatible with 1:00-1:30 and R22 be the set of appointments from R after 1:30 that are compatible with 1:00-1:30
Let R31 be the set of appointments from R before 1:30 that are compatible with 1:30-2:00 and R32 be the set of appointments from R after 2:00 that are compatible with 1:30-2:00
Let R1Comb = findSet(R11) + 12:30-1:00 + findSet(R12)
Let R2Comb = findSet(R21) + 1:00-1:30 + findSet(R22)
Let R3Comb = findSet(R31) + 1:30-2:00 + findSet(R32)
Function findSet(R)
Let A be the time interval to fill
While R is not empty
Choose a request r in R that has the smallest finishes_at
Add r to A
Remove all appointments in R that are not compatible with r
EndWhile
Return A
EndFunction
Return the R that has the smallest amount of holes in R1Comb, R2Comb, R3Comb
This algorithm makes use of a few concepts
An appointment r1 is not compatible with r2 if they overlap.
Because of #1, we know that Ri1/Ri2 for i=1,2,3 will not be conflicting with each other. Because if an appointment in Ri2 is not compatible with Ri1, then it is also not compatible with the lunch period, which is a contradiction because we took out all the non compatible appointments.
Once we split the set of appointments, then it's a matter of solving 2 scheduling problems, which can be solved greedily.
An this algorithm is still O(n log n) because you are doing the greedy algorithm 6 times (a constant), and each greedy iteration is O(n log n), and the first few lines and the last line are all O(n).
People write theses on scheduling and it's not an easy problem. I suggest you looking at http://www.asap.cs.nott.ac.uk/watt/resources/university.html to get a better understanding.
Good luck :)

Resources