计算剩余时间

什么是一个好的algorithm来确定剩下的时间来完成一些事情? 我知道总共有多less条线路,已经有多less条线路了,我应该如何估算剩余的时间?

为什么不?

(linesProcessed / TimeTaken) (timetaken / linesProcessed) * LinesLeft = TimeLeft

TimeLeft将随后以任何时间timeTaken表示。

编辑:

感谢您的评论,你应该这样做:

(TimeTaken / linesProcessed) * linesLeft = timeLeft

所以我们有

(10 / 100) * 200 = 20秒现在10秒过去了
(20 / 100) * 200 = 40秒现在剩下10秒,我们处理100多行
(30 / 200) * 100 = 15秒,现在我们都明白为什么复制文件对话框从3小时跳到30分钟:-)

我很惊讶没有人用代码回答这个问题!

计算时间的简单方法,如@JoshBerke回答,可以编码如下:

 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 ... } 

这个简单的例子很适合简单的进度计算。
但是,对于一个更复杂的任务,这个计算有很多方法可以改进!

例如,当您下载一个大文件时,下载速度很容易波动。 要计算最准确的“ETA”,一个好的algorithm是只考虑过去10秒的进度。 查看ETACalculator.cs以获得该algorithm的实现!

ETACalculator.cs来自Progression – 我写的一个开源库。 它为各种“进度计算”定义了一个非常易于使用的结构。 这使得报告不同types进度的嵌套步骤变得很容易。 如果你关心感知的performance(就像@JoshBerkebuild议的那样),它将会非常有帮助。

确保pipe理感知的性能 。

尽pipe所有的进度条在testing中花费的时间完全相同,但有两个特点使得用户认为过程更快,即使不是:

  1. 进度栏顺利完成
  2. 进度条加快到最后

不要复活一个死的问题,但我不断回来引用这个页面。
您可以在Stopwatch类上创build一个扩展方法,以获得估计剩余时间跨度的function。

 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; } } 

例:

 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()); } 

希望对某人有用处。

编辑:应该指出,这是最准确的时候,每个循环需要相同的时间。
编辑2:而不是inheritance我创build了一个扩展方法。

一般来说,在处理的任何时候你都知道三件事:

  1. (A)已经处理了多less个单位/块/物品。
  2. (B)处理这些项目花了多长时间。
  3. 剩余项目数(C)。

给定这些项目,剩余时间的估计 (除非处理项目的时间是恒定的)

B * C / A

我做了这个,它工作的很好,随意改变方法签名根据你的variablestypes或也返回types,可能你想获得TimeSpan对象或只是秒…

  /// <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(); } 

您将需要在处理开始时初始化DateTimevariables,并在每次迭代中将其发送给方法。

不要忘记,如果进程很长,可能窗口会被锁住,所以当你把返回值放到一个控件中时,不要忘记使用它的.Refresh()方法。

如果你正在使用线程,那么你可以尝试使用Invoke(Action)方法来设置文本,使用这个扩展方法很容易实现。

如果您使用控制台应用程序,那么您应该没有问题逐行显示输出。

希望它可以帮助别人。

这很大程度上取决于“东西”是什么。 如果您可以假定每行处理的时间量相似,则可以进行简单的计算:

 TimePerLine = Elapsed / LinesProcessed TotalTime = TimePerLine * TotalLines TimeRemaining = TotalTime - LinesRemaining * TimePerLine 

没有我知道的标准algorithm,我的消化会是:

  • 创build一个variables来保存%
  • 计算你想要跟踪的任务的复杂性(或估计它)
  • 根据复杂程度的不同,将时间增加到%。

您可能已经看到负载栏在一个点上比另一个点快得多的程序。 那么这很重要,因为他们是这样做的。 (尽pipe他们可能只是在主包装中定期增加)

其中time$("ms")表示从00:00:00开始的当前时间(以毫秒为单位), lof表示要处理的总行数, x表示当前行:

 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) 

这真的取决于正在做什么……线路是不够的,除非每条线路都花费相同的时间。

最好的方法(如果你的线不相似)可能会看代码的逻辑部分,找出每个部分平均需要多长时间,然后使用这些平均时间来估计进度。

如果你知道完成的百分比,你可以简单地假设时间线性地变化,类似

timeLeft = timeSoFar *(1 / Percentage)

可能会工作。

我已经知道完成百分比和时间,所以这帮助了我:

TimeElapsed *((100 – %完成)/%完成)= TimeRemaining

然后我每次更新完这个数值,完成改变,给我一个不断变化的ETA。

有两种performance时间的方式

  1. 经过的时间和剩余的时间:经过的时间会增加,但剩下的时间可能是稳定的总时间(如果每秒稳定)

  2. 经过的时间和剩余的时间:
    所以剩下的时间=总需要 – 已经过去了

我的想法/公式更像这样:

已处理 – 从运行的线程从0更新为Total

我有1000毫秒计时间隔计算每秒处理:

 processedPerSecond = Processed - lastTickProcessed; lastTickProcessed = Processed; //store state from past call 

processedPerSecond和lastTickProcessed是超出定时器方法的全局variables

现在如果我们想要得到完成处理需要多less秒钟(在理想常数假设下)totalSecondsNeeded = TotalLines / PerSecond

但我们要显示案例2. TimeLeft so TimeLeftSeconds =(TotalLines – Processed)/ PerSecond

 TimeSpan remaining = new TimeSpan(0, 0, (transactions.Count - Processed) / processedPerSecond); labelTimeRemaining.Text = remaining.ToString(@"hh\:mm\:ss"); 

当然,如果PerSecond跳跃,TimeLeftSeconds将会“跳跃”,所以如果过去的PerSecond是10,然后是30,然后回到10,用户就会看到它。

有一种方法可以计算平均值,但是如果进程加速到最后,这可能不会显示实时剩余

 int perSecond = (int)Math.Ceiling((processed / (decimal)timeElapsed.TotalSeconds)); //average not in past second 

因此,开发人员可以根据预测处理“跳跃”的程度来“挑选”一种最准确的方法

我们也可以计算并保存每个PerSecond,然后取最后10秒并取平均值,但在这种情况下,用户将不得不等待10秒才能看到第一个计算,或者我们可以显示从第一个每秒开始的剩余时间,然后逐步平均地总结到10个最后的每秒

我希望我的“跳跃”思想能帮助别人创造令人满意的东西

这个怎么样….

我用它来遍历一组logging(在一个Excel文件中的行,在一个例子中)

L是当前行号X是例程开始时,dat_Start被设置为Now()的总行数

 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")