确定某些内容的剩余时间有什么好的算法?我知道有多少总行数,已经完成了多少行,我应该如何估算剩余时间?
答案 0 :(得分:56)
为什么不呢?
(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分钟:-)
答案 1 :(得分:27)
我很惊讶没有人用代码回答这个问题!
按照@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”,一个好的算法就是只考虑过去10秒的进度。查看ETACalculator.cs以了解此算法的实现!
ETACalculator.cs 来自 Progression - 我写的一个开源库。它为各种“进度计算”定义了一个非常易于使用的结构。它可以轻松实现报告不同类型进度的嵌套步骤。如果你担心感知表现(正如@JoshBerke所建议的那样),它会对你有很大的帮助。
答案 2 :(得分:16)
尽管所有进度条在测试中花费了相同的时间,但是两个特征使用户认为该过程更快,即使它不是:
- 顺利完成的进度条
- 进展到最后的进度条
醇>
答案 3 :(得分:11)
不要复活一个死的问题,但我一直回来参考这个页面 您可以在Stopwatch类上创建一个扩展方法,以获得可以获得估计的剩余时间跨度的功能。
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:
而不是子类化我创建了一个扩展方法。
答案 4 :(得分:3)
通常,您在处理过程中的任何时间点都知道三件事:
鉴于这些项目,剩余时间的估计(除非处理项目的时间不变)将
B * C / A
答案 5 :(得分:3)
我做了这个并且它工作得很好,随意根据您的变量类型或返回类型更改方法签名,可能您想获取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();
}
您需要在处理开始时初始化DateTime
变量,并在每次迭代时将其发送到方法。
不要忘记,如果进程很长,您的窗口可能会被锁定,因此当您将返回值放入控件时,不要忘记使用它的.Refresh()
方法。
如果您正在使用线程,那么您可以尝试使用Invoke(Action)
方法设置文本,更容易使用this extension method to archieve it。
如果您使用控制台应用程序,那么您不应该在逐行显示输出时遇到问题。
希望它有所帮助。
答案 6 :(得分:2)
这在很大程度上取决于“某事”是什么。如果您可以假设处理每一行的时间相似,则可以进行简单的计算:
TimePerLine = Elapsed / LinesProcessed
TotalTime = TimePerLine * TotalLines
TimeRemaining = TotalTime - LinesRemaining * TimePerLine
答案 7 :(得分:1)
我知道没有标准算法,我的消息是:
你可能已经看到了一个程序,其中加载条在一个点上比另一个点运行得快得多。嗯,这几乎是因为他们是这样做的。 (尽管它们可能只是在主包装器中定期增加增量)
答案 8 :(得分:1)
其中time$("ms")
表示自00: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)
答案 9 :(得分:0)
如果您知道完成的百分比,并且您可以简单地假设时间线性缩放,例如
timeLeft = timeSoFar *(1 /百分比)
可能有用。
答案 10 :(得分:0)
这实际上取决于正在做什么......除非每条线都花费相同的时间,否则线路是不够的。
最好的方法(如果你的线不相似)可能是查看代码的逻辑部分,找出每个部分平均花费多长时间,然后使用这些平均时间来估计进度。
答案 11 :(得分:0)
我已经知道完成百分比&amp;时间过去了,所以这对我有所帮助:
TimeElapsed *((100 - %完成)/%完成)= TimeRemaining
然后我每次更改完成后都会更新此值,从而为我提供不断变化的ETA。
答案 12 :(得分:0)
有两种显示时间的方法
已过去的时间和剩余时间: 所以过去会增加,但剩下的可能是稳定的总时间(如果每秒稳定)
时间过去和剩余时间:
所以剩下的时间=所需的总数 - 经过时间
我的想法/公式更像是这样:
已处理 - 从正在运行的线程从0更新为总计
我有计时器,间隔为1000毫秒,计算每秒处理次数:
processedPerSecond = Processed - lastTickProcessed;
lastTickProcessed = Processed; //store state from past call
processedPerSecond和lastTickProcessed是计时器方法
之外的全局变量现在,如果我们想获得完成处理所需的秒数(理想的常数假设) totalSecondsNeeded = TotalLines / PerSecond
但我们想要展示案例2. TimeLeft如此 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个
我希望我的“跳跃”思想会帮助某人建立令人满意的东西
答案 13 :(得分:0)
这个怎么样....
我用它来浏览一组记录(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")
答案 14 :(得分:0)
PowerShell函数
function CalculateEta([datetime]$processStarted, [long]$totalElements, [long]$processedElements) {
$itemsPerSecond = $processedElements / [DateTime]::Now.Subtract($processStarted).TotalSeconds
$secondsRemaining = ($totalElements - $processedElements) / $itemsPerSecond
return [TimeSpan]::FromSeconds($secondsRemaining)
}
答案 15 :(得分:0)
我更喜欢 System.Threading.Timer ,而不是 System.Diagnostics.Stopwatch 。
System.Threading.Timer,该方法在线程上执行单个回调方法 线程池线程
以下代码是使用 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;
}
顺便说一句,如果您要模拟和虚拟化单元测试的日期时间,则可以使用System.Reactive.Concurrency.IScheduler(scheduler.Now.UtcDateTime)代替直接使用DateTime。
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;
}
答案 16 :(得分:0)
在 Python 中:
首先创建一个包含每个条目所用时间的数组,然后计算剩余元素并计算平均所用时间
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()
带有 k=5000
个随机元素和 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