Для ввода любого целого числа W ограничено диапазоном р знак равноИкс,Y], «переполнение», из-за отсутствия лучшего термина, W над р является W % (y-x+1) + x
, Это заставляет это обернуться вокруг, если W превышает Y.
В качестве примера этого принципа предположим, что мы перебираем месяцы календаря:
int this_month = 5;
int next_month = (this_month + 1) % 12;
где оба целых числа будут между 0 и 11 включительно. Таким образом, выражение выше «зажимает» целое число в диапазоне р = [0,11]. Этот подход использования выражения прост, элегантен и выгоден, так как пропускает ветвление.
А что если мы хотим сделать то же самое, но в обратном направлении? Следующее выражение работает:
int last_month = ((this_month - 1) % 12 + 12) % 12;
но это заумно Как это может быть украшено?
ТЛ; др — Может выражение ((x-1) % k + k) % k
упрощаться дальше?
Примечание: тег C ++ указан, потому что другие языки по-разному обрабатывают отрицательные операнды для оператора по модулю.
Ваше выражение должно быть ((x-1) + k) % k
, Это позволит правильно обернуть x = 0 до 11. В общем, если вы хотите сделать шаг назад больше, чем на 1, вам нужно убедиться, что вы добавили достаточно, чтобы первый операнд операции по модулю был> = 0.
Вот реализация в Python. Вы должны быть в состоянии перевести его на язык по вашему выбору:
def wrap_around(value, delta, min_val, max_val):
if delta >= 0:
return (value + delta - min_val) % max_val + min_val
else:
return ((value + delta) + max_val * (-delta) - min_val) % max_val + min_val
Это также позволяет использовать месяцы с метками от 0 до 11 или от 1 до 12, устанавливая min_val
а также max_val
соответственно.
к% к всегда будет 0. Я не уверен на 100%, что вы пытаетесь сделать, но, кажется, вы хотите, чтобы последний месяц был ограничен от 0 до 11 включительно.
(this_month + 11) % 12
Должно хватить.
Общее решение — написать функцию, которая вычисляет желаемое значение:
//Returns floor(a/n) (with the division done exactly).
//Let ÷ be mathematical division, and / be C++ division.
//We know
// a÷b = a/b + f (f is the remainder, not all
// divisions have exact Integral results)
//and
// (a/b)*b + a%b == a (from the standard).
//Together, these imply (through algebraic manipulation):
// sign(f) == sign(a%b)*sign(b)
//We want the remainder (f) to always be >=0 (by definition of flooredDivision),
//so when sign(f) < 0, we subtract 1 from a/n to make f > 0.
template<typename Integral>
Integral flooredDivision(Integral a, Integral n) {
Integral q(a/n);
if ((a%n < 0 && n > 0) || (a%n > 0 && n < 0)) --q;
return q;
}
//flooredModulo: Modulo function for use in the construction
//looping topologies. The result will always be between 0 and the
//denominator, and will loop in a natural fashion (rather than swapping
//the looping direction over the zero point (as in C++11),
//or being unspecified (as in earlier C++)).
//Returns x such that:
//
//Real a = Real(numerator)
//Real n = Real(denominator)
//Real r = a - n*floor(n/d)
//x = Integral(r)
template<typename Integral>
Integral flooredModulo(Integral a, Integral n) {
return a - n * flooredDivision(a, n);
}
Easy Peasy, не используйте первый модуль оператора, это лишнее:
int last_month = (this_month - 1 + 12) % 12;
что является общим случаем
В этом случае вы можете написать 11
, но я все равно делаю -1 + 11
поскольку это более ясно заявляет, чего вы хотите достичь.
Не уверен, что у вас была такая же проблема, как у меня, но моя проблема была в основном в том, что я хотел ограничить все числа определенным диапазоном. Скажите, что диапазон был 0-6, поэтому использование% 7 означает, что любое число выше 6 вернется к 0 или выше. Фактическая проблема в том, что числа меньше нуля не обернулись до 6. У меня есть решение для этого (где X — верхний предел вашего диапазона чисел, а 0 — минимум):
if(inputNumber <0)//If this is a negative number
{
(X-(inputNumber*-1))%X;
}
else
{
inputNumber%X;
}