Объясните этот фрагмент, который находит максимум двух целых чисел без использования if-else или любого другого оператора сравнения?

найти максимум два числа. Вы не должны использовать if-else или любой другой оператор сравнения. Я нашел этот вопрос на онлайн-доске объявлений, поэтому я подумал, что должен спросить в StackOverflow

пример Входной сигнал: 5, 10 Выход: 10

Я нашел это решение, может кто-нибудь помочь мне понять эти строки кода

int getMax(int a, int b) {  
    int c = a - b;  
    int k = (c >> 31) & 0x1;  
    int max = a - k * c;  
    return max;  
}

19 ответов


int getMax(int a, int b) {
    int c = a - b;
    int k = (c >> 31) & 0x1;
    int max = a - k * c;
    return max;
}

давайте разберем это. Эта первая строка кажется простой - она хранит разницу a и b. Это значение отрицательно, если a < b и неотрицательный в противном случае. Там на самом деле ошибка здесь - если разница чисел a и b настолько большой, что он не может поместиться в целое число, это приведет к неопределенному поведению - ой! Так что давайте предположим, что здесь этого не происходит.

в следующей строке, которая is

int k = (c >> 31) & 0x1;

идея в том, чтобы проверить, если значение c отрицательный. Практически во всех современных компьютерах числа хранятся в формате два, в котором старший бит числа равен 0, если число положительное и 1, если число отрицательное. Кроме того, большинство ints 32 бита. (c >> 31) сдвигает число вниз 31 бит, оставляя самый высокий бит числа в месте для самого низкого бита. Следующий шаг-взять этот номер и ANDing он с 1 (чье двоичное представление равно 0 везде, кроме последнего бита) стирает все более высокие биты и просто дает вам самый низкий бит. Так как самый низкий бит c >> 31 это самый высокий бит c, это читает самый высокий бит c 0 или 1. Так как самый высокий бит-1 iff c это 1, это способ проверить, является ли c отрицательно (1) или положительно (0). Сочетая это рассуждение с вышеизложенным,k 1, Если a < b и 0 иначе.

последний шаг сделать это:

int max = a - k * c;

если a < b, потом k == 1 и k * c = c = a - b и так

a - k * c = a - (a - b) = a - a + b = b

который является правильным max, так как a < b. В противном случае, если a >= b, потом k == 0 и

a - k * c = a - 0 = a

что также является правильным макс.


поехали: (a + b) / 2 + |a - b| / 2


использовать побитовые хаки

r = x ^ ((x ^ y) & -(x < y)); // max(x, y)

если вы знаете, что INT_MIN <= x - y <= INT_MAX, затем вы можете использовать следующие, Что быстрее, потому что (x - y) только должен быть оценен один раз.

r = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // max(x, y)

источник : бит Twiddling хаки Шон Эрон Андерсон


(sqrt( a*a + b*b - 2*a*b ) + a + b) / 2

это основано на той же технике, как Майк.решение dld, но здесь менее "очевидно", что я делаю. Операция " abs " выглядит так, как будто вы сравниваете знак чего-то, но я здесь использую тот факт, что sqrt() всегда вернет вам положительный квадратный корень, поэтому я в квадрате (a-b) записываю его полностью, а затем квадратно укореняю его снова, добавляя a+b и деля на 2.

вы увидите, что он всегда работает: например, пользователя пример 10 и 5 Вы получаете sqrt(100 + 25 - 100) = 5 затем добавить 10 и 5 дает вам 20 и разделить на 2 дает вам 10.

Если мы используем 9 и 11 числа, мы получим (корень(121 + 81 - 198) + 11 + 9)/2 = (Функция sqrt(4) + 20) / 2 = 22/2 = 11


самый простой ответ ниже.

#include <math.h>

int Max(int x, int y)
{
    return (float)(x + y) / 2.0 + abs((float)(x - y) / 2);
}

int Min(int x, int y)
{
    return (float)(x + y) / 2.0 - abs((float)(x - y) / 2);
}

int max(int i, int j) {
    int m = ((i-j) >> 31);
    return (m & j) + ((~m) & i);
}

такое решение позволяет избежать умножения. m будет либо 0x00000000, либо 0xffffffff


используя идею сдвига, чтобы извлечь знак, размещенный другими, вот еще один способ:

max (a, b) = new[] { a, b } [((a - b) >> 31) & 1]

это толкает два числа в массив с максимальным числом, заданным элементом массива, индекс которого является битом знака разницы между двумя числами.

отметим, что:

  1. разница (a - b) может переполниться.
  2. если номера без знака и >> оператор ссылается на логическое правая смена,& 1 - это лишнее.

вот как я думаю, я бы сделал эту работу. Это не так читаемо, как вам может понравиться, но когда вы начинаете с "как мне сделать X без использования очевидного способа сделать X, вы должны ожидать этого. Теоретически это тоже дает некоторую переносимость, но вам нужно найти довольно необычную систему, чтобы увидеть проблему.

#define BITS (CHAR_BIT * sizeof(int) - 1)

int findmax(int a, int b) { 
    int rets[] = {a, b};
    return rets[unsigned(a-b)>>BITS];
}

Это имеет некоторые преимущества по сравнению с показанным на вопрос. Прежде всего, он вычисляет правильный размер сдвига, а не жестко закодирован для 32-битного значения типа int. Во-вторых, с большинством компиляторов мы можем ожидать, что все умножение произойдет во время компиляции, поэтому все, что осталось во время выполнения, - это тривиальная битовая манипуляция (вычитание и сдвиг), за которой следует загрузка и возврат. Короче говоря, это почти наверняка будет довольно быстро, даже на самом маленьком микроконтроллере, где оригинал использовал умножение, которое должно было произойти во время выполнения, поэтому, хотя это, вероятно, довольно быстро на настольной машине, это часто будет немного медленнее на маленьком микроконтроллере.


вот что делают эти строки:

c-это a-b. если c отрицательно, a

k-32-й бит c, который является битом знака c (предполагая 32-битные целые числа. Если сделано на платформе с 64-битными целыми числами, этот код не будет работать). Он сдвинул 31 бит вправо, чтобы удалить самый правый 31 бит, оставив знаковый бит в самом правом месте, а затем anding его с 1, чтобы удалить все биты влево (который будет заполнен 1s, если c отрицательный). Таким образом, k будет 1, Если c отрицательный и 0, если c положительный.

тогда max = a-k * c. Если c равно 0, это означает a>=b, поэтому max - a-0 * c = a. Если c равно 1, это означает, что a

в целом, он просто использует бит знака разницы, чтобы избежать использования больше или меньше операций. Если честно, немного глупо говорить, что этот код не использует сравнение. c-результат сравнения a и b. Код просто не использует оператор сравнения. Вы можете сделать то же самое во многих кодах сборки, просто вычитая числа, а затем прыгая на основе значений, установленных в регистре состояния.

Я также должен добавить, что все эти решения предполагают, что два числа являются целыми числами. Если они поплавки, парный, или что-то более сложное (BigInts, Рациональные числа и т. д.) тогда вам действительно нужно использовать оператор сравнения. Маленькие хитрости обычно не подходят для таких.


функция getMax () без какой-либо логической операции -

int getMax(int a, int b){
    return (a+b+((a-b)>>sizeof(int)*8-1|1)*(a-b))/2;
}

объяснение:

давайте разбить "Макс" на куски,

max
= ( max + max ) / 2
= ( max + (min+differenceOfMaxMin) ) / 2
= ( max + min + differenceOfMaxMin ) / 2
= ( max + min + | max - min | ) ) / 2

таким образом, функция должна выглядеть следующим образом -

getMax(a, b)
= ( a + b + absolute(a - b) ) / 2

теперь

absolute(x)
= x [if 'x' is positive] or -x [if 'x' is negative]
= x * ( 1 [if 'x' is positive] or -1 [if 'x' is negative] )

в целочисленном положительном числе Первый БИТ (БИТ знака) -0; в отрицательном -1. Сдвигая биты вправо ( > > ), можно захватить Первый БИТ.

во время правой смены пустое пространство заполняется битом знака. Так что 01110001 >> 2 = 00011100, а 10110001 >> 2 = 11101100.

в результате для 8-битного сдвига числа 7 бит либо произведет- 1 1 1 1 1 1 1 [0 или 1] для отрицательных, или 0 0 0 0 0 0 0 [0 или 1] для положительных.

теперь, если или операция производится с 00000001 (= 1), негативные количество выходов -11111111 (= -1), и положительные- 00000001 (= 1).

и

absolute(x)
= x * ( 1 [if 'x' is positive] or -1 [if 'x' is negative] )
= x * ( ( x >> (numberOfBitsInInteger-1) ) | 1 )
= x * ( ( x >> ((numberOfBytesInInteger*bitsInOneByte) - 1) ) | 1 )
= x * ( ( x >> ((sizeOf(int)*8) - 1) ) | 1 )

наконец,

getMax(a, b)
= ( a + b + absolute(a - b) ) / 2
= ( a + b + ((a-b) * ( ( (a-b) >> ((sizeOf(int)*8) - 1) ) | 1 )) ) / 2

статический int mymax (int a, int b)

    {
        int[] arr;
        arr = new int[3];
        arr[0] = b;
        arr[1] = a;
        arr[2] = a;
        return arr[Math.Sign(a - b) + 1];

    }

Если B > A, то (А-Б) будет отрицательным, знак будет возвращать -1, добавив 1 мы получаем индекс 0, который является B, если B=A, то А-B будет 0, +1 даст 1 индекс, поэтому она не имеет значения, если мы возвращаемся A или B, если А > B, то a-b будет положительной и знак вернется 1, добавление 1 мы получаем индекс 2, где хранится.


#include<stdio.h>
main()
{
        int num1,num2,diff;
        printf("Enter number 1 : ");
        scanf("%d",&num1);
        printf("Enter number 2 : ");
        scanf("%d",&num2);
        diff=num1-num2;
        num1=abs(diff);
        num2=num1+diff;
        if(num1==num2)
                printf("Both number are equal\n");
        else if(num2==0)
                printf("Num2 > Num1\n");
        else
                printf("Num1 > Num2\n");
}

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

double findmax(double a, double b)
{
    //find the difference of the two numbers
    double diff=a-b;
    double temp_diff=diff;
    int int_diff=temp_diff;
    /*
      For the floating point numbers the difference contains decimal
      values (for example 0.0009, 2.63 etc.) if the left side of '.' contains 0 then we need
      to get a non-zero number on the left side of '.'
    */
    while ( (!(int_diff|0)) && ((temp_diff-int_diff)||(0.0)) )
    {
       temp_diff = temp_diff * 10;
       int_diff = temp_diff;
    }
    /*
      shift the sign bit of variable 'int_diff' to the LSB position and find if it is 
      1(difference is -ve) or 0(difference is +ve) , then multiply it with the difference of
      the two numbers (variable 'diff') then subtract it with the variable a.
    */
    return a- (diff * ( int_diff >> (sizeof(int) * 8 - 1 ) & 1 ));
}

описание

  • первое, что функция принимает аргументы как double и имеет тип возврата как double. Причина этого в том, что создать одну функцию, которая может найти максимум для всех типов. Когда типа integer числа предоставляются или один является целым числом, а другой-плавающей точкой, то также из-за неявного преобразования функция может быть использована для поиска max для целых чисел.
  • основная логика проста, допустим, у нас есть два числа a & b, Если a-b>0(т. е. разница положительна), то a-максимум, если a-b==0, то оба равны и если a-b
  • бит знака сохраняется как самый значительный бит (MSB) в памяти. Если MSB равно 1 и наоборот. Чтобы проверить, является ли MSB 1 или 0, мы сдвигаем MSB в позицию LSB и побитовое & с 1, если результат равен 1, то число - ve еще нет. is +ve. Этот результат получается утверждением:

    int_diff >> (sizeof(int) * 8 - 1 ) & 1

здесь, чтобы получить знаковый бит из MSB в LSB, мы сдвигаем его вправо на K-1 бит (где k-количество бит, необходимое для сохранения целого числа в памяти, которое зависит от типа системы). Здесь k= sizeof (int) * 8 as sizeof () дает количество байтов, необходимое для сохранения целого числа, чтобы получить no. из битов мы умножаем его на 8. После правой смены мы применяем побитовое & с 1, чтобы получить результат.

  • теперь после получения результата (предположим его как r) как 1(для-ve diff) и 0(для +ve diff) умножим результат на разность двух чисел, логика дается следующим образом:

    1. если a>b, то a-b>0 т. е. является +ve, поэтому результат 0(т. е. Р=0). Итак, a-(a-b)*r => a-(a-b)*0, что дает " a " как максимум.
    2. если a a-(a-b)*1 => a-a+b =>b , что дает " b " как максимум.
  • теперь осталось два пункта 1. использование цикла while и 2. почему я использовал переменную 'int_diff' как целое число. Чтобы ответить на них правильно, мы должны понять некоторые моменты:

    1. плавающий тип значения нельзя использовать в качестве операнда для побитовых операторов.
    2. по вышеуказанной причине нам нужно получить значение в целочисленном значении, чтобы получить знак различия с помощью побитовых операторов. Эти две точки описывают необходимость переменной 'int_diff' как целочисленного типа.
    3. теперь предположим, что мы находим разницу в переменной "diff" теперь есть 3 возможности для значений "diff" независимо от знака этих значений. (ля.) |diff / >=1, (b). 0
    4. когда мы присваиваем значение double целочисленной переменной, десятичная часть теряется.
    5. для случая (a) значение 'int_diff' >0 (т. е. 1,2,...). Для двух других случаев int_diff=0.
    6. условие (temp_diff-int_diff) / / 0.0 проверяет, если diff==0, поэтому оба числа равны.
    7. если diff!=0 тогда мы проверяем, является ли int_diff|0 истинным, т. е. случай (b) истинен
    8. в цикле while мы пытаемся получить значение int_diff как ненулевое, чтобы значение int_diff также получает знак diff.

вот пара бит-twiddling методы, чтобы получить максимум из двух целых значений:

Способ 1

int max1(int a, int b) {
  static const size_t SIGN_BIT_SHIFT = sizeof(a) * 8 - 1;
  int mask = (a - b) >> SIGN_BIT_SHIFT;
  return (a & ~mask) | (b & mask);
}

объяснение:

  • (a - b) >> SIGN_BIT_SHIFT - If a > b затем a - b положительно, таким образом, бит знака 0, и маска 0x00.00. В противном случае, a < b так a - b отрицательно, бит знака 1 и после переключения, мы получаем маску 0xFF..FF
  • (a & ~маска) - Если маска 0xFF..FF, потом ~mask is 0x00..00 и тогда это значение 0. В противном случае, ~mask is 0xFF..FF и значение a
  • (B & mask) - если маска 0xFF..FF, потом b. В противном случае, mask is 0x00..00 и значение 0.

и наконец:

  • если a >= b затем a - b положительно, получаем max = a | 0 = a
  • если a < b затем a - b отрицательно, мы получаем max = 0 | b = b

Способ 2

int max2(int a, int b) {
  static const size_t SIGN_BIT_SHIFT = sizeof(a) * 8 - 1;
  int mask = (a - b) >> SIGN_BIT_SHIFT;
  return a ^ ((a ^ b) & mask);
}

объяснение:

  • объяснение маски такое же, как для Способ 1. Если a > b маска 0x00..00, в противном случае маска 0xFF..FF
  • если маска 0x00..00, потом (a ^ b) & mask is 0x00..00
  • если маска 0xFF..FF, потом (a ^ b) & mask is a ^ b

и наконец:

  • если a >= b, мы get a ^ 0x00..00 = a
  • если a < b, мы получим a ^ a ^ b = b

логику, описанную в задаче, можно объяснить так, как если 1-е число меньше, то 0 будет вычитаться, иначе разница будет вычитаться из 1-го числа, чтобы получить 2-е число. Я нашел еще одно математическое решение, которое, я думаю, немного проще понять эту концепцию.

рассматривая a и b как заданные числа

c=|a/b|+1;
d=(c-1)/b;
smallest number= a - d*(a-b);

опять же, идея состоит в том, чтобы найти k, который увядает 0 или 1 и умножить его на разность двух чисел.И, наконец, это число должно быть вычитается из 1-го числа, чтобы получить меньшее из двух чисел. P. S. Это решение будет не в случае 2-го числа равна нулю


int a=151;
int b=121;
int k=Math.abs(a-b);
int j= a+b;
double k1=(double)(k);
double j1= (double) (j);
double c=Math.ceil(k1/2) + Math.floor(j1/2);
int c1= (int) (c);
System.out.println(" Max value = " + c1);

(a / b) * b + (b/a) * a - (a/b)*(b / a)*a + (abs(a - b) % a) % b


есть один способ

 public static int Min(int a, int b)
  {
   int dif = (int)(((uint)(a - b)) >> 31);
   return a * dif + b * (1 - dif);
  }

и

return (a>=b)?b:a;

думаю, мы можем просто умножить числа с их побитовыми сравнениями, например:

int max=(a>b)*a+(a<=b)*b;