2011-07-20 5 views
9

Załóżmy, że mam wektor v z elementami m w nim i indeksem dostępu losowego do wektora o nazwie i.Używanie operatora modulus do utrzymywania indeksów kontenera

Kiedy zwiększam indeks, jeśli wykracza poza granice, chcę zindeksować pierwszy (zeroth) element. Podobnie, gdy zmniejszam indeks, jeśli indeks to < 0, chcę indeksować do ostatniego elementu. W tej chwili jestem w ruchu jedynie przez pojemnik jeden element na raz, więc wymyślił tej funkcji:

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

Wezwanie-witryna może wyglądać następująco:

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 

funkcja ta jednak jeśli ktoś nie odejmuje wartość> m z indeksem:

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

moje pytanie: Co to jest najbardziej elegancki realizacja funkcję, która pobiera dowolną liczbę całkowitą i powraca to miejsce jako wskaźnik?

Odpowiedz

12

Sztuką do obsługi MOD jest ten, który pracuje z pozytywnymi, jak i negatywnymi liczbach:

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

Na przykład, załóżmy, że chcemy zachować wartość pomiędzy 0 a 359 włącznie. Możemy użyć tego:

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

Oto prosty przykład w 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

Na niektórych platformach może być szybsze uniknięcie drugiej operacji modulo, kosztem porównania: 'val = val% mod; return val <0? val + mod: val; ' –

+0

Czy to działa, gdy' val <-mod_val', czy obsługuje tylko ujemne liczby całkowite z '-mod_val

+0

@Andre Caron - Działa w tym scenariuszu, dodałem kolejny przykład (zobacz ostatni printf). – dcp

0

Niestety, C++ nie implementuje odpowiedniego modułu, który nadal działa poprawnie dla ujemnych liczb całkowitych.

Wydaje mi się, że najczystsze rozwiązanie polega na tym, że właściwie zajmuje się wszystkimi sprawami, używając if. Ten przynajmniej sprawia kod oczywiste (ponieważ każdy przypadek jest wyraźny) i błędy łatwiej znaleźć:

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

To bardzo nieefektywne, jeśli "indeks" jest duży i ujemny. –

+0

Próbowałem twojej funkcji z argumentami -400,360 i otrzymałem 216, ale odpowiedź powinna być 320. – dcp

+0

@dcp Spróbuj ponownie, straszny błąd. –

-1

Poniższa zapewnia index jest w [0, n), ale tylko z jednej operacji modułu i żadnych oddziałów :

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

gdzie pierwszy człon (zawierający operator modułu zespolonego) osiąga wartość do (-n, n) i drugi człon zapewnia, że ​​wartość w [0, n).

Należy zauważyć, że jest to niewiarygodne, gdy n jest typem niepodpisanym iw starszych wersjach (starszych niż 11) C++, gdzie operator% jest zależny od implementacji dla argumentów ujemnych.