Ограничьте значение между min и max, используя только арифметику - PullRequest
0 голосов
/ 07 ноября 2018

Можно ли ограничить значение в данном диапазоне, от минимального до максимального, используя только арифметику? То есть + - x / и %?

Я не могу использовать такие функции, как min, max или IF -statements.

Предположим, у меня есть диапазон [1850, 1880], для любых значений < 1850 должен отображаться 1850. Для значений > 1880, 1880 должно отображаться. Также было бы приемлемо, если бы за пределами диапазона отображалось только 1850.

Я пытался:

x = (((x - xmax) % (xmax - xmin)) + (xmax - xmin)) % (xmax - xmin) + xmin 

но он дает разные значения в середине диапазона для значений ниже xmin.

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Я нашел это, пока возился в ... 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
0 голосов
/ 07 ноября 2018

Если вы знаете размер целочисленного типа, вы можете извлечь его знаковый бит (при условии дополнения до двух), используя целочисленное деление:

// 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;
}

Желаемая функция выглядит так:

enter image description here

Полезно сначала сдвинуть минимум к нулю:

enter image description here

Эта функциональная форма может быть вычислена как сумма двух сдвинутых абсолютных значений функций ниже:

enter image description here

Однако результирующая функция масштабируется с коэффициентом 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).

...