Ограничить значение между min и max, используя только арифметику
Можно ли ограничить значение в данном диапазоне между минимальным и максимальным, используя только арифметику? То есть, +
-
x
/
а также %
?
Я не могу использовать такие функции, как min
, max
ни IF
-заявления.
Давайте предположим, что у меня есть диапазон [1850, 1880]
для любых значений < 1850
должно отображаться 1850
, Для ценностей > 1880
, 1880
должен отображаться. Было бы также приемлемо, если только 1850
был отображен за пределами диапазона.
Я старался:
x = (((x - xmax) % (xmax - xmin)) + (xmax - xmin)) % (xmax - xmin) + xmin
но это дает разные значения в середине диапазона для значений ниже, чем xmin
,
2 ответа
Я нашел это, пока возился в... Excel. Это работает только для строго положительных целых чисел. Хотя это не является более ограничительным, как ответ meowgoesthedog, потому что он также эффективно делит пополам целое пространство путем деления на два в конце. Он не использует мод.
//A = 1 if x <= min
//A = 0 if x >= min
A = 1-(min-min/x)/min
//B = 0 if x <= max
//B = 1 if x > max
B = (max-max/x)/max
x = A*min + (1-A)*(1-B)*x + B*max
Если вы знаете размер целочисленного типа, вы можете извлечь его знаковый бит (при условии дополнения до двух), используя целочисленное деление:
// Example in C
int sign_bit(int s)
{
// cast to unsigned (important)
unsigned u = (unsigned)s;
// number of bits in int
// if your integer size is fixed, this is just a constant
static const unsigned b = sizeof(int) * 8;
// pow(2, b - 1)
// again, a constant which can be pre-computed
static const unsigned p = 1 << (b - 1);
// use integer division to get top bit
return (int)(u / p);
}
Возвращает 1, если s < 0
и 0 в противном случае; его можно использовать для расчета абсолютного значения:
int abs_arith(int v)
{
// sign bit
int b = sign_bit(v);
// actual sign (+1 / -1)
int s = 1 - 2 * b;
// sign(v) * v = abs(v)
return s * v;
}
Желаемая функция выглядит так:
Полезно сначала сдвинуть минимум к нулю:
Эта форма функции может быть вычислена как сумма двух смещенных функций абсолютного значения ниже:
Однако результирующая функция масштабируется в 2 раза; здесь помогает смещение на ноль, потому что нам нужно только разделить на 2 и вернуться к исходному минимуму:
// Example in C
int clamp_minmax(int val, int min, int max)
{
// range length
int range = max - min;
// shift minimum to zero
val = val - min;
// blue function
int blue = abs_arith(val);
// green function
int green = range - abs_arith(val - range);
// add and divide by 2
val = (blue + green) / 2;
// shift to original minimum
return val + min;
}
Это решение, хотя и удовлетворяет требованиям проблемы, ограничено целочисленными типами со знаком (и языками, которые допускают целочисленное переполнение - я не уверен, как это можно преодолеть, например, в Java).
Я нашел это решение в Python:
A = -1 # Minimum value
B = +1 # Maximum value
x = min(max(x, A), B)