当然,您可以将剩余文件大小除以当前下载速度,但如果您的下载速度波动(并且会波动),这不会产生非常好的结果。产生更平滑倒计时的更好算法是什么?
6 回答
指数移动平均线对此非常有用。它提供了一种平滑平均值的方法,这样每次添加新样本时,旧样本对整体平均值的重要性就会降低。它们仍然被考虑,但它们的重要性呈指数下降——因此得名。而且由于它是一个“移动”平均值,因此您只需要保留一个数字即可。
在测量下载速度的情况下,公式如下所示:
averageSpeed = SMOOTHING_FACTOR * lastSpeed + (1-SMOOTHING_FACTOR) * averageSpeed;
SMOOTHING_FACTOR
是一个介于 0 和 1 之间的数字。这个数字越大,丢弃旧样本的速度就越快。正如您在公式中看到的,when SMOOTHING_FACTOR
is 1 您只是使用上次观察的值。何时SMOOTHING_FACTOR
为 0averageSpeed
永远不会改变。因此,您需要介于两者之间的东西,并且通常需要较低的值才能获得不错的平滑度。我发现 0.005 为平均下载速度提供了一个相当不错的平滑值。
lastSpeed
是最后测量的下载速度。您可以通过每秒运行一个计时器来计算自上次运行以来下载了多少字节来获取此值。
averageSpeed
显然,是您要用来计算估计剩余时间的数字。将此初始化为lastSpeed
您获得的第一个测量值。
几年前,我编写了一个算法来预测磁盘映像和多播程序中的剩余时间,当当前吞吐量超出预定义范围时,该程序使用带有重置的移动平均值。除非发生剧烈变化,否则它将保持平稳,然后会迅速调整,然后再次回到移动平均线。请参阅此处的示例图表:
该示例图表中的粗蓝线是一段时间内的实际吞吐量。请注意传输前半部分的低吞吐量,然后在后半部分急剧上升。橙色线是总体平均值。请注意,它永远不会调整得足够远,无法准确预测需要多长时间才能完成。灰线是移动平均值(即最后 N 个数据点的平均值 - 在此图中 N 为 5,但实际上,N 可能需要更大才能足够平滑)。它恢复得更快,但仍需要一段时间来调整。N越大,时间越长。因此,如果您的数据非常嘈杂,则 N 必须更大,恢复时间会更长。
绿线是我使用的算法。它就像移动平均线一样,但是当数据超出预定义的范围(由浅蓝色和黄色细线指定)时,它会重置移动平均线并立即向上跳跃。预定义的范围也可以基于标准偏差,因此它可以自动调整数据的噪声程度。我只是将这些值放入 Excel 中,为这个答案绘制图表,所以它并不完美,但你明白了。
但是,可以设计数据使该算法无法很好地预测剩余时间。最重要的是,您需要大致了解您期望数据的行为方式并相应地选择算法。我的算法在我看到的数据集上运行良好,所以我们继续使用它。
另一个重要提示是,开发人员通常会在进度条和时间估计计算中忽略设置和拆卸时间。这会导致 99% 或 100% 的进度条一直停留在那里很长时间(当缓存被刷新或其他清理工作正在进行时)或在扫描目录或其他设置工作时进行疯狂的早期估计,累积时间但没有累积任何百分比进度,这会使一切都失败。您可以运行多个测试,包括设置和拆卸时间,并根据作业的大小估算这些时间的平均时间长度,并将该时间添加到进度条中。例如,前 5% 的工作是设置工作,最后 10% 是拆卸工作,然后中间的 85% 是下载或任何您跟踪的重复过程。
speed=speedNow*0.5+speedLastHalfMinute*0.3+speedLastMinute*0.2
我认为你能做的最好的就是将剩余的文件大小除以平均下载速度(到目前为止下载的速度除以你下载的时间)。开始时这会有点波动,但下载的时间越长,它就会越来越稳定。
我发现 Ben Dolman 的回答非常有帮助,但对于像我这样不太喜欢数学的人来说,我仍然需要大约一个小时才能将其完全实现到我的代码中。这是在python中说同样事情的一种更简单的方法,如果有任何不准确之处请告诉我,但在我的测试中效果很好:
def exponential_moving_average(data, samples=0, smoothing=0.02):
'''
data: an array of all values.
samples: how many previous data samples are avraged. Set to 0 to average all data points.
smoothing: a value between 0-1, 1 being a linear average (no falloff).
'''
if len(data) == 1:
return data[0]
if samples == 0 or samples > len(data):
samples = len(data)
average = sum(data[-samples:]) / samples
last_speed = data[-1]
return (smoothing * last_speed) + ((1 - smoothing) * average)
input_data = [4.5, 8.21, 8.7, 5.8, 3.8, 2.7, 2.5, 7.1, 9.3, 2.1, 3.1, 9.7, 5.1, 6.1, 9.1, 5.0, 1.6, 6.7, 5.5, 3.2] # this would be a constant stream of download speeds as you go, pre-defined here for illustration
data = []
ema_data = []
for sample in input_data:
data.append(sample)
average_value = exponential_moving_average(data)
ema_data.append(average_value)
# print it out for visualization
for i in range(len(data)):
print("REAL: ", data[i])
print("EMA: ", ema_data[i])
print("--")
作为 Ben Dolman 答案的扩展,您还可以计算算法中的波动。它会更平滑,但它也会预测平均速度。
像这样的东西:
prediction = 50;
depencySpeed = 200;
stableFactor = .5;
smoothFactor = median(0, abs(lastSpeed - averageSpeed), depencySpeed);
smoothFactor /= (depencySpeed - prediction * (smoothFactor / depencySpeed));
smoothFactor = smoothFactor * (1 - stableFactor) + stableFactor;
averageSpeed = smoothFactor * lastSpeed + (1 - smoothFactor) * averageSpeed;
波动与否,它将与另一个一样稳定,具有正确的预测和依赖速度值;您必须根据您的互联网速度玩一点。此设置非常适合 600 kB/s 的平均速度,而它在 0 到 1MB 之间波动。