Найти максимум три числа в 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)следующим образом: setmtoxесли и только если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;