数学问题:缩放图形以使其与另一个图形匹配

时间:2011-07-22 12:58:10

标签: math

我有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]);
}

我想计算缩放因子,以便剩余的差异最小。

3 个答案:

答案 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;
}