问题很简单。 让我们说你有功能
double interpolate (double x);
你有一张表有已知x->的地图; ÿ
例如
5 15
7 18
10 22
注意:真实的表格更大,这只是一个例子。
所以对于8你将返回18 +((8-7)/(10-7))*(22-18)= 19.3333333
我找到的一个很酷的方式是 http://www.bnikolic.co.uk/blog/cpp-map-interp.html (长话短说,它使用std :: map,key = x,value = y表示x-> y数据对)。
如果有人询问标题中的if else else方式是什么 它基本上是:
if ((x>=5) && (x<=7))
{
//interpolate
}
else
if((x>=7) && x<=10)
{
//interpolate
}
那么有更聪明的方法吗或地图方式是最先进的? :)
顺便说一下,我更喜欢C ++中的源代码,但显然任何具有1:1映射到C ++的语言解决方案都很不错。
答案 0 :(得分:29)
嗯,我能想到的最简单的方法就是使用二分搜索找到你的观点所在的点。如果可以,尽量避免使用地图,因为它们在实践中非常慢。
这是一种简单的方法:
const double INF = 1.e100;
vector<pair<double, double> > table;
double interpolate(double x) {
// Assumes that "table" is sorted by .first
// Check if x is out of bound
if (x > table.back().first) return INF;
if (x < table[0].first) return -INF;
vector<pair<double, double> >::iterator it, it2;
// INFINITY is defined in math.h in the glibc implementation
it = lower_bound(table.begin(), table.end(), make_pair(x, -INF));
// Corner case
if (it == table.begin()) return it->second;
it2 = it;
--it2;
return it2->second + (it->second - it2->second)*(x - it2->first)/(it->first - it2->first);
}
int main() {
table.push_back(make_pair(5., 15.));
table.push_back(make_pair(7., 18.));
table.push_back(make_pair(10., 22.));
// If you are not sure if table is sorted:
sort(table.begin(), table.end());
printf("%f\n", interpolate(8.));
printf("%f\n", interpolate(10.));
printf("%f\n", interpolate(10.1));
}
答案 1 :(得分:4)
存储您的积分:
index X Y
1 1 -> 3
2 3 -> 7
3 10-> 8
然后从最大值到最小值循环,一旦你得到一个数字,你知道它就是你想要的那个。
你想让我们说6
所以:
// pseudo
for i = 3 to 1
if x[i] <= 6
// you found your range!
// interpolate between x[i] and x[i - 1]
break; // Do not look any further
end
end
答案 2 :(得分:4)
您可以使用二叉搜索树来存储插值数据。当您有大量N个插值点时,这很有用,因为插值可以在O(log N)时间内执行。但是,在您的示例中,情况似乎并非如此,RedX建议的线性搜索更合适。
#include <stdio.h>
#include <assert.h>
#include <map>
static double interpolate (double x, const std::map<double, double> &table)
{
assert(table.size() > 0);
std::map<double, double>::const_iterator it = table.lower_bound(x);
if (it == table.end()) {
return table.rbegin()->second;
} else {
if (it == table.begin()) {
return it->second;
} else {
double x2 = it->first;
double y2 = it->second;
--it;
double x1 = it->first;
double y1 = it->second;
double p = (x - x1) / (x2 - x1);
return (1 - p) * y1 + p * y2;
}
}
}
int main ()
{
std::map<double, double> table;
table.insert(std::pair<double, double>(5, 6));
table.insert(std::pair<double, double>(8, 4));
table.insert(std::pair<double, double>(9, 5));
double y = interpolate(5.1, table);
printf("%f\n", y);
}
答案 3 :(得分:3)
是的,我想你应该在这些间隔和自然数字之间的地图中思考。我的意思是,只需标记间隔并使用开关:
switch(I) {
case Int1: //whatever
break;
...
default:
}
我不知道,这是我想到的第一件事。
编辑如果您的号码在相对较小的时间间隔内(在执行映射时需要考虑的事项),那么切换比if-else更有效
答案 4 :(得分:1)
你如何获得它是相当可读和可理解的,并且通过“聪明”的解决方案可以说很多。但是,您可以取消下限检查和笨拙&&
,因为序列是有序的:
if (x < 5)
return 0;
else if (x <= 7)
// interpolate
else if (x <= 10)
// interpolate
...
答案 5 :(得分:1)
如果你的x坐标必须是不规则的间距,那么按排序顺序存储x坐标,并使用二进制搜索找到最近的坐标,例如使用Daniel Fleischman的答案。
但是,如果您的问题允许,请考虑预先插入到规则间隔的数据。所以
5 15 7 18 10 22
变为
5 15 6 16.5 7 18 8 19.3333333 9 20.6666667 10 22
然后在运行时,你可以用O(1)进行插值,使用类似的东西:
double interp1( double x0, double dx, double* y, int n, double xi )
{
double f = ( xi - x0 ) / dx;
if (f<0) return y[0];
if (f>=(n-1)) return y[n-1];
int i = (int) f;
double w = f-(double)i;
return dy[i]*(1.0-w) + dy[i+1]*w;
}
使用
double y[6] = {15,16.5,18,19.3333333, 20.6666667, 22 }
double yi = interp1( 5.0 , 1.0 , y, 5, xi );
这不一定适用于所有问题 - 你可能最终失去准确性(如果没有包含所有x样本的漂亮网格),并且如果它会使你的表更多,它可能会有一个错误的缓存惩罚更大。但对于您可以开始控制x坐标的情况,这是一个很好的选择。