What's a good algorithm for determining the remaining time for something to complete? I know how many total lines there are, and how many have completed already, how should I estimate the time remaining?
Why not?
(linesProcessed / TimeTaken) (timetaken / linesProcessed) * LinesLeft = TimeLeft
TimeLeft will then be expressed in whatever unit of time timeTaken is.
Edit:
Thanks for the comment you're right this should be:
(TimeTaken / linesProcessed) * linesLeft = timeLeft
so we have
(10 / 100) * 200 = 20 Seconds now 10 seconds go past
(20 / 100) * 200 = 40 Seconds left now 10 more seconds and we process 100 more lines
(30 / 200) * 100 = 15 Seconds and now we all see why the copy file dialog jumps from 3 hours to 30 minutes :-)
I'm surprised no one has answered this question with code!
The simple way to calculate time, as answered by #JoshBerke, can be coded as follows:
DateTime startTime = DateTime.Now;
for (int index = 0, count = lines.Count; index < count; index++) {
// Do the processing
...
// Calculate the time remaining:
TimeSpan timeRemaining = TimeSpan.FromTicks(DateTime.Now.Subtract(startTime).Ticks * (count - (index+1)) / (index+1));
// Display the progress to the user
...
}
This simple example works great for simple progress calculation.
However, for a more complicated task, there are many ways this calculation could be improved!
For example, when you're downloading a large file, the download speed could easily fluctuate. To calculate the most accurate "ETA", a good algorithm would be to only consider the past 10 seconds of progress. Check out ETACalculator.cs for an implementation of this algorithm!
ETACalculator.cs is from Progression -- an open source library that I wrote. It defines a very easy-to-use structure for all kinds of "progress calculation". It makes it easy to have nested steps that report different types of progress. If you're concerned about Perceived Performance (as #JoshBerke suggested), it will help you immensely.
Make sure to manage perceived performance.
Although all the progress bars took exactly the same amount of time in the test, two characteristics made users think the process was faster, even if it wasn't:
progress bars that moved smoothly towards completion
progress bars that sped up towards the end
Not to revive a dead question but I kept coming back to reference this page.
You could create an extension method on the Stopwatch class to get functionality that would get an estimated remaining time span.
static class StopWatchUtils
{
/// <summary>
/// Gets estimated time on compleation.
/// </summary>
/// <param name="sw"></param>
/// <param name="counter"></param>
/// <param name="counterGoal"></param>
/// <returns></returns>
public static TimeSpan GetEta(this Stopwatch sw, int counter, int counterGoal)
{
/* this is based off of:
* (TimeTaken / linesProcessed) * linesLeft=timeLeft
* so we have
* (10/100) * 200 = 20 Seconds now 10 seconds go past
* (20/100) * 200 = 40 Seconds left now 10 more seconds and we process 100 more lines
* (30/200) * 100 = 15 Seconds and now we all see why the copy file dialog jumps from 3 hours to 30 minutes :-)
*
* pulled from http://stackoverflow.com/questions/473355/calculate-time-remaining/473369#473369
*/
if (counter == 0) return TimeSpan.Zero;
float elapsedMin = ((float)sw.ElapsedMilliseconds / 1000) / 60;
float minLeft = (elapsedMin / counter) * (counterGoal - counter); //see comment a
TimeSpan ret = TimeSpan.FromMinutes(minLeft);
return ret;
}
}
Example:
int y = 500;
Stopwatch sw = new Stopwatch();
sw.Start();
for(int x = 0 ; x < y ; x++ )
{
//do something
Console.WriteLine("{0} time remaining",sw.GetEta(x,y).ToString());
}
Hopefully it will be of some use to somebody.
EDIT:
It should be noted this is most accurate when each loop takes the same amount of time.
Edit 2:
Instead of subclassing I created an extension method.
Generally, you know three things at any point in time while processing:
How many units/chunks/items have been processed up to that point in time (A).
How long it has taken to process those items (B).
The number of remaining items (C).
Given those items, the estimate (unless the time to process an item is constant) of the remaining time will be
B * C / A
I made this and it works quite good, feel free to change the method signature according to your variable types or also to the return type, probably you would like to get the TimeSpan object or just the seconds...
/// <summary>
/// Calculates the eta.
/// </summary>
/// <param name="processStarted">When the process started</param>
/// <param name="totalElements">How many items are being processed</param>
/// <param name="processedElements">How many items are done</param>
/// <returns>A string representing the time left</returns>
private string CalculateEta(DateTime processStarted, int totalElements, int processedElements)
{
int itemsPerSecond = processedElements / (int)(processStarted - DateTime.Now).TotalSeconds;
int secondsRemaining = (totalElements - processedElements) / itemsPerSecond;
return new TimeSpan(0, 0, secondsRemaining).ToString();
}
You will require to initialize a DateTime variable when the processing starts and send it to the method on each iteration.
Do not forget that probably your window will be locked if the process is quite long, so when you place the return value into a control, don't forget to use the .Refresh() method of it.
If you are using threads then you can attempt to set the text using the Invoke(Action) method, would be easier to use this extension method to archieve it easily.
If you use a console application, then you should not have problems displaying the output line by line.
Hope it helps someone.
It depends greatly on what the "something" is. If you can assume that the amount of time to process each line is similar, you can do a simple calculation:
TimePerLine = Elapsed / LinesProcessed
TotalTime = TimePerLine * TotalLines
TimeRemaining = TotalTime - LinesRemaining * TimePerLine
there is no standard algorithm i know of, my sugestion would be:
Create a variable to save the %
Calculate the complexity of the task you wish to track(or an estimative of it)
Put increments to the % from time to time as you would see fit given the complexity.
You probably seen programs where the load bar runs much faster in one point than in another. Well that's pretty much because this is how they do it. (though they probably just put increments at regular intervals in the main wrapper)
Where time$("ms") represents the current time in milliseconds since 00:00:00.00, and lof represents the total lines to process, and x represents the current line:
if Ln>0 then
Tn=Tn+time$("ms")-Ln 'grand total of all laps
Rn=Tn*(lof-x)/x^2 'estimated time remaining in seconds
end if
Ln=time$("ms") 'start lap time (current time)
That really depends on what is being done... lines are not enough unless each individual line takes the same amount of time.
The best way (if your lines are not similar) would probably be to look at logical sections of the code find out how long each section takes on average, then use those average timings to estimate progress.
If you know the percentage completed, and you can simply assume that the time scales linearly, something like
timeLeft = timeSoFar * (1/Percentage)
might work.
I already knew the percentage complete & time elapsed, so this helped me:
TimeElapsed * ((100 - %complete) / %complete) = TimeRemaining
I then updated this value every time %complete changed, giving me a constant varying ETA.
There is 2 ways of showing time
Time elapsed and Time Remaining overall:
so elapsed will increase but remaining will be likely stable total time needed (if per second is stable)
Time elapsed and Time Left:
so Time Left = Total Needed - Elapsed
My idea/formula is more likely like this:
Processed - updated from running thread from 0 to Total
I have timer with 1000ms interval that calculates processed per second:
processedPerSecond = Processed - lastTickProcessed;
lastTickProcessed = Processed; //store state from past call
processedPerSecond and lastTickProcessed are global variables out of timer method
Now if we would like to get how many seconds is required to complete the processing (in ideal constant assumption)
totalSecondsNeeded = TotalLines / PerSecond
but we want to show case 2. TimeLeft so
TimeLeftSeconds = (TotalLines - Processed) / PerSecond
TimeSpan remaining = new TimeSpan(0, 0, (transactions.Count - Processed) / processedPerSecond);
labelTimeRemaining.Text = remaining.ToString(#"hh\:mm\:ss");
Of course TimeLeftSeconds will "jump" if PerSecond jumps, so if past PerSecond was 10 then 30 then back to 10, the user will see it.
There is a way to calculate average, but this may not show real time left if process speeds up at the end
int perSecond = (int)Math.Ceiling((processed / (decimal)timeElapsed.TotalSeconds)); //average not in past second
So it may be the choice for a developer to "pick" a method that will be most accurate based on prediction of how "jumpy" the processing is
We could also calculate and save each PerSecond, then take last 10 second and made average, but in this case user will have to wait 10 seconds to see first calculation
or we could show time left starting from first per second and then progressively average summing up to 10 last PerSecond
I hope my "jumpy" thoughts will help someone to build something satisfying
How about this....
I used this to walk through a set of records (rows in an Excel file, in one case)
L is the current row number
X is the total number of rows
dat_Start is set to Now() when the routine begins
Debug.Print Format((L / X), "percent") & vbTab & "Time to go:" & vbTab & Format((DateDiff("n", dat_Start, Now) / L) * (X - L), "00") & ":" & Format(((DateDiff("s", dat_Start, Now) / L) * (X - L)) Mod 60, "00")
PowerShell function
function CalculateEta([datetime]$processStarted, [long]$totalElements, [long]$processedElements) {
$itemsPerSecond = $processedElements / [DateTime]::Now.Subtract($processStarted).TotalSeconds
$secondsRemaining = ($totalElements - $processedElements) / $itemsPerSecond
return [TimeSpan]::FromSeconds($secondsRemaining)
}
I prefer System.Threading.Timer rather than System.Diagnostics.Stopwatch.
System.Threading.Timer, which executes a single callback method on a
thread pool thread
The following code is an example of a calculating elapsed time with Threading.Timer.
public class ElapsedTimeCalculator : IDisposable
{
private const int ValueToInstantFire = 0;
private readonly Timer timer;
private readonly DateTime initialTime;
public ElapsedTimeCalculator(Action<TimeSpan> action)
{
timer = new Timer(new TimerCallback(_ => action(ElapsedTime)));
initialTime = DateTime.UtcNow;
}
// Use Timeout.Infinite if you don't want to set period time.
public void Fire() => timer.Change(ValueToInstantFire, Timeout.Infinite);
public void Dispose() => timer?.Dispose();
private TimeSpan ElapsedTime => DateTime.UtcNow - initialTime;
}
BTW You can use System.Reactive.Concurrency.IScheduler (scheduler.Now.UtcDateTime) instead of using DateTime directly, if you would like to mock and virtualize the datetime for unit tests.
public class PercentageViewModel : IDisposable
{
private readonly ElapsedTimeCalculator elapsedTimeCalculator;
public PercentageViewModel()
{
elapsedTimeCalculator = new ElapsedTimeCalculator(CalculateTimeRemaining))
}
// Use it where You would like to estimate time remaining.
public void UpdatePercentage(double percent)
{
Percent = percent;
elapsedTimeCalculator.Fire();
}
private void CalculateTimeRemaining(TimeSpan timeElapsed)
{
var timeRemainingInSecond = GetTimePerPercentage(timeElapsed.TotalSeconds) * GetRemainingPercentage;
//Work with calculated time...
}
public double Percent { get; set; }
public void Dispose() => elapsedTimeCalculator.Dispose();
private double GetTimePerPercentage(double elapsedTime) => elapsedTime / Percent;
private double GetRemainingPercentage => 100 - Percent;
}
In Python:
First create a array with the time taken per entry, then calculate the remaining elements and calculate average time taken
import datetime from datetime
import time
# create average function**
def average(total):
return float(sum(total)) / max(len(total), 1)
# create array
time_elapsed = []
# capture starting time
start_time = datetime.now()
# do stuff
# capture ending time
end_time = datetime.now()
# get the total seconds from the captured time (important between two days)
time_in_seconds = (end_time - start_time).total_seconds()
# append the time to a array
time_elapsed.append(time_in_seconds)
# calculate the remaining elements, then multiply it with the average time taken
est_time_left = (LastElement - Processed) * average(time_elapsed)
print(f"Estimated time left: {time.strftime('%H:%M:%S', time.gmtime(est_time_left))}")
** timeit() with k=5000 random elements and number=1000
def average2(total):
avg = 0
for e in total: avg += e
return avg / max(len(total),1)
>> timeit average 0.014467999999999925
>> timeit average2 0.08711790000000003
>> timeit numpy.mean: 0.16030989999999967
>> timeit numpy.average: 0.16210096000000003
>> timeit statistics.mean: 2.8182458
Related
I am attempting to create a timesheet calculator which takes calculates the time an employee works and I am close, with one problem.
As I perform the calculation, I only want hours and minutes to display. I am able to get that done, but that causes an issue. If the employee punches out before a full minute is elapsed, that minute is not included in the calculation.
For example, if an emp punches in at 12:00:30 and punches out at 5:00:29, that last minute is not counted in the calculation, so the time shows as 4:59 instead of 5:00.
How do I get the calculation to be based on the hours and minutes and exclude seconds completely?
This is the code I have:
private void btnPunchOut_Click(object sender, EventArgs e)
{
DateTime stopTime = DateTime.Now;
lblPunchOutTime.Text = stopTime.ToShortTimeString();
TimeSpan timeWorked = new TimeSpan();
timeWorked = stopTime - startTime;
lblTimeWorked.Text = timeWorked.ToString(#"hh\:mm");
}
Use TimeSpan.TotalSeconds perhaps...And then add 30 seconds or more, before you convert it to hours by dividing by 3600.
As in
lblTimeWorked.Text = ((timeWorked.TotalSeconds+30)/3600).ToString("0.00") + " hours";
Use Timespan.TotalHours if you want the hours.
But if you want to be accurate, you should create a separate class dedicated to calculating the hours worked by a staff member. Then you can encapsulate lots of business rules in the dedicated class. Staff have entitlements and overtime, expenses or penalty rates - so this can get complex if done properly.
If you want a calculation that really ignores the seconds, the clearest way to accomplish that is to get rid of the seconds on both the start time and the end time. It might not seem accurate because it allows a difference of one second to become a difference of one minute. But that could still be a valid business rule, that you want to subtract according the the minutes that appeared on the clock rather than the actual elapsed seconds.
In other words,
1:00:01 is adjusted to 1:00:00.
1:00:59 is adjusted to 1:00:00.
1:01:00 is "adjusted" to 1:01:00.
1:01:01 is adjusted to 1:01:00.
You can accomplish that with an extension like this:
public static class TimespanExtensions
{
public static TimeSpan TrimToMinutes(this TimeSpan input)
{
return TimeSpan.FromMinutes(Math.Truncate(input.TotalMinutes));
}
}
(I'm sure there's a more efficient way of truncating the seconds, but at least this is clear.)
Now instead of having to figure out how to calculate the difference while rounding seconds or adding seconds, you just trim the seconds before calculating the difference. Here's a unit test:
[TestMethod]
public void NumberOfMinutesIgnoresSeconds()
{
var startTime = TimeSpan.FromSeconds(59).TrimToMinutes();
var endTime = TimeSpan.FromSeconds(60).TrimToMinutes();
Assert.AreEqual(1, (endTime - startTime).TotalMinutes);
}
One Timespan represents 59 seconds, and the next one is 60, or the first second of the next minute. But if you trim the seconds and then calculate the difference you get exactly one minute.
In the context of your code,
DateTime stopTime = DateTime.Now;
lblPunchOutTime.Text = stopTime.ToShortTimeString();
var timeWorked = stopTime.TrimToMinutes() - startTime.TrimToMinutes();
lblTimeWorked.Text = timeWorked.ToString(#"hh\:mm");
I'm calling an update function to draw a real time simulation and was wondering if there was an effective way to get the number of milliseconds passed since the last update? At the moment I have a DispatchTimer calling at regular intervals to update the simulation but the timing isn't accurate enough and ends up being about 60% slower than it should be (it varies).
I would use Stopwatch.GetTimestamp() to get a tick count, then compare the value before and after. You can convert this to timings by:
var startTicks = Stopwatch.GetTimestamp();
// Do stuff
var ticks = Stopwatch.GetTimestamp() - startTicks;
double seconds = ticks / Stopwatch.Frequency;
double milliseconds = (ticks / Stopwatch.Frequency) * 1000;
double nanoseconds = (ticks / Stopwatch.Frequency) * 1000000000;
You could also use var sw = Stopwatch.StartNew(); and sw.Elapsed.TotalMilliseconds afterwards if you just want to time different chunks of code.
Keep a variable that will not reset between calls.
Yours may not need to be static like mine.
private static DateTime _LastLogTime = DateTime.Now;
Then within the method:
// This ensures only the exact one Tick is used for subsequent calculations
// Instead of calling DateTime.Now again and getting different values
DateTime NewTime = DateTime.Now;
TimeSpan ElapsedTime = NewTime - _LastLogTime;
_LastLogTime = NewTime;
string LogMessage = string.Format("{0,7:###.000}", ElapsedTime.TotalSeconds);
I only needed down to the thousandth of a second within my string, but you can get much more accurate with the resulting TimeSpan.
Also there is a .TotalMilliseconds or even .Ticks(the most accurate) value available within the resulting TimeSpan.
I have a dataset of voltages (Sampled every 500ms). Lets say it looks something like this (In an array):
0ms -> 1.4v
500ms -> 1.3v
1000ms -> 1.2v
1500ms -> 1.5v
2000ms -> 1.3v
2500ms -> 1.3v
3000ms -> 1.2v
3500ms -> 1.3v
Assuming the transition between readings is linear (IE: 250ms = 1.35v), how would I go about calculating the total % of time that the reading is above or equal to 1.3v?
I was initially going to just get % of values that are >= 1.3v (IE: 6/8 in sample array), however this only works if the angle between points is 45 degrees. I am assuming I have to do something like create a line from point 1 to point 2 and find the intercept with the base line (1.3v). Then do the same for point 2 and point 3 and find the distance between both intersects (Say 700ms) then repeat for all points and get as a % of total sample time.
EDIT
Maybe I wasn't clear when I initially asked. I need help with identifying how I can perform these calculations, IE: objects/classes that I can use to help me virtually graph these lines and perform these calculations or any 3rd party math packages that might offer these capabilities.
The important part is not to think in data points, but in intervals. Every interval (e.g. 0-500, 500-1000, ...) is one of three cases (starting with float variables above and below both 0):
Trivial: Both start and end point are below your threshold - below += 1
Trivial: Both start and end point are above your threshold - above += 1
Interesting: One point is below, one above your threshold. Let's call the smaller value min and the higher value max. Now we do above += (max-threshold)/(max-min) and below += (threshold-min)/(max-min), so we linearily distribute this interval between both states.
Finally normalize the results by dividing both above and below by the number of intervals. This will give you a pair of numbers, that represent the fractions, i.e. that add up to 1 modulo rounding errors. Ofcourse multiplication with 100 gives you the percentages.
EDIT
#phoog pointed out in the comment, that I did not mention an "equal" case. This is by design, as your question already contains that: You chose >= as a comparison, so I definitly ment to use the same comparison here.
If I've understood the problem correctly, you can use a class like this to hold each entry:
public class DataEntry
{
public DataEntry(int time, double reading)
{
Time = time;
Reading = reading;
}
public int Time { get; set; }
public double Reading { get; set; }
}
And then the following link statement to get segments above 1.3:
var entries = new List<DataEntry>()
{
new DataEntry(0, 1.4),
new DataEntry(500, 1.3),
new DataEntry(1000, 1.2),
new DataEntry(1500, 1.5),
new DataEntry(2000, 1.3),
new DataEntry(2500, 1.3),
new DataEntry(3000, 1.2),
new DataEntry(3500, 1.3)
};
double totalTime = entries
.OrderBy(e => e.Time)
.Take(entries.Count - 1)
.Where((t, i) => t.Reading >= 1.3 && entries[i + 1].Reading >= 1.3)
.Sum(t => 500);
var perct = (totalTime / entries.Max(e => e.Time));
This should give you the 500ms segments that remained above 1.3.
i'm trying to do a average speed of bugs killed per min. This is my code now but its throwing an error saying string input format is wrong. Any advice? I'm using C# for WPF.
//Score is the number of bugs hit
score.Text = (_killings * 1).ToString();
//Change it to integer
int x = Int32.Parse(score.Text);
int y = Int32.Parse(TBCountdown.Text); - this is where the error is showing
//Get the average speed by dividing number of bugs hit during the time
int average = (x / y);
//Displaying the average score by converting int to string
averagescore.Text = average.ToString();
For more information, i'm using a dispatcher timer and this is my code for the timer.
TBCountdown.Text = string.Format("00:0{0}:0{1}", time / 60, time % 60);
There are a couple of things that are wrong with your code, I am presuming that you are incrementing a variable and using it to create an elapsed time of some sort. The first thing to state is that Ed S. is correct and you should not be using your UI to store your value, create a backing variable and work with that. As far as your issues: the first one that comes to mind that I mentioned in my comments is that you are trying to convert a string that looks like a TimeSpan to an integer, the second is that your Format String will not convert to a DateTime Object it comes up with a Format Exception Error. I was able to get your code to work by changing your Timer's Tick eventhandler to this.
DateTime temp = DateTime.Parse(string.Format("00:{0}:{1}", time / 60, time % 60));
TBCountdown.Text = temp.ToString("00:mm:ss");
and you can use something like this to get your Y value (you did not respond to me about what information you were wanting for value)
int y = (int)TimeSpan.Parse(TBCountdown.Text).TotalSeconds;
There is a lot of conjecture here about what you want, if this isn't correct please let us know.
This is how you format the textbox:
TBCountdown.Text = string.Format("00:0{0}:0{1}", time / 60, time % 60);
And then you try to parse that as an int. Well, it's not an int. Parse it back as a DateTime or, better yet, just use the variable you already have (time). In general it is a poor idea to rely on your UI for storing your data. The UI is for display.
int y = time;
TBCountdown.Text = string.Format("00:0{0}:0{1}", (time / 60).ToString(), (time % 60).ToString());
remember this is a string function
I'd like to write a class that will fire an event x a pre-defined numbers of times per second call it n.
However, I would like x NOT to fire evenly spaced within each second.
So, say n = 100, 25 might fire in the first 300ms then a further 50 over the next 600ms and the final 25 in the remaining 100ms.
Ideally, I'd like better granularity then that presented above and a greater range of spacing within each second.
I'm wondering if I would be able to create an array with defined millisecond and how many events to fire. Then use a loop and the Stopwatch class to determine whether the defined events for that millisecond waypoint should be fired.
The question is, could the array be calculated for each second fast enough, each second should have random spacing.
Obviously, the event would need to be Asynchronous to avoid getting delayed by what ever is wired up to it.
Anyone come across any similar need?
UPDATE
I thought I'd at least put my initial effort on here.
So I found that there is enough resolution to check which millisecond you are currently in and which second. Basically, each second I rebuild my spacing each millisecond gets an entry in the array and each entry says how many times to fire the event that millisecond.
My issue is the spacing...I needs a better way to try and clump the event count, currently this jsut seems to plonk them in evenly spaced in either 0, 1 or 2.
public delegate void EmptyEventDelegate();
public class RandEvent
{
public event EmptyEventDelegate OnEvent = delegate { };
private bool running = false;
Random r = new Random();
private int eventsPS;
public RandEvent(int eventsPS = 1)
{
this.eventsPS = eventsPS;
}
public void Start()
{
running = true;
Task.Factory.StartNew(() =>
{
Run();
});
}
private void Run()
{
var sw = new Stopwatch();
sw.Start();
int currentSecond = 0;
int[] eventCount = BuildEventSpacing();
while(running)
{
if (currentSecond != sw.Elapsed.Seconds)
{
currentSecond = sw.Elapsed.Seconds;
eventCount = BuildEventSpacing();
}
else
{
for(int i = 0; i < eventCount[sw.Elapsed.Milliseconds]; i++)
OnEvent();
}
}
sw.Stop();
}
private int[] BuildEventSpacing()
{
var array = new int[1000];
for (int i = 0; i < eventsPS; i++)
{
array[r.Next(0, 999)]++;
}
return array;
}
public void Stop()
{
running = false;
}
}
To create the array of random event times:
Choose a minimum time interval between events, and fill an array with all possible event times. For example, if you choose a granularity of 1ms, then the array will have 1000 values: 0, 0.001, 0.002, ..., 0.999.
Each second, shuffle the array to randomize the order of the elements.
Use the first n elements of the array as the firing times for the event.
Create a Timer and in the Tick event do whatever needs to be done. Additionally, use the following method (in an additional handler of the Tick event) to change the interval each time. It would be best to provide the same Random instance is passed to the method every time rather than providing new instances.
private static double millisecondsPerSecond = 1000.0;
/// <summary>
/// Method used to determine how long you would wait for the event to fire next
/// </summary>
/// <param name="averageFiresPerSecond">The approximate number of times the event should occur per second.</param>
/// <param name="variance">How much variance should be allowed, as a percentage. i.e. a variance of 0.1 would mean that
/// the delay will be +/- 10% of the exact rate.</param>
/// <param name="generator">A randon number generator.</param>
/// <returns>The number of milliseconds to wait for the next event to fire.</returns>
public double GetNextDelay(int averageFiresPerSecond, double variance, Random generator)
{
double randomFactor = ((generator.NextDouble() * 2) * variance);
return (millisecondsPerSecond / averageFiresPerSecond) * randomFactor;
}
Let's say you want 100 numbers whose sum is 1. You can do the following:
Generate 100 random numbers between 0 and 1 (i.e. Random.NextDouble) and store in a list.
Sum the numbers.
Divide each number in the list by the sum.
You now have a list of 100 numbers that sum to 1.0.
Now, create a one-shot timer whose interval is the first value in the list. When it fires, the handler does its job and then sets up the one-shot timer again with an interval equal to the next value in the list.
Except that's not going to work perfectly because it doesn't take into account the amount of time required to process the tick and setup the timer again. So you'll have to keep track of that latency and adjust the next tick accordingly. But even that's not going to be perfect.
As I said in my comment, you're going to have a difficult time getting any of the .NET timers to give you more than 60 ticks per second. At best, you can expect 15 ms between timer ticks. Even if you go with a custom timer implementation, you're going to have some trouble because timer ticks don't occur exactly on time. The problem gets worse, of course, as the timer interval decreases.
In any event, there's no way that you can get a Windows timer to give you better than 1 ms resolution, so you'll have to adjust the method I described above to give you numbers that are greater than or equal to 0.001.
Your idea of using a Stopwatch can work with my method of creating the event intervals. Understand, however, that the loop is going to consume close to 100% of CPU time on a single core. Even then, it might not give you events exactly on time, because other higher-priority tasks might cause your loop to be swapped out.
I would suggest to make a collection of TimeSpan objects initialized to be equal and have a sum of 1 second. The exact values would be determined by your n.
From there, you can offset pairs or groups by whatever values you like and your total will still be one second. For example...
var offset = new TimeSpan.FromMilliseconds(10); // 10ms offset
timeSpans[0] += offset;
timeSpans[1] -= offset;
In this way you can shift them around without affecting the overall sum. If you need to make them so that more come early and fewer come late, then you could do something like...
timeSpans[0] -= offset;
timeSpans[1] -= offset;
timeSpans[2] += offset;
timeSpans[2] += offset;
This would make indexes 0 and 1 have a shorter delay and index 2's delay will be twice as long, but keep the sum total unaffected.
The only thing to keep in mind is that none of the time spans should be less than 0s and your sum total should always be 1s, then you're golden. Once you have your TimeSpan objects distributed, you can use them in order to pause between firing event x. You're going to want to vary (randomize?) your offset a few times and each time you change it, choose new timeSpans (randomly?) to apply the offset to. At the end of the process, it should be pretty chaotically spaced out.
I can provide a more detailed code sample if this is too abstract. :)
Hope this helps!
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
namespace TestProject1
{
[TestClass]
public class Test
{
[TestMethod]
public void TestSteps()
{
var random = new Random();
for (int i = 0; i < 20000; i++)
{
int numberOfEvents = random.Next(1,1000);
var generator = new RandomTimeStepGenerator(numberOfEvents);
var stopwatch = new Stopwatch();
stopwatch.Start();
var steps = generator.MillisecondDeltas;
Assert.AreEqual(numberOfEvents, steps.Count);
var sum = generator.MillisecondDeltas.Sum();
Assert.AreEqual(1000.0,sum,0.1);
Assert.IsTrue(stopwatch.ElapsedMilliseconds<10);
}
}
}
public class RandomTimeStepGenerator
{
private readonly int _numberOfEvents;
const int timeResolution = 10000;
public RandomTimeStepGenerator(int numberOfEvents)
{
_numberOfEvents = numberOfEvents;
}
public int NumberOfEvents
{
get { return _numberOfEvents; }
}
public List<double> MillisecondDeltas
{
get
{
var last=0;
var result = new List<double>();
var random = new Random();
for (var i = 0; i < timeResolution && result.Count < _numberOfEvents; i++)
{
var remainingEvents = _numberOfEvents - result.Count;
var remainingTime = timeResolution - i;
if(remainingEvents==1) // make sure the last event fires on the second
{
result.Add((timeResolution - last) / 10.0);
last = i;
}
else if (remainingTime <= remainingEvents) // hurry up and fire you lazy !!! your going to run out of time
{
result.Add(0.1);
}
else
{
double probability = remainingEvents / (double)remainingTime;
int next = random.Next(0,timeResolution);
if ((next*probability) > _numberOfEvents)
{
result.Add((i - last)/10.0);
last = i;
}
}
}
return result;
}
}
}
}