计算几个函数的平均函数

时间:2011-02-18 14:51:15

标签: c# algorithm

我有几个有序的X / Y对列表,我想计算一个有序的X / Y对列表,代表这些列表的平均值。

所有这些列表(包括“平均列表”)将被绘制到图表上(参见下面的示例图片)。

我有几个问题:

  1. 不同的列表没有相同数量的值
  2. X和Y值可以增加,减少和增加(等等)(参见下面的示例图片)
  3. 我需要在C#中实现这一点,我想这对算法本身并不重要。

    Example of Lines

    抱歉,我无法以更正式或数学的方式解释我的问题。

    编辑:我用“X / Y对列表”替换了“功能”这个术语,这不那么令人困惑。

5 个答案:

答案 0 :(得分:4)

我将使用你的功能的比喻,即汽车在弯曲的赛道上奔跑,在那里你想要根据汽车的位置提取赛道的中心线。每辆车的位置都可以描述为时间的函数:

p1(t) = (x1(t), y1(t))
p2(t) = (x2(t), y2(t))
p3(t) = (x3(t), y3(t))

关键问题是赛车以不同的速度赛车,这意味着p1(10)可能是赛道上p2(10)的两倍。{{1}}。如果你对这两个点采取了天真的平均值,并且赛车之间的轨道有一条尖锐的曲线,那么平均值可能远离赛道。

如果你只是转换你的功能不再是时间的函数,而是沿着轨道的距离的函数,那么你就可以做到你想要什么。

您可以这样做的一种方法是选择最慢的汽车(即具有最多样本数量的汽车)。然后,对于最慢车辆位置的每个样本,查看所有其他车辆的路径,找到两个最近的点,并选择插值线上最接近最慢车辆位置的点。然后将这些点平均在一起。对所有慢车样本执行此操作后,您将获得平均路径。

我假设所有的汽车都在大致相同的地方开始和结束;如果任何一辆赛车只占赛道的一小部分,你需要增加一些逻辑来检测它。


可能的改进(性能和准确性)是跟踪每辆车使用的最新样本和每辆车的速度(相对采样率)。对于你最慢的车,这将是一个简单的地图:1 => 1,2 => 2,3 => 3,......对于其他车型,它可能更像是:1 => 0.3,2 => 0.7,3 => 1.6(小数值是由插值引起的)。速度将是样本数量变化的倒数(例如,慢车将具有速度1,而另一辆车具有速度1 /(1.6-0.7)= 1.11)。然后,您可以确保不会意外地回溯任何车辆。您还可以提高计算速度,因为您不必搜索每条路径上的所有点的整个集合;相反,您可以假设下一个样本将接近当前样本加上1 /速度。

答案 1 :(得分:4)

我会使用贾斯汀提出的方法,只需一次调整。他建议使用带有小数指数的映射表,但我建议使用整数指数。这可能听起来有点数学,但是必须阅读以下两次并不是羞耻(我也必须这样做)。假设对A列表中的索引i处的点已搜索另一列表B中的最近点,并且该最近点位于索引j处。要找到B中最接近A [i + 1]的点,您应该只考虑B中指数等于或大于j的点。它可能是j + 1,但可能是j或j + 2,j + 3等,但从不低于j。即使最接近A [i + 1]的点的索引小于j,您仍然不应该使用该点进行插值,因为这会导致意外的平均值和图形。我现在花点时间为您创建一些示例代码。我希望你看到这种优化是有道理的。

编辑:在实现这一点时,我意识到j不仅从下面(通过上述方法)限制,而且还从上面限定。当你尝试从A [i + 1]到B [j],B [j + 1],B [j + 2]等的距离时,你可以停止比较距离A [i + 1]到B [j + ...]停止减少。在B中进一步搜索是没有意义的。同样的推理也适用于j从下面开始的界限:即使B中其他地方的某些点更接近,那可能不是你想要插入的点。这样做会导致意外的图表,可能不如您预期的那么顺畅。而第二个限制的额外奖励是提高了性能。我创建了以下代码:

