it-roy-ru.com

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

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

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

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

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

116
templatetypedef

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

28
mike.dld

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

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)

Источник: Bit Twiddling Hacks от Шона Эрона Андерсона

19
Prasoon Saurav
(sqrt( a*a + b*b - 2*a*b ) + a + b) / 2

Это основано на той же технике, что и решение mike.dld , но здесь менее "очевидно", что я делаю. Операция «abs» выглядит так, как будто вы сравниваете знак чего-то, но я здесь пользуюсь тем фактом, что sqrt () всегда будет возвращать вам положительный квадратный корень, поэтому я возводю квадрат в квадрат (ab) и записываю его полностью, а затем укоренив его снова, добавив + 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

11
CashCow

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

#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);
}
8
novice
int max(int i, int j) {
    int m = ((i-j) >> 31);
    return (m & j) + ((~m) & i);
}

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

4
vikky.rk

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

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

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

Обратите внимание, что:

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

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

3
Jerry Coffin

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

с является а-б. если 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.

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

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

2
Keith Irwin

функция 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
1
Minhas Kamal
#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");
}
0
Chirag

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

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

    1. если a> b, то a-b> 0, т. е. равно + ve, поэтому результат равен 0 (т. е. r = 0). Таким образом, a- (a-b) * r => a- (a-b) * 0, что дает «a» в качестве максимума.
    2. если 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 в качестве целого числа. Чтобы ответить на них правильно, мы должны понять некоторые моменты:

    1. Значения плавающего типа не могут использоваться в качестве операнда для побитовых операторов.
    2. По вышеуказанной причине нам нужно получить значение в целочисленном значении, чтобы получить знак различия с помощью побитовых операторов. Эти две точки описывают необходимость переменной int_diff как целочисленного типа.
    3. Теперь допустим, что мы находим разницу в переменной diff, теперь есть 3 варианта значений diff независимо от знака этих значений. (А). | diff |> = 1, (б). 0 <| diff | <1, (c). | Дифф | == 0.
    4. Когда мы присваиваем двойное значение целочисленной переменной, десятичная часть теряется.
    5. Для случая (а) значение '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.
0
ashesh

// В C # вы можете использовать математическую библиотеку для выполнения функции min или max

используя Систему;

class NumberComparator {

static void Main()
{

    Console.Write(" write the first number to compare: ");
    double first_Number = double.Parse(Console.ReadLine());

    Console.Write(" write the second number to compare: ");
    double second_Number = double.Parse(Console.ReadLine());

    double compare_Numbers = Math.Max(first_Number, second_Number);
    Console.Write("{0} is greater",compare_Numbers);

}

}

0

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 будет положительным, а sign вернет 1, добавив 1, мы получим индекс 2, где хранится a. 

0
Raj Saraf

Вот пара методов bit-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
  • (маска & & -) - если маска 0xFF..FF, то ~mask равна 0x00..00, а затем это значение 0. В противном случае ~mask равен 0xFF..FF, а значение a
  • (b & mask) - если маска 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
0
Daniel Trugman