# Event scheduling.

Given a set of actions (that are performed at a rate of one action per unit time) where each must be performed at an assigned frequency (assume the frequencies add up so things can work) and at approximately uniform intervals, derive a scheduling function who's execution uses as few a resources as possible (that is the cost of calling the function is to be minimized).

An easy example:

1 @ ~1/2
2 @ ~1/4
3 @ ~1/8

1,2,1,3,1,2,1,1,2,1,3,1,2,1,2,...

• The highest frequency and the lowest may differ by a factor of 100-1000.
• The frequencies are approximately exponential distributed, but not exactly and with an arbitrary ratio (e.g. 1.12 or 1.373)
• The function must generate an infinite sequence but may be repeating or non-repeating.
• The function should get as close to exact as possible.
• Real time constraints apply so
• The function must have a deterministic runtime
• The function must provide hard (tight) bounds on the intervals between values.

minf = 10
base = 1.17724
frq = [int(minf * base**i) for i in range(36)]
print sum(frq)  # prints 20000
seen = dict((i,0) for i in frq)
start  = [(0,a) for a in frq]
at = 1.0
for _ in range(20000):
step = [((a-at)*b, a, b) for (a, b) in start]
step.sort()
seen[step] = seen[step] + 1
step = (0, at, step)
start = [(a,b) for (_,a,b) in step]
at = at + 1

for (k,v) in seen.iteritems():
print "%5d %5d    %g" % (k, v, v-k)


output (skipping a few that are close to the right frequency):

 2180  2130    -50
261   278    17
2566  2489    -77
136   148    12
307   325    18
819   834    15
160   174    14
1573  1552    -21
426   449    23
695   713    18
188   203    15
1852  1818    -34
3021  2898    -123
591   612    21
222   239    17
98   109    11
362   384    22
115   127    12
502   525    23


average error 8%

• A trivial solution is that at each point of time, you simply generate an event that has been generated too rarely so far, breaking ties arbitrarily, but I guess you have already considered that. Perhaps you could explain what problems this approach has in your application? – Jukka Suomela Sep 30 '10 at 17:47
• @Jukka: Can you prove that it will give reasonable hard bounds on the intervals? To elaborate can you show it will never end up with everything wanting to run right now? – BCS Sep 30 '10 at 18:09
• Well, if everything wants to run right now, then you have managed to run everything exactly in the right proportions (if nobody is "ahead", then nobody is "behind", either). You can then reset your counters and repeat; if your algorithm is deterministic, you will produce the same sequence repeatedly. In the large scale, everything should be fine, but I guess you are worried of what might happen in the small scale. – Jukka Suomela Sep 30 '10 at 18:49
• @Jukka: See edit. – BCS Sep 30 '10 at 20:46
• I don't understand your problem statement. Do the jobs have lengths? What's your objective function? – Warren Schudy Sep 30 '10 at 22:33

Slightly editing your code, the "trivial" solution that I referred to in my comment would be something like this:

minf = 10
base = 1.17724
frq = [int(minf * base**i) for i in range(36)]
total = sum(frq)
seen = dict((i,0) for i in frq)
for at in range(total):
step = [(c-at*f/float(total), f, c) for (f, c) in seen.iteritems()]
step.sort()
chosenf = step
seen[chosenf] += 1

for (k,v) in seen.iteritems():
print "%5d %5d    %g" % (k, v, v-k)


(Prints zeroes, as expected.)

• I guess I showed the wrong stats. It's not the count of occurrences I care about near as much as the interval between occurrence. The original code beast the modified code by about a factor of 2 in that regard. – BCS Sep 30 '10 at 21:54
• Exactly what would you like to minimise, subject to what constraints? I think you should try to come up with a mathematically precise formulation like "if $a_i$ is the longest interval between consecutive events of type $i$, and $b_i$ is the shortest interval, then I'd like to minimise the maximum of $a_i - b_i$ over all event types $i$". – Jukka Suomela Sep 30 '10 at 22:11