> 但是我的任务是,每当新值到达时都重新计算平均值,并重新加权旧值。 –OP
我想实现一种迭代算法,该算法计算加权平均值。具体的权重定律无关紧要,但是对于最新的值,它应该接近1,对于最早的值,它应该接近0。
该算法应该是迭代的。即它不应该记住所有先前的值。它应该只知道一个最新值以及有关过去的任何汇总信息,例如平均值,总和,计数等的先前值。
有可能吗?
例如,以下算法可以是:
void iterate(double value) {
sum *= 0.99;
sum += value;
count++;
avg = sum / count;
}
它将使指数权重下降,这可能不好。有可能会逐步减轻体重吗?
编辑1
称重法的要求如下:
1)重量减少到过去2)我有一些平均或特征持续时间,因此,这个持续时间长的值比新持续时间的值要小得多3)我应该可以设置此持续时间
编辑2
我需要以下内容。假设v_i
是值,其中v_1
是第一个。还假设w_i
是权重。但是w_0
是最后一个。
所以,在第一个值出现之后,我有了第一个平均值
a_1 = v_1 * w_0
[第二个值v_2到来之后,我应该具有平均值
a_2 = v_1 * w_1 + v_2 * w_0
我应该拥有下一个值
a_3 = v_1 * w_2 + v_2 * w_1 + v_3 * w_0
注意,当我沿着值序列移动时,体重曲线正在跟着我移动。
即每个值始终没有自己的权重。我的目标是减少过去的体重。
[先介绍一下背景。如果我们保持一个正常的平均值,它将像这样:
average(a) = 11
average(a,b) = (average(a)+b)/2
average(a,b,c) = (average(a,b)*2 + c)/3
average(a,b,c,d) = (average(a,b,c)*3 + d)/4
您可以在这里看到,这是一个“在线”算法,我们只需要跟踪数据片段:1)平均值中的总数,以及2)平均值本身。然后我们可以将平均值除以总数,加上新的数字,然后除以新的总数。
加权平均值略有不同。这取决于哪种加权平均值。例如,如果您定义:
weightedAverage(a,wa, b,wb, c,wc, ..., z,wz) = a*wa + b*wb + c*wc + ... + w*wz
or
weightedAverage(elements, weights) = elements·weights
...然后,除了添加新元素*权重,您无需执行任何其他操作!但是,如果您定义的加权平均值类似于概率的期望值:
weightedAverage(elements,weights) = elements·weights / sum(weights)
...然后,您需要跟踪总重量。而不是除以元素总数,您除以总数weight,添加新元素* weight,然后除以新的总数weight] >>。 或者,您也不需要进行除法,如下所示:您可以仅跟踪封闭点或对象中的临时点积和总重量,并在屈服时对其进行除法(这有助于避免数值复合舍入误差引起的不准确)。
在python中为:
def makeAverager(): dotProduct = 0 totalWeight = 0 def averager(newValue, weight): nonlocal dotProduct,totalWeight dotProduct += newValue*weight totalWeight += weight return dotProduct/totalWeight return averager
演示:
>>> averager = makeAverager() >>> [averager(value,w) for value,w in [(100,0.2), (50,0.5), (100,0.1)]] [100.0, 64.28571428571429, 68.75] >>> averager(10,1.1) 34.73684210526316 >>> averager(10,1.1) 25.666666666666668 >>> averager(30,2.0) 27.4
> 但是我的任务是,每当新值到达时都重新计算平均值,并重新加权旧值。 –OP
即使使用非常简单的加权方案,您的任务几乎总是不可能的。
您正在使用O(1)存储器,通过变化的加权方案获得平均值。例如,对于一些几乎任意更改的权重序列,将{values·weights1
,(values+[newValue2])·weights2
,(values+[newValue2,newValue3])·weights3
,...}作为新值传入。由于注入性,这是不可能的。将数字合并在一起后,您将丢失大量信息。例如,即使具有权重向量
[8,4,2,1]
,并添加一个具有任意权重的新元素,例如...+[1]
,但是必须将所有先前的元素增加same乘积因子,例如[16,8,4,2]+[1]
。因此,在每个步骤中,您都将添加新的任意权重以及过去的新的任意缩放比例,因此您具有2个自由度(如果需要保持点积归一化,则只有1个自由度)。您将获得的权重向量如下所示:[w0] [w0*(s1), w1] [w0*(s1*s2), w1*(s2), w2] [w0*(s1*s2*s3), w1*(s2*s3), w2*(s3), w3] ...
因此,您可以使任何看起来像这样的加权方案都可以工作(除非您需要通过权重之和对事物进行归一化,在这种情况下,您必须将新的平均值除以新的总和,然后通过保持仅O(1)内存)。仅将先前的平均值乘以新的
s
(这将隐式地将点积分布到权重中),然后对新的+w*newValue
进行固定。
我认为您正在寻找这样的东西:
void iterate(double value) {
count++;
weight = max(0, 1 - (count / 1000));
avg = ( avg * total_weight * (count - 1) + weight * value) / (total_weight * (count - 1) + weight)
total_weight += weight;
}
在这里,我假设您希望权重之和为1。只要您可以在将来不改变权重的情况下生成相对权重,就可以得到一个模仿此行为的解决方案。
即,假设您将权重定义为序列{s_0, s_1, s_2, ..., s_n, ...}
,并将输入定义为序列{i_0, i_1, i_2, ..., i_n}
。
请考虑以下形式:sum(s_0*i_0 + s_1*i_1 + s_2*i_2 + ... + s_n*i_n) / sum(s_0 + s_1 + s_2 + ... + s_n)
。请注意,可以使用几个聚合计数器来逐步计算此值:
int counter = 0; double numerator = 0; double denominator = 0; void addValue(double val) { double weight = calculateWeightFromCounter(counter); numerator += weight * val; denominator += weight; } double getAverage() { if (denominator == 0.0) return 0.0; return numerator / denominator; }
当然,在这种情况下,calculateWeightFromCounter()不应生成总和为1的权重—此处的窍门是,我们通过除以权重之和来求平均值,以便最终,权重实际上似乎等于一个。
真正的窍门是您如何进行CalculationWeightFromCounter()。例如,您可以简单地返回计数器本身,但是请注意,最后的加权数不一定会接近计数器的总和,因此您可能无法获得所需的确切属性。 (很难说,因为如上所述,您留下了一个相当开放的问题。)
此时间太长,无法在评论中发布,但可能会有用。
假设您有:w_0*v_n + ... w_n*v_0
(以下简称为w[0..n]*v[n..0]
)
然后下一步是:w_0*v_n1 + ... w_n1*v_0
(简称为w[0..n1]*v[n1..0]
)
这意味着我们需要一种从w[1..n1]*v[n..0]
来计算w[0..n]*v[n..0]
的方法。
[z在某个位置x处,v[n..0]
肯定是0, ..., 0, z, 0, ..., 0
是可能的。
如果没有任何“额外”存储,则f(z*w(x))=z*w(x + 1)
,其中w(x)
是位置x的权重。
重新排列方程式,w(x + 1) = f(z*w(x))/z
。好吧,w(x + 1)
对于常数x最好是常数,所以f(z*w(x))/z
最好是常数。因此,f
必须让z
传播-即f(z*w(x)) = z*f(w(x))
。
但是这里又有一个问题。请注意,如果z
(可以是任何数字)可以通过f
传播,那么w(x)
当然可以。所以f(z*w(x)) = w(x)*f(z)
。因此f(w(x)) = w(x)/f(z)
。但是对于常数x
,w(x)
是常数,因此f(w(x))
也最好是常数。 w(x)
是常数,所以f(z)
最好是常数,以便w(x)/f(z)
是常数。因此,f(w(x)) = w(x)/c
其中c
是常数。
因此,f(x)=c*x
其中c
是常数,当x
是权重值时。
所以w(x+1) = c*w(x)
。
即,每个权重是前一个的倍数。因此,权重的形式为w(x)=m*b^x
。
注意,这假设f
仅有的信息是最后的合计值。请注意,除非您愿意存储代表输入的非恒定数据量,否则有时会遇到这种情况。您不能用实数表示实数的无限长度向量,但是可以以一定的固定数量有限地近似估计它们。但这仅仅是一个近似值。
尽管我还没有严格地证明这一点,但我的结论是,您想要的东西不可能高精度地完成,但是您可以使用log(n)空间(也可以是O( 1)对于许多实际应用)生成质量近似值。您也许可以使用更少的东西。
我尝试用Java编写几乎所有的代码。如前所述,您的目标是无法实现的。您只能从一定数量的上次记忆值中计算平均值。如果不需要精确,则可以近似使用较旧的值。我尝试通过准确地记住最后5个值,而仅记住较旧的值仅对5个值求和,并记住最后5个SUM来做到这一点。那么,用于记住最后的n + n * n个值的复杂度为O(2n)。这是一个非常粗略的近似值。
您可以根据需要修改“ lastValues”和“ lasAggregatedSums”数组大小。看到这张试图显示最后一个值的图形的ascii-art图片,它显示了第一列(较旧的数据)被记住为合计值(不是单独地),并且仅记住了最早的5个值。
values: ##### ##### ##### # ##### ##### ##### # # ##### ##### ##### ##### ## ## ##### ##### ##### ##### ##### ##### time: --->
挑战1
:我的示例不计算权重,但我认为适当地为“ lastAggregatedSums”添加权重应该不成问题-唯一的问题是,如果您希望降低权重值越大,则难度就越大,因为数组正在旋转,因此要知道哪个数组成员的权重并不是一件容易的事。也许您可以修改算法以始终在数组中“移动”值而不是旋转?然后增加权重应该不是问题。
挑战2:数组使用0值初始化,并且即使我们没有收到足够的值,这些值也从一开始就计入平均值。如果您长时间运行该算法,则可能不必担心它在一开始就学习了一段时间。如果这样做,您可以发布修改;-)
public class AverageCounter {
private float[] lastValues = new float[5];
private float[] lastAggregatedSums = new float[5];
private int valIdx = 0;
private int aggValIdx = 0;
private float avg;
public void add(float value) {
lastValues[valIdx++] = value;
if(valIdx == lastValues.length) {
// count average of last values and save into the aggregated array.
float sum = 0;
for(float v: lastValues) {sum += v;}
lastAggregatedSums[aggValIdx++] = sum;
if(aggValIdx >= lastAggregatedSums.length) {
// rotate aggregated values index
aggValIdx = 0;
}
valIdx = 0;
}
float sum = 0;
for(float v: lastValues) {sum += v;}
for(float v: lastAggregatedSums) {sum += v;}
avg = sum / (lastValues.length + lastAggregatedSums.length * lastValues.length);
}
public float getAvg() {
return avg;
}
}
您可以将(加权总和)指数均值与不同的有效窗口大小(N)结合起来,以获得所需的权重。使用更多的指数方法来更详细地定义您的体重曲线。(更多指数手段还意味着存储和计算更多值,因此这是一个折衷方案)
> 但是我的任务是,每当新值到达时都重新计算平均值,并重新加权旧值。 –OP
我认为您正在寻找这样的东西:
在这里,我假设您希望权重之和为1。只要您可以在将来不改变权重的情况下生成相对权重,就可以得到一个模仿此行为的解决方案。
此时间太长,无法在评论中发布,但可能会有用。
我尝试用Java编写几乎所有的代码。如前所述,您的目标是无法实现的。您只能从一定数量的上次记忆值中计算平均值。如果不需要精确,则可以近似使用较旧的值。我尝试通过准确地记住最后5个值,而仅记住较旧的值仅对5个值求和,并记住最后5个SUM来做到这一点。那么,用于记住最后的n + n * n个值的复杂度为O(2n)。这是一个非常粗略的近似值。
您可以将(加权总和)指数均值与不同的有效窗口大小(N)结合起来,以获得所需的权重。使用更多的指数方法来更详细地定义您的体重曲线。(更多指数手段还意味着存储和计算更多值,因此这是一个折衷方案)