Использование оператора модуля для отслеживания индексов контейнера - PullRequest
10 голосов
/ 20 июля 2011

Предположим, у меня есть вектор v с m элементами в нем и индекс произвольного доступа к вектору с именем i.

Когда я увеличиваю индекс, если он выходит за границы, я хочу индексировать первый (нулевой) элемент. Точно так же, когда я уменьшаю индекс, если индекс <0, я хочу индексировать до последнего элемента. На данный момент я перемещаюсь через контейнер только по одному элементу за раз, поэтому придумала следующую функцию: </p>

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

Однако эта функция не будет выполнена, если вычесть значение> m из индекса:

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

Мой вопрос: Какая самая элегантная реализация функции, которая принимает любое целое число и возвращает свое место в качестве индекса?

Ответы [ 3 ]

13 голосов
/ 20 июля 2011

Уловка для обработки 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 голосов
/ 16 марта 2016

Следующее гарантирует, что index находится в [0, n), но только с одной операцией модуля и без ветвей:

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

, где первый член (содержащий оператор модуля) получает значение в (-n, n), а второй член гарантирует, что значение находится в [0, n).

Обратите внимание, что это ненадежно, когда n является типом без знака и в более старых (до 11) версиях C ++, где оператор% зависит от реализации для отрицательных аргументов.

0 голосов
/ 20 июля 2011

К сожалению, C ++ не реализует правильный модуль, который все еще работает правильно для отрицательных целых чисел.

Я думаю, что самое чистое решение действительно использует if, чтобы правильно позаботиться о всех случаях. Это, по крайней мере, делает код очевидным (потому что каждый случай явный ), и ошибки легче найти:

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