计算几个函数的平均函数

Pre*_*eli 6 c# algorithm

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

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

我有几个问题:

  1. 不同的列表没有相同数量的值
  2. X和Y值可以增加,减少和增加(等等)(参见下面的示例图片)

我需要在C#中实现这一点,我想这对算法本身并不重要.

线条的例子

对不起,我无法以更正式或数学的方式解释我的问题.

编辑:我用"X/Y对列表"替换术语"功能",这不那么令人困惑.

JBS*_*rro 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);
}
Run Code Online (Sandbox Code Playgroud)

供您参考,函数 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

第三次编辑:在评论中很明显可能存在错误。我认为除了提到的小错误之外,没有任何错误,除了图表末尾之外,应该没有任何区别。为了证明它确实有效,这是它的结果(虚线是平均值): 在此输入图像描述