IEnumerable<Tuple<double, double>> Average(List<Tuple<double, double>> A, List<Tuple<double, double>> B)
{
    if (A == null || B == null || A.Any(p => p == null) || B.Any(p => p == null)) throw new ArgumentException();
    Func<double, double> square = d => d * d;//squares its argument
    Func<int, int, double> euclidianDistance = (a, b) => Math.Sqrt(square(A[a].Item1 - B[b].Item1) + square(A[a].Item2 - B[b].Item2));//computes the distance from A[first argument] to B[second argument]

    int previousIndexInB = 0;
    for (int i = 0; i < A.Count; i++)
    {
        double distance = euclidianDistance(i, previousIndexInB);//distance between A[i] and B[j - 1], initially 
        for (int j = previousIndexInB + 1; j < B.Count; j++)
        {
            var distance2 = euclidianDistance(i, j);//distance between A[i] and B[j]
            if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
            {
                distance = distance2;
                previousIndexInB = j;
            }
            else
            {
                break;//don't place the yield return statement here, because that could go wrong at the end of B.
            }
        }
        yield return LinearInterpolation(A[i], B[previousIndexInB]);
    }
}
Tuple<double, double> LinearInterpolation(Tuple<double, double> a, Tuple<double, double> b)
{
    return new Tuple<double, double>((a.Item1 + b.Item1) / 2, (a.Item2 + b.Item2) / 2);
}

对于您的信息,函数Average返回列表A包含的相同数量的插值点,这可能没问题,但您应该考虑针对您的特定应用程序。我在其中添加了一些注释以澄清一些细节,并且我已在上文中描述了此代码的所有方面。我希望它清楚,否则随意提问。

第二次编辑:我误读并认为你只有两个积分榜。我创建了一个上面接受多个列表的通用函数。它仍然只使用上面解释的那些原则。

IEnumerable<Tuple<double, double>> Average(List<List<Tuple<double, double>>> data)
{
    if (data == null || data.Count < 2 || data.Any(list => list == null || list.Any(p => p == null))) throw new ArgumentException();
    Func<double, double> square = d => d * d;
    Func<Tuple<double, double>, Tuple<double, double>, double> euclidianDistance = (a, b) => Math.Sqrt(square(a.Item1 - b.Item1) + square(a.Item2 - b.Item2));

    var firstList = data[0];
    for (int i = 0; i < firstList.Count; i++)
    {
        int[] previousIndices = new int[data.Count];//the indices of points which are closest to the previous point firstList[i - 1]. 
        //(or zero if i == 0). This is kept track of per list, except the first list.
        var closests = new Tuple<double, double>[data.Count];//an array of points used for caching, of which the average will be yielded.
        closests[0] = firstList[i];
        for (int listIndex = 1; listIndex < data.Count; listIndex++)
        {
            var list = data[listIndex];
            double distance = euclidianDistance(firstList[i], list[previousIndices[listIndex]]);
            for (int j = previousIndices[listIndex] + 1; j < list.Count; j++)
            {
                var distance2 = euclidianDistance(firstList[i], list[j]);
                if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
                {
                    distance = distance2;
                    previousIndices[listIndex] = j;
                }
                else
                {
                    break;
                }
            }
            closests[listIndex] = list[previousIndices[listIndex]];
        }
        yield return new Tuple<double, double>(closests.Select(p => p.Item1).Average(), closests.Select(p => p.Item2).Average());
    }
}

实际上我分别为2个列表做了具体案例可能是一件好事:它很容易解释并在理解通用版本之前提供了一个步骤。此外,可以取出平方根,因为它在排序时不会改变距离的顺序,只是长度。

第三次编辑:在评论中很明显可能存在错误。我认为除了上面提到的小虫子之外没有其他的东西,除了图表的末尾之外不应该有任何区别。作为它实际工作的证明,这是它的结果(虚线是平均值): enter image description here

答案 2 :(得分:2)

由于这些不是y=f(x)函数,它们可能类似于(x,y)=f(t)吗?

如果是这样,你可以沿t插值,并为每个t计算avg(x)和avg(y)。

编辑这当然假设t可以用于您的代码 - 这样您就拥有了T / X / Y三元组的有序列表。

答案 3 :(得分:0)

有几种方法可以做到这一点。一种是将所有数据组合成一组单独的点,并通过组合集做出最佳拟合曲线。

答案 4 :(得分:0)

你有例如带有

的2个“功能”
fc1 = { {1,0.3} {2, 0.5} {3, 0.1} }
fc1 = { {1,0.1} {2, 0.8} {3, 0.4} }

你想要两个函数的算术平均值(俚语:“平均值”)。为此,您只需计算逐点算术平均值:

fc3 = { {1, (0.3+0.1)/2} ... }

优化: 如果您有大量的积分,您应首先将“有序的X / Y对列表”转换为矩阵或至少按列存储点,如下所示: {0.3,0.1},{0.5,0.8},{0.1,0.4}