2012-07-09 89 views
12

問題很容易。 可以說,你有功能更好的方法比如果其他如果...線性插值

double interpolate (double x); 

,你必須具有已知的X->ý
例如
音符的地圖的表:實表格比較大,這只是一個例子。

所以你8將返回18 +((8-7)/(10-7))*(22-18)=我發現19.3333333

一個很酷的方式是 http://www.bnikolic.co.uk/blog/cpp-map-interp.html (長話短說它對x-> y數據對使用std :: map,key = x,value = y)。

如果有人問什麼,如果不然,如果標題 其他方式,它基本上是:

if ((x>=5) && (x<=7)) 
{ 
//interpolate 
} 
else 
    if((x>=7) && x<=10) 
    { 
     //interpolate 
    } 

那麼,有沒有一種更聰明的方法做,或地圖的方式是藝術的狀態? :)

btw我更喜歡在C++ soutions,但顯然任何語言的解決方案,有1:1映射到C++是很好的。

+0

在這種情況下似乎不適用,但無論如何:如果插值點的「x」值間隔相等,則可以使用模運算來查找第一個大於等於query_value值的索引時間。一些線性轉換是必要的。 – 2012-07-10 09:53:42

+0

是的,我有意不作等距地表明我想要線性插值,但是間隔不一定相同。 – NoSenseEtAl 2012-07-10 11:49:13

+0

儘管如此,如果在應用某些函數之後可以使間隔等距,模數方法仍然可以工作。 – 2012-07-10 11:52:16

回答

3

是的,我猜你應該在這些間隔和自然數之間的映射中考慮。我的意思是,只需標記間隔並使用開關:

switch(I) { 

    case Int1: //whatever 
     break; 

     ... 

    default: 

} 

我不知道,這是我第一次想到的。

編輯交換機的效率比的if-else,如果你的號碼是一個相對較小的區間內(這件事情做映射時要考慮到)

+0

,但不是從x映射到我(int在開關中使用)幾乎是我試圖解決的問題? – NoSenseEtAl 2012-07-09 14:37:22

4

存儲您的積分排序:

index X Y 
1  1 -> 3 
2  3 -> 7 
3  10-> 8 

然後從max到min循環,只要你低於一個數字,你就知道它是你想要的。

你要假設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 
+1

當表的大小不是很小時,搜索樹(AVL/RB)可能更有效。 – 2012-07-10 09:48:52

+0

@AmbrozBizjak啓用了優化功能,該循環可以從分支預測中受益,因此可以預期合理的速度 – stefan 2012-07-10 09:52:34

+1

@stefan效率幾乎肯定仍然是線性的。作爲旁註: – 2012-07-10 09:55:19

1

如何你已經得到它是相當易於閱讀和理解,而且有很多關於這在「聰明」的解決方案可說。但是,您可以廢除下界檢查和笨拙&&因爲順序排列:

if (x < 5) 
    return 0; 
else if (x <= 7) 
    // interpolate 
else if (x <= 10) 
    // interpolate 
... 
+1

他說表格更大... – ErikE 2012-08-02 04:37:44

4

可以使用二叉搜索樹來存儲插值數據。當您有大量的N個插值點時,這是有利的,因爲隨後可以在O(logN)時間內執行插值。但是,在您的示例中,似乎並不是這種情況,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); 
} 
+0

請注意,我的示例對於演示目的而言不合理,實際數據大於5分。 – NoSenseEtAl 2012-07-10 11:16:01

+0

順便說一句,你重新做我鏈接到我的問題:)只是沒有提升:分配 – NoSenseEtAl 2012-07-10 11:27:22

26

那麼,我能想到的最簡單的方法是使用二進制搜索來找到你的觀點所在的位置。儘量避免使用地圖,因爲它們在練習中速度很慢。

這是一個簡單的方法:

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)); 
} 
+0

這看起來非常喜歡什麼操作。你知道'lower_bounds'究竟是如何執行二進制搜索嗎?根據文檔,它將在任何隨機訪問迭代器上實現O(logN)。太好了! – onitake 2012-07-27 10:45:16

+0

我不知道實現細節,但lower_bound使用運算符<執行二分搜索(實際上在O(logN)中)。 – 2012-07-27 15:20:03

+1

要獲得更加連續的結果,當x超出範圍時的輸出應該更好地成爲邊界值,例如,如果(x> table.back()。first)返回table.back()。 – Whyllee 2012-08-02 05:08:05

0

如果你的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 

然後在運行時,你可以:(1)使用像這樣帶O插值:使用

double y[6] = {15,16.5,18,19.3333333, 20.6666667, 22 } 
double yi = interp1(5.0 , 1.0 , y, 5, xi); 

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

不一定適合每一個概率lem--你最終會失去準確性(如果沒有包含所有x-樣本的好網格),並且如果它會使你的表格變得更大,它可能會導致緩存懲罰不佳。但是,對於首先控制x座標的情況,這是一個很好的選擇。

相關問題