Найти максимум три числа в C без использования условного оператора и тернарного оператора
Я должен найти максимум три числа, предоставленных Пользователем, но с некоторыми ограничениями. Он не позволяет использовать какие-либо условные операторы. Я попробовал использовать тернарный оператор, как показано ниже.
max=(a>b?a:b)>c?(a>b?a:b):c
Но опять же его ограничили использованием тернарного оператора. Сейчас я не получаю никакого понятия, как это сделать?
13 ответов:
Использование преимущества короткого замыкания в булевых выражениях:
int max(int a, int b, int c) { int m = a; (m < b) && (m = b); //these are not conditional statements. (m < c) && (m = c); //these are just boolean expressions. return m; }
Пояснение:
В булевой операции
Примените этот принцип к приведенному выше коду. ИзначальноAND
, такой какx && y
, y вычисляется тогда и только тогда, когдаx
это правда. Еслиx
ложно, тоy
не вычисляется, потому что все выражение было бы ложным, которое может быть выведено даже без вычисленияy
. Это называется коротким замыканием, когда значение логического выражения может быть выведено без вычисления всех операндов в оно.m
- этоa
. Теперь, если(m < b)
истинно, то это означает, чтоb
больше, чемm
(что на самом делеa
), поэтому второе подвыражение(m = b)
вычисляется иm
устанавливается вb
. Если же(m < b)
ложно, то второе подвыражение не будет вычислено иm
останетсяa
(что больше, чемb
). Аналогичным образом вычисляется второе выражение (в следующей строке).Короче говоря, вы можно прочитать выражение
(m < x) && (m = x)
следующим образом: setm
tox
если и только еслиm
меньше, чемx
то есть(m < x)
истинно. Надеюсь, это поможет вам понять код.Тестовый код:
int main() { printf("%d\n", max(1,2,3)); printf("%d\n", max(2,3,1)); printf("%d\n", max(3,1,2)); return 0; }
Вывод:
3 3 3
Онлайн-демонстрация: http://www.ideone.com/8045P
Примечание реализация
max
выдает предупреждения, поскольку вычисляемые выражения не используются:Прог.c: 6: предупреждение: значение вычисленный не используется
еда.c: 7: предупреждение: вычисленное значение не используетсяЧтобы избежать этих (безвредных) предупреждений, вы можете реализовать
max
следующим образом:Фокус в том, что теперь мы приводим булевы выражения кint max(int a, int b, int c) { int m = a; (void)((m < b) && (m = b)); //these are not conditional statements. (void)((m < c) && (m = c)); //these are just boolean expressions. return m; }
void
, что вызывает подавление предупреждений :
Предполагая, что вы имеете дело с целыми числами, как насчет:
#define max(x,y) (x ^ ((x ^ y) & -(x < y))) int max3(int x, int y, int z) { return max(max(x,y),z); }
Просто чтобы добавить еще одну альтернативу, чтобы избежать условного выполнения (который не является тем, который я бы использовал, но кажется отсутствующим в наборе решений):
Этот подход использует (как и большинство других) тот факт, что результат булева выражения при преобразовании в int дает либо 0, либо 1. Упрощенный вариант для двух значений будет следующим:int max( int a, int b, int c ) { int l1[] = { a, b }; int l2[] = { l1[ a<b ], c }; return l2[ l2[0] < c ]; }
int max( int a, int b ) { int lookup[] { a, b }; return lookup[ a < b ]; }
Если выражение
a<b
верно, мы возвращаемb
, бережно хранимый в первом индексе массива поиска. Если выражение возвращает false, затем мы возвращаемa
, который хранится как элемент0
массива поиска. Используя это как строительный блок, вы можете сказать:int max( int a, int b, int c ) { int lookup[ max(a,b), c ]; return lookup[ max(a,b) < c ]; }
, который может быть тривиально преобразован в код выше, избегая второго вызова внутреннего
max
, используя результат, уже сохраненный вlookup[0]
, и вставляя исходный вызов вmax(int,int)
.
(Эта часть - просто еще одно доказательство, которое вы должны измерить, прежде чем делать выводы, см. редактирование в конце)
Относительно чего буду ли я на самом деле использовать... Ну, вероятно, тот, который @ Foo Baa здесь модифицирован для использования встроенной функции, а не макроса. Следующим вариантом будет либо этот, либо тот, который указан в @MSN здесь.
Общим знаменателем этих трех решений, отсутствующих в общепринятом ответе, является то, что они не только избегают синтаксической конструкцииif
или тернарного оператора?:
, но и вообще избегают ветвления, что может иметь влияние в случае, если эти три решения не совпадают. спектакль. ветвь-предиктор в ЦП не может пропустить, когда нет ветвей.
Рассматривая производительность, сначала измерьте, а затем подумайте
Я фактически реализовал несколько различных опций для 2-way max и проанализировал сгенерированный компилятором код. Следующие три решения генерируют все тот же ассемблерный код:
int max( int a, int b ) { if ( a < b ) return b; else return a; } int max( int a, int b ) { return (a < b? b : a ); } int max( int a, int b ) { (void)((a < b) && (a = b)); return a; }
Что неудивительно, так как все три представляют собой одно и то же операция. Интересная часть информации заключается в том, что сгенерированный код не содержит никакой ветви. Реализация проста с помощью инструкции
cmovge
(тест выполняется с помощью g++ на платформе intel x64):movl %edi, %eax # move a into the return value cmpl %edi, %esi # compare a and b cmovge %esi, %eax # if (b>a), move b into the return value ret
Фокус заключается в условной инструкции перемещения, которая избегает любой потенциальной ветви.
Ни одно из других решений не имеет ветвей, но все они переводят на большее количество инструкций процессора, чем любое из этих, что в конце концов убеждает нас в том, что мы всегда следуетписать простой код и позволить компилятору оптимизировать его для нас.
Обновление: глядя на это 4 года спустя, я вижу, что это плохо работает, если два или более значений оказываются равными. Замена
>
на>=
изменяет поведение, но не устраняет проблему. Он все еще может быть спасен, поэтому я не буду его удалять, но не используйте его в производственном коде.
Ладно, вот мой:
Обратите внимание, что использованиеint max3(int a, int b, int c) { return a * (a > b & a > c) + b * (b > a & b > c) + c * (c > a & c > b); }
&
вместо&&
позволяет избежать любого условного кода; он опирается на тот факт, что>
всегда дает 0 или 1. (Код генерируемые дляa > b
могут включать в себя условные переходы, но они не видны из C.)
int fast_int_max(int a, int b) { int select= -(a < b); unsigned int b_mask= select, a_mask= ~b_mask; return (a&a_mask)|(b&b_mask); } int fast_int_max3(int a, int b, int c) { return fast_int_max(a, fast_int_max(b, c)); }
Логически значимые операторы (включая b), либо все нулевые биты (если a
int max(int a, int b) { long d = (long)b - (long)a; int m = (int)(d >> 63); return a & m | b & ~m; } int max(int a, int b, int c) { long d; int m; d = (long)b - (long)a; m = (int)(d >> 63); a = a & m | b & ~m; d = (long)c - (long)a; m = (int)(d >> 63); return a & m | c & ~m; }
Никаких условных обозначений. Только гипс для uint. Совершенное решение.
int abs (a) { return (int)((unsigned int)a); } int max (a, b) { return (a + b + abs(a - b)) / 2; } int min (a, b) { return (a + b - abs(a - b)) / 2; } void sort (int & a, int & b, int & c) { int max = max(max(a,b), c); int min = min(min(a,b), c); int middle = middle = a + b + c - max - min; a = max; b = middle; c = min; }
#include "stdafx.h" #include <iostream> int main() { int x,y,z; scanf("%d %d %d", &x,&y, &z); int max = ((x+y) + abs(x-y)) /2; max = ((max+z) + abs(max-z)) /2; printf("%d ", max); return 0; }
Вы можете использовать этот код, чтобы найти наибольший из двух:
max{a,b} = abs(a-b)/2 + (a+b)/2
Затем используйте его снова, чтобы найти третье число:
max{a,b,c} = max(a,max(b,c))
Смотрите, что это работает для положительных чисел вы можете изменить его на работу для отрицательных, а также.
Нетусловных операторов , только циклы и назначения. И совершенно по-другому формируют чужие ответы:)
while (a > b) { while (a > c) { tmp = a; goto finish; } tmp = c; goto finish; } while (b > c) { tmp = b; goto finish; } tmp = c; finish: max = tmp;