我有几个有序的X / Y对列表,我想计算一个有序的X / Y对列表,代表这些列表的平均值.
所有这些列表(包括“平均列表”)将被绘制到图表上(参见下面的示例图片).
我有几个问题:
>不同的列表没有相同数量的值
> X和Y值可以增加,减少和增加(等等)(参见下面的示例图片)
我需要在C#中实现这一点,我想这对算法本身并不重要.
对不起,我无法以更正式或数学的方式解释我的问题.
编辑:我用“X / Y对列表”替换术语“功能”,这不那么令人困惑.
解决方法
我会使用贾斯汀提出的方法,进行一次调整.他建议使用带有小数指数的映射表,但我建议使用整数指数.这可能听起来有点数学,但是必须阅读以下两次并不是一件好事(我也必须这样做).假设对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>,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个列表做了具体案例可能是一件好事:它很容易解释并在理解通用版本之前提供了一个步骤.此外,可以取出平方根,因为它不会改变排序时的距离顺序,只改变长度.
第三次编辑:在评论中,很明显可能存在错误.我认为除了上面提到的小虫子之外没有其他的东西,除了图表的末尾之外不应该有任何区别.作为它实际工作的证明,这是它的结果(虚线是平均值):