计算剩余时间

时间:2009-01-23 15:43:04

标签: c# algorithm

确定某些内容的剩余时间有什么好的算法?我知道有多少总行数,已经完成了多少行,我应该如何估算剩余时间?

17 个答案:

答案 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)

确保管理perceived performance

  

尽管所有进度条在测试中花费了相同的时间,但是两个特征使用户认为该过程更快,即使它不是:

     
      
  1. 顺利完成的进度条
  2.   
  3. 进展到最后的进度条
  4.   

答案 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)

通常,您在处理过程中的任何时间点都知道三件事:

  1. 到目前为止(A)处理了多少单位/块/项目。
  2. 处理这些项目需要多长时间(B)。
  3. 剩余项目数(C)。
  4. 鉴于这些项目,剩余时间的估计(除非处理项目的时间不变)将

    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)

有两种显示时间的方法

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

  2. 时间过去和剩余时间:
    所以剩下的时间=所需的总数 - 经过时间

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

    已处理 - 从正在运行的线程从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