2011-07-20 35 views
9

假設我有一個向量v,其中包含m個元素,並且向量稱爲i的向量的隨機訪問索引。使用模運算符來保持容器的索引

當我增加索引,如果它超出界限,我想索引第一個(第零)元素。同樣,當我遞減索引時,如果索引是< 0,我想索引到最後一個元素。目前,我只通過容器一個元素同時移動,所以想出了這個功能:

unsigned int GetIndexModM(int index,unsigned int m) {return (index + m) % m;} 

的調用點可能是這樣的:

std::vector<Whatever> v = ... // initialise with 5 elements 
unsigned int i = 0; 
unsigned int j = GetIndexModM(static_cast<int>(i) - 1,v.size()); // get preceeding index 

此功能

unsigned int j = GetIndexModM(static_cast<int>(i) - 17,v.size()); // oops: returns -2 

我的問題:什麼是最優雅的實現,需要的任意整數,並返回它的位置作爲一個指數函數但是如果一個減去值>米指數會失敗?

回答

12

處理MOD的訣竅是這樣的,這正和負數的工作原理:

val = ((val % mod_val) + mod_val) % mod_val; 

例如,假設我們要保持在0和359(含)之間的值。我們可以這樣使用:

val = ((val % 360) + 360) % 360; 

下面是C++中的一個簡單示例。

int getmod(int val, int mod) { 
    return ((val % mod) + mod) % mod; 
} 

int main() { 
    printf("%d\n", getmod(50,360)); // prints 50 
    printf("%d\n", getmod(-400,360)); // prints 320 
    printf("%d\n", getmod(350,360)); // prints 350 
    printf("%d\n", getmod(375,360)); // prints 15 
    printf("%d\n", getmod(-725,360)); // prints 355 


    return 0; 
} 
+0

在某些平臺上,以比較爲代價避免第二次模運算可能會更快:'val = val%mod;返回val <0? val + mod:val;' –

+0

當val <-mod_val',或者它只處理帶有-mod_val

+0

@Andre Caron - 它在這種情況下工作,我又增加了一個例子(見上一頁printf)。 – dcp

0

不幸的是,C++沒有實現一個適用於負整數的正確模數。

我認爲最清潔的解決方案確實是使用if正確處理所有情況。這至少使得代碼很明顯(因爲每一個案件是明確),更容易錯誤地發現:

unsigned GetIndexModM(int index, unsigned m) { 
    if (index < 0) 
     return GetIndexModM(index + m, m); 
    if (index >= m) 
     return index % m; 
    return index; 
} 
+3

如果'index'很大並且是負值,那麼這非常不合適。 –

+0

我用-400,360的參數試過了你的函數,我得到了216個返回結果,但答案應該是320. – dcp

+0

@dcp再試一次,可怕的錯誤。 –

-1

下確保index是在[0,n),但只有一個模運算,並沒有分支:

index = index % n + (index < 0)*n 

其中第一項(包含模運算符)得到值代入式(-nn)和第二項確保值是在[0,n)。

請注意,當n是一個無符號類型時,以及C++較早的(pre-11)版本中,其中的%運算符對負參數的實現依賴性是不可靠的。