我有2个值表,想要缩放第一个值,以便尽可能地匹配第二个值。两者都有相同的长度。如果两者都在图中绘制为图形,则它们应尽可能彼此接近。但我不想要二次,而是简单的线性权重。 我的问题是,由于Abs功能,我不知道如何实际计算最佳比例因子。
一些伪代码:
//given:
float[] table1= ...;
float[] table2= ...;
//wanted:
float factor= ???; // I have no idea how to compute this
float remainingDifference=0;
for(int i=0; i<length; i++)
{
float scaledValue=table1[i] * factor;
//Sum up the differences. I use the Abs function because negative differences are differences too.
remainingDifference += Abs(scaledValue - table2[i]);
}
我想计算缩放因子,以便剩余的差异最小。
答案 0 :(得分:3)
简单的线性权重很难像你说的那样。
a_n = first sequence
b_n = second sequence
c = scaling factor
你的剩余函数是(总和是从i = 1到N,点数):
SUM( |a_i - c*b_i| )
取c相对于c的导数:
d/dc SUM( |a_i - c*b_i| )
= SUM( b_i * (a_i - c*b_i)/|a_i - c*b_i| )
设置为0
并解决c
很难。我认为没有一种分析方法可以做到这一点。您可能需要尝试https://math.stackexchange.com/,看看他们是否有任何好主意。
但是,如果使用二次权重,它会变得非常简单:
d/dc SUM( (a_i - c*b_i)^2 )
= SUM( 2*(a_i - c*b_i)* -c )
= -2c * SUM( a_i - c*b_i ) = 0
=> SUM(a_i) - c*SUM(b_i) = 0
=> c = SUM(a_i) / SUM(b_i)
如果可以,我强烈建议使用后一种方法。
答案 1 :(得分:1)
我建议在Newton Raphson上尝试某种变体。
构造一个函数Diff(k),用于查看固定标记A和B之间两个图形之间的区域差异。
在数学上我猜它会是积分的(x = A到B){f(x) - k * g(x)} dx
无论如何,实际上你可以减去这些值,
就好像你的范围从X = -10到10,并且你在[-10,10]中的每个整数i上有f(i)和g(i)的数据点(即21个数据点)
那么你只需求和(i = -10到10){f(i) - k * g(i)}
基本上你会期望这个函数看起来像抛物线 - 会有一个最优的k,并且在任一方向上略微偏离它会增加整体面积差异
并且差异越大,你会发现差距越大
所以,这应该是一个非常流畅的功能(如果你有很多数据点)
所以你想最小化Diff(k)
所以你想找出是否导数,即d / dk Diff(k)= 0
所以就这个新功能D'(k)
做Newton Raphson在k = 1时启动它,并且应该快速解决一个解决方案
这可能会给你一个最佳的计算时间
如果你想要一些更简单的东西,只需从0左右的一些k1和k2开始
所以说Diff(1.5)= -3和Diff(2.9)= 7
那么你会选择一个k表示3/10(10 = 7 - -3)在1.5和2.9之间
并且取决于是否产生正值或负值,将其用作新的k1或k2,冲洗并重复
答案 2 :(得分:0)
如果将来有人偶然发现这个问题,这里有一些代码(c ++) 诀窍是首先按比例因子对样本进行排序,这将导致每个样本最适合2个样本。然后从两端开始迭代到导致最小绝对偏差的因子(L1范数)。
除了排序之外的所有内容都有一个线性运行时间=&gt;运行时为O(n * log n)
/*
* Find x so that the sum over std::abs(pA[i]-pB[i]*x) from i=0 to (n-1) is minimal
* Then return x
*/
float linearFit(const float* pA, const float* pB, int n)
{
/*
* Algebraic solution is not possible for the general case
* => iterative algorithm
*/
if (n < 0)
throw "linearFit has invalid argument: expected n >= 0";
if (n == 0)
return 0;//If there is nothing to fit, any factor is a perfect fit (sum is always 0)
if (n == 1)
return pA[0] / pB[0];//return x so that pA[0] = pB[0]*x
//If you don't like this , use a std::vector :P
std::unique_ptr<float[]> targetValues_(new float[n]);
std::unique_ptr<int[]> indices_(new int[n]);
//Get proper pointers:
float* targetValues = targetValues_.get();//The value for x that would cause pA[i] = pB[i]*x
int* indices = indices_.get(); //Indices of useful (not nan and not infinity) target values
//The code above guarantees n > 1, so it is safe to get these pointers:
int m = 0;//Number of useful target values
for (int i = 0; i < n; i++)
{
float a = pA[i];
float b = pB[i];
float targetValue = a / b;
targetValues[i] = targetValue;
if (std::isfinite(targetValue))
{
indices[m++] = i;
}
}
if (m <= 0)
return 0;
if (m == 1)
return targetValues[indices[0]];//If there is only one target value, then it has to be the best one.
//sort the indices by target value
std::sort(indices, indices + m, [&](int ia, int ib){
return targetValues[ia] < targetValues[ib];
});
//Start from the extremes and meet at the optimal solution somewhere in the middle:
int l = 0;
int r = m - 1;
// m >= 2 is guaranteed => l > r
float penaltyFactorL = std::abs(pB[indices[l]]);
float penaltyFactorR = std::abs(pB[indices[r]]);
while (l < r)
{
if (l == r - 1 && penaltyFactorL == penaltyFactorR)
{
break;
}
if (penaltyFactorL < penaltyFactorR)
{
l++;
if (l < r)
{
penaltyFactorL += std::abs(pB[indices[l]]);
}
}
else
{
r--;
if (l < r)
{
penaltyFactorR += std::abs(pB[indices[r]]);
}
}
}
//return the best target value
if (l == r)
return targetValues[indices[l]];
else
return (targetValues[indices[l]] + targetValues[indices[r]])*0.5;
}