Попробуем написать поиск максимума в массиве без использования IF
Найдем максимум из двух чисел
Воспользумся замечательным свойством
1 | |a - b| = a - b, если a >= b |
Таким образом
1 | |a - b| + a + b = a - b + a + b = 2a, если a >= b |
Итого будем пользоваться формулой
1 | max(a, b) = (|a - b| + a + b) / 2 |
Или в коде
1 | int max(int a, int b) |
Модуль числа
Можно просто вычислить
1 | |a| = √a² |
Но для целых чисел придется преобразовывать во float и обратно, могут быть переполнения и прочие неприятности.
Поэтому лучше пожонглируем битами
1 | |a| = sign(a) * a |
Отрицательные будут умножены на -1 и станут положительными, положительные на 1 и останутся неизмененными
1 | int absolute(int a) |
Знак числа
Тут без if обойтись сложнее
Создадим вспомогательную функцию
1 | sign_helper(a) = (a >> 31) & 1 |
Она берет самый старший бит, где в 32-битном числе хранится знак.
Таким образом
sign_helper(a) = 1, еслиa < 0, т.е. для отрицательных чиселsign_helper(a) = 0, еслиa = 0, для нуляsign_helper(a) = 0, еслиa > 0, для положительных
Сам знак числа вычислим через небольшую арифметику
1 | sign(a) = 1 - (sign_helper(a - 1) + sign_helper(a)); |
Так мы научились отделять 0 от положительных чисел и получили 2, 1 и 0
Сам знак числа получается отнятием от 1 этого значения:
1 - 2 = -1, для отрицательных1 - 1 = 0, для нуля1 - 2 = -1, для положительных
Весь код
1 | int sign_helper(int a) |
Хотя кажется, что для функции absolute нам не нужно явно отличать 0 от не нуля, но такая точность понадобится дальше
Итерация по массиву
У нас есть массив, например, такой:
1 | int arr[] = {1, 2, 17, 0, -4}; |
Надо по нему проитерироваться, но при этом нельзя использовать if. А ведь цикл for неявно содержит if в условии выхода.
Поэтому будем итерироваться рекурсивно
Нам понадобится длина массива
1 | int len = sizeof(arr) / sizeof(arr[0]) |
И напишем примерно нашу функцию
1 | int rec(int *arr, int len, int i, int accumulator) |
Эта функция, разумеется, упадет по stack overflow.
Надо каким-то образом, если i == len выйти из функции, вернув accumulator
Заметим, что
sign(len - i - 1) = 0на последнем шаге, когдаi = len - 1sign(len - i - 1) = 1когда шаг не последний
Объявим массив из двух указателей на функции.
1 | typedef int (*t_rec)(int *, int, int, int); // function type we will use |
По индексу 0 будет лежать указатель на функцию, возвращающую аккумулятор, а по индексу 1 - указатель на саму рекурсивную функцию
Будем брать следующую функцию из этого массива по индексу sign(len - i - 1)
1 | int rec(int *arr, int len, int i, int accumulator) |
Теперь надо вызывать правильно эту фунцию. В качестве начального значения аккумулятора передадим минимальное значение для int: std::numeric_limits<int>::min()
1 | int max_arr(int* arr, int len) |
Весь код
1 |
|
Вариант на javascript
1 | const sign_helper = a => (a >> 31) & 1; |