Подтвердить что ты не робот

Объясните этот фрагмент, который находит максимум два целых числа без использования 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;  
}
4b9b3361

Ответ 1

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 настолько велика, что не может вписаться в целое число, это приведет к поведению undefined - oops! Поэтому допустим, что этого не происходит.

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

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

идея состоит в том, чтобы проверить, является ли значение c отрицательным. Практически во всех современных компьютерах цифры хранятся в формате, называемом двумя дополнениями, в котором старший бит числа равен 0, если число положительное и 1, если число отрицательное. Более того, большинство int 32 бит. (c >> 31) сдвигает число вниз на 31 бит, оставляя наивысший бит числа в пятне для младшего бита. Следующий шаг для этого числа и ANDing с 1 (чье двоичное представление равно 0 всюду, кроме последнего) стирает все более высокие биты и просто дает вам самый младший бит. Так как младший бит c >> 31 является самым старшим битом c, это означает, что старший бит c равен 0 или 1. Поскольку старший бит равен 1, если f 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

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

a - k * c = a - 0 = a

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

Ответ 2

Здесь мы идем: (a + b) / 2 + |a - b| / 2

Ответ 3

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

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 Hacks от Шона Эрона Андерсона

Ответ 4

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

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

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

Если мы будем использовать 9 и 11 в качестве наших чисел, мы получим (sqrt (121 + 81 - 198) + 11 + 9)/2 = (sqrt (4) + 20)/2 = 22/2 = 11

Ответ 5

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

#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);
}

Ответ 6

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

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

Ответ 7

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

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

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

Заметьте, что:

  • Разница (a - b) может переполняться.
  • Если числа без знака и оператор >> ссылается на логический сдвиг вправо, & 1 не требуется.

Ответ 8

Вот, как я думаю, я бы сделал эту работу. Это не так читаемо, как вам может понравиться, но когда вы начинаете с "как мне сделать 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. Во-вторых, с большинством компиляторов мы можем ожидать, что всякое умножение произойдет во время компиляции, поэтому все, что осталось во время выполнения, - это тривиальная манипуляция бит (вычитание и сдвиг), за которой следуют загрузка и возврат. Короче говоря, это почти наверняка будет довольно быстрым даже на самом маленьком микроконтроллере, где оригинальное используемое умножение, которое должно было произойти во время выполнения, поэтому, хотя это, вероятно, довольно быстро на настольной машине, часто будет довольно бит медленнее на небольшом микроконтроллере.

Ответ 9

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

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

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

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

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

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

Ответ 10

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

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

Пояснение:

Позволяет разбить "max" на куски,

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

Ответ 11

static 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, то (ab) будет отрицательным, знак вернет -1, добавив 1, мы получим индекс 0, который является b, если b = a, тогда ab будет 0, +1 даст 1 индекс, чтобы он неважно, вернем ли мы a или b, когда a > b тогда ab будет положительным, а знак вернет 1, добавив 1, мы получим индекс 2, где a сохраняется.

Ответ 12

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

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. Причина этого заключается в том, что для создания единственной функции, которая может найти максимум для всех типов. Когда заданы числа целочисленного типа, или одно целое, а другое - это плавающая точка, то также из-за неявного преобразования функция также может использоваться для нахождения max для целых чисел.
  • Базовая логика проста: допустим, что мы имеем два числа a и b, если ab > 0 (т.е. разность положительна), тогда a является максимумом else, если ab == 0, то оба равны, и если a-b < 0 (т.е. diff - -ve) b максимум.
  • Знак бит сохраняется как самый значительный бит (MSB) в памяти. Если MSB равен 1 и наоборот. Чтобы проверить, является ли MSB равным 1 или 0, мы переносим MSB в позицию LSB и побитовое и с 1, если результат равен 1, тогда число равно -ve else no. + ve. Этот результат получается из утверждения:

    int_diff → (sizeof (int) * 8 - 1) и 1

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

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

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

    • Значения плавающего типа не могут использоваться в качестве операнда для побитовых операторов.
    • Из-за вышеизложенного нам нужно получить значение в целочисленном значении, чтобы получить знак различия, используя побитовые операторы. Эти две точки описывают необходимость переменной 'int_diff' как целочисленный тип.
    • Теперь скажем, что мы находим разницу в переменной "diff", теперь есть три возможности для значений "diff" независимо от знака этих значений. (А). | diff | >= 1, (b). 0 < | diff | < 1, (c). | Дифф |. == 0
    • Когда мы назначаем двойное значение целочисленной переменной, десятичная часть теряется.
    • Для случая (a) значение 'int_diff' > 0 (т.е. 1,2,...). Для других двух случаев int_diff = 0.
    • Условие (temp_diff-int_diff) || 0.0 проверяет, если diff == 0, поэтому оба числа равны.
    • Если diff!= 0, то мы проверяем, истинно ли int_diff | 0, т.е. case (b) истинно
    • В цикле while мы пытаемся получить значение int_diff как ненулевое, так что значение int_diff также получает знак diff.

Ответ 13

#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");
}

Ответ 14

Используются два трюка: трюк целочисленного представления и математический.

Математический трюк

Для данного a и b утверждение a < b означает, что a - b < 0. Пусть у нас есть функция единственного аргумента is_negative(x), которая возвращает 1, если аргумент отрицательный и нуль в противном случае (обратите внимание, что is_negative(0) = 0). Тогда функция max(a, b) может быть определена как max(a, b) = a - is_negative(a - b)*(a - b).

Proof

Возможны три случая: a < b, a > b, a = b.

  • Если a < b, то a - b < 0, а затем is_negative(a - b) есть 1. Следовательно, max = a - 1*(a - b) = b. Итак, это b, как и ожидалось.
  • Если a > b, то a - b > 0, а is_negative(a - b) - 0. Следовательно, max = a - 0*(a - b) = a. Это a.
  • Если a = b, то a - b равно нулю, а is_negative(a - b) равно 0. Итак, max a.

Итак, для всех случаев max работает так, как ожидалось.

Интеллектуальный трюк представления

Как определить is_negative(x) без использования ветвления? Тот факт, что большинство современных архитектур использует Приходит на ум две аплодисмента. Итак, самый первый бит целого числа должен хранить знак. Например, здесь представлено -1:

11111111111111111111111111111111 -> this is -1
^
|
+- This bit represents sign.

В принципе, если целое число отрицательное, бит устанавливается, в противном случае - ноль. Для 32 битового целого x знак можно получить:

(x >> 31) & 0x1;

x >> 31 не циклически сдвигает биты вправо на позицию 31. Non roundly означает, что бит с левой стороны равен нулю, а последний бит - подписан. В основном -1 становится 1:

11111111111111111111111111111111 // before shift
00000000000000000000000000000001 // after  shift

(Биты смещены вправо, нули дополняются слева)

По некоторым причинам автор фрагмента кода дважды очистил 0-31 бит на & 0x1.

Сказав это, is_negative(x) = (x >> 31) & 1.

Почему это неправильно

Слабая часть здесь - целочисленная арифметика. Целочисленная арифметика является модульной, поэтому это означает, что MIN_INT - 1 равно нулю! И мы использовали предположение, что a - b = 0 означает, что a = b, а это не так. Таким образом, алгоритм прерывается в случае переполнения целых чисел.

Ответ 15

Вот несколько методов бит-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 - Если a > b, то a - b положителен, поэтому знаковый бит 0, а маска - 0x00.00. В противном случае a < b, поэтому a - b отрицательно, бит знака 1 и после смещения мы получаем маску 0xFF..FF
  • (a и ~ mask). Если маска 0xFF..FF, то ~mask - 0x00..00, а затем это значение 0. В противном случае ~mask 0xFF..FF, а значение a
  • (b и маска). Если маска 0xFF..FF, то это значение b. В противном случае mask есть 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 есть 0x00..00
  • Если маска 0xFF..FF, то (a ^ b) & mask есть a ^ b

Наконец:

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

Ответ 16

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);

Ответ 17

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

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

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

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

Ответ 18

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

Ответ 19

Существует один способ

 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;

Ответ 20

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

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