Explicar este fragmento que encuentra el máximo de dos enteros sin utilizar if-else o cualquier otro operador de comparación?


Encuentre el máximo de dos números. No debe usar if-else ni ningún otro operador de comparación. Encontré esta pregunta en el tablón de anuncios en línea, así que pensé que debería preguntar en StackOverflow

EJEMPLO Entrada: 5, 10 Salida: 10

Encontré esta solución, puede alguien ayudarme a entender estas líneas de código

int getMax(int a, int b) {  
    int c = a - b;  
    int k = (c >> 31) & 0x1;  
    int max = a - k * c;  
    return max;  
}
Author: smci, 2011-01-23

19 answers

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

Vamos a diseccionar esto. Esta primera línea parece ser sencilla-almacena la diferencia de a y b. Este valor es negativo si a < b y no es negativo de lo contrario. En realidad hay un error aquí-si la diferencia de los números a y b es tan grande que no puede caber en un entero, esto conducirá a un comportamiento indefinido-oops! Así que asumamos que eso no sucede aquí.

En la siguiente línea, que es

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

La idea es comprobar si el valor de c es negativo. En prácticamente todas las computadoras modernas, los números se almacenan en un formato llamado complemento de dos en el que el bit más alto del número es 0 si el número es positivo y 1 si el número es negativo. Además, la mayoría de los INT son de 32 bits. (c >> 31) desplaza el número hacia abajo 31 bits, dejando el bit más alto del número en el lugar para el bit más bajo. El siguiente paso de tomar este número y ANDing con 1 (cuya representación binaria es 0 en todas partes excepto el último pedacito) borra todos los pedacitos más altos y apenas le da el pedacito más bajo. Dado que el bit más bajo de c >> 31 es el bit más alto de c, esto lee el bit más alto de c como 0 o 1. Dado que el bit más alto es 1 iff c es 1, esta es una forma de verificar si c es negativo (1) o positivo (0). Combinando este razonamiento con el anterior, k es 1 si a < b y es 0 de lo contrario.

El paso final es hacer esto:

int max = a - k * c;

Si a < b, entonces k == 1 y k * c = c = a - b, y so

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

Que es el máximo correcto, desde a < b. De lo contrario, si a >= b, entonces k == 0 y

a - k * c = a - 0 = a

Que también es el max.

 111
Author: templatetypedef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-18 00:44:12

Aquí vamos: (a + b) / 2 + |a - b| / 2

 28
Author: mike.dld,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-23 07:41:47

Use hacks a nivel de bits

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

Si sabe que INT_MIN <= x - y <= INT_MAX, entonces puede usar lo siguiente, que es más rápido porque (x - y) solo necesita ser evaluado una vez.

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

Fuente: Bit Twiddling Hacks por Sean Eron Anderson

 19
Author: Prasoon Saurav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-30 18:39:14
(sqrt( a*a + b*b - 2*a*b ) + a + b) / 2

Esto se basa en la misma técnica que mike.solución de dld, pero es menos "obvio" aquí lo que estoy haciendo. Una operación" abs " parece que estás comparando el signo de algo, pero aquí estoy aprovechando el hecho de que sqrt() siempre te devolverá la raíz cuadrada positiva, por lo que estoy cuadrando (a-b) escribiéndolo en su totalidad y luego enraizándolo de nuevo, agregando a+b y dividiendo por 2.

Verá que siempre funciona: por ejemplo, el ejemplo del usuario de 10 y 5 obtiene sqrt(100 + 25 - 100) = 5 luego agrega 10 y 5 te da 20 y divide por 2 te da 10.

Si usamos 9 y 11 como nuestros números nos gustaría conseguir (sqrt(121 + 81 - 198) + 11 + 9)/2 = (sqrt(4) + 20) / 2 = 22/2 = 11

 11
Author: CashCow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:23

La respuesta más simple está abajo.

#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
Author: novice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-03 03:41:17
int max(int i, int j) {
    int m = ((i-j) >> 31);
    return (m & j) + ((~m) & i);
}

Esta solución evita la multiplicación. m será 0x00000000 o 0xffffffff

 4
Author: vikky.rk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-10 19:09:14

Usando la idea cambiante para extraer el signo tal como fue publicado por otros, aquí hay otra manera:

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

Esto empuja los dos números en una matriz con el número máximo dado por el elemento de matriz cuyo índice es el bit de signo de la diferencia entre los dos números.

Tenga en cuenta que:

  1. La diferencia (a - b) puede desbordarse.
  2. Si los números están sin signo y el operador >> se refiere a un lógico desplazamiento a la derecha, el & 1 es innecesario.
 3
Author: Ani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-23 08:19:13

Así es como creo que haría el trabajo. No es tan legible como te gustaría, pero cuando empiezas con "cómo hago X sin usar la forma obvia de hacer X, tienes que esperar eso. En teoría, esto también renuncia a algo de portabilidad, pero tendría que encontrar un sistema bastante inusual para ver un problema.

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

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

Esto tiene algunas ventajas sobre la que se muestra en la pregunta. En primer lugar, calcula el tamaño correcto de shift, en lugar de ser codificado para ints de 32 bits. En segundo lugar, con la mayoría de los compiladores podemos esperar que toda la multiplicación ocurra en tiempo de compilación, por lo que todo lo que queda en tiempo de ejecución es una manipulación trivial de bits (restar y cambiar) seguida de una carga y retorno. En resumen, esto es casi seguro que será bastante rápido, incluso en el microcontrolador más pequeño, donde la multiplicación original utilizada tuvo que ocurrir en tiempo de ejecución, así que aunque es probablemente bastante rápido en una máquina de escritorio, a menudo será bastante más lento en un microcontrolador pequeño.

 3
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-20 23:12:49

Esto es lo que están haciendo esas líneas:

C es a-b. si c es negativo, a

K es el bit 32 de c que es el bit de signo de c (suponiendo enteros de 32 bits. Si se hace en una plataforma con enteros de 64 bits, este código no funcionará). Se ha desplazado 31 bits a la derecha para eliminar los 31 bits más a la derecha dejando el bit de signo en el lugar más a la derecha y luego andándolo con 1 para eliminar todos los bits a la izquierda (que se llenará con 1s si c es negativo). Así que k será 1 si c es negativo y 0 si c es positivo.

Entonces max = a - k * c. Si c es 0, esto significa que a>=b, por lo que max es un - 0 * c = a. Si c es 1, esto significa que a

En general, solo se usa el bit de signo de la diferencia para evitar usar operaciones mayores o menores que. Honestamente, es un poco tonto decir que este código no usa una comparación. c es el resultado de comparar a y b. El código simplemente no utiliza un operador de comparación. Podrías haga una cosa similar en muchos códigos de ensamblaje simplemente restando los números y luego saltando en función de los valores establecidos en el registro de estado.

También debo añadir que todas estas soluciones están asumiendo que los dos números son enteros. Si son flotadores, dobles, o algo más complicado(BigInts, Números racionales, etc.) entonces usted realmente tiene que utilizar un operador de comparación. Los trucos de bits generalmente no sirven para eso.

 2
Author: Keith Irwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-23 09:01:19

Función GetMax() Sin Ninguna Operación Lógica-

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

Explicación:

Vamos a romper el 'max' en pedazos,

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

Así que la función debería verse así-

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

Ahora,

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

En número entero positivo el primer bit (bit de signo) es- 0; en negativo es- 1. Desplazando bits a la derecha ( > > ) se puede capturar el primer bit.

Durante el desplazamiento a la derecha, el espacio vacío se llena con el signo trozo. Así 01110001 >> 2 = 00011100, mientras 10110001 >> 2 = 11101100.

Como resultado, para la 8 bits de número de cambio de 7 bits va a producir- 1 1 1 1 1 1 1 [0 o 1] para el negativo, o 0 0 0 0 0 0 0 [0 o 1] positivos.

Ahora, si O operación se realiza con 00000001 (= 1), número negativo de los rendimientos- 11111111 (= -1), y positivo- 00000001 (= 1).

Así que,

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 )

Finalmente,

getMax(a, b)
= ( a + b + absolute(a - b) ) / 2
= ( a + b + ((a-b) * ( ( (a-b) >> ((sizeOf(int)*8) - 1) ) | 1 )) ) / 2
 1
Author: Minhas Kamal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-31 03:15:22

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

    }

Si b > a entonces (a-b) será negativo, el signo devolverá -1, agregando 1 obtenemos el índice 0 que es b, si b=a entonces a-b será 0, +1 dará 1 índice por lo que no importa si estamos devolviendo a o b, cuando a > b entonces a-b será positivo y el signo devolverá 1, agregando 1 obtenemos el índice 2 donde se almacena a.

 0
Author: Raj Saraf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-25 13:23:17
#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
Author: Chirag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-26 10:25:54

El código que estoy proporcionando es para encontrar máximo entre dos números, los números pueden ser de cualquier tipo de datos(entero, flotante). Si los números de entrada son iguales, la función devuelve el número.

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

Descripción

  • Lo primero que la función toma los argumentos como double y tiene return type como double. La razón de esto es que para crear una sola función que puede encontrar el máximo para todos los tipos. Cuando se proporcionan números de tipo entero o uno es un entero y otro es el punto flotante entonces también debido a la conversión implícita la función se puede utilizar para encontrar el máximo para los enteros también.
  • La lógica básica es simple, digamos que tenemos dos números a & b si a-b>0(es decir, la diferencia es positiva) entonces a es máximo si a-b==0 entonces ambos son iguales y si a-b
  • El bit de signo se guarda como el Bit más Significativo(MSB) en la memoria. Si MSB es 1 y viceversa. Para comprobar si MSB es 1 o 0 desplazamos el MSB a la posición LSB y Bitwise & con 1, si el resultado es 1 entonces el número es-ve else no. is + ve. Este resultado se obtiene por la declaración:

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

Aquí para obtener el bit de signo del MSB al LSB lo cambiamos a k-1 bits(donde k es el número de bits necesarios para guardar un número entero en la memoria que depende del tipo de sistema). Aquí k = sizeof (int) * 8 como sizeof () da el número de bytes necesarios para guardar un entero para obtener no. de bits, lo multiplicamos por 8. Después del cambio correcto, aplicamos el bitwise & with 1 para obtener el resultado.

  • Ahora después de obtener el resultado(asumámoslo como r) como 1(for-ve diff) y 0(for +ve diff) multiplicamos el resultado con la diferencia de los dos números, la lógica se da de la siguiente manera:

    1. si a>b entonces a-b>0 es decir, es +ve por lo que el resultado es 0(es decir, r=0). Así, a-(a-b)*r => a- > (a-b)*0, que da 'a' como máximo.
    2. si a a- > (a-b)*1 => a- > a+b =>b , que da a 'b' como máximo.
  • Ahora quedan dos puntos 1. el uso del bucle while y 2. por qué he usado la variable 'int_diff' como un entero. Para responder a estos correctamente tenemos que entender algunos puntos:

    1. Los valores de tipo flotante no se pueden usar como operando para los operadores de bits.
    2. Debido a la razón anterior, necesitamos obtener el valor en un valor entero para obtener el signo de diferencia mediante el uso de operadores bitwise. Estos dos puntos describen la necesidad de la variable 'int_diff' como tipo entero.
    3. Ahora digamos que encontramos la diferencia en la variable 'diff' ahora hay 3 posibilidades para los valores de 'diff' independientemente del signo de estos valores. (un). / diff / > = 1, (b). 0
    4. Cuando asignamos un valor doble a la variable integer la parte decimal se pierde.
    5. Para el caso (a) el valor de 'int_diff' >0 (es decir, 1, 2,...). Para otros dos casos int_diff=0.
    6. La condición (temp_diff-int_diff)||0.0 comprueba si diff==0 para que ambos números sean iguales.
    7. If diff!= 0 entonces comprobamos si int_diff / 0 es verdadero, es decir, case (b) es verdadero
    8. En el bucle while, tratamos de obtener el valor de int_diff como distinto de cero para que el valor de int_diff también obtenga el signo de diff.
 0
Author: ashesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-05 12:32:14

Aquí hay un par de bits-cambiando métodos para obtener el máximo de dos valores integrales:

Método 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);
}

Explicación:

  • (a - b) >> SIGN_BIT_SHIFT - Si a > b entonces a - b es positivo, por lo tanto el bit de signo es 0, y la máscara es 0x00.00. De lo contrario, a < b así que a - b es negativo, el bit de signo es 1 y después de cambiar, obtenemos una máscara de 0xFF..FF
  • (a & ~ mask) - Si la máscara es 0xFF..FF, entonces ~mask es 0x00..00 y entonces este valor es 0. De lo contrario, ~mask es 0xFF..FF y el valor es a
  • (b & mask) - Si la máscara es 0xFF..FF, entonces este valor es b. De lo contrario, mask es 0x00..00 y el valor es 0.

Finalmente:

  • Si a >= b entonces a - b es positivo, obtenemos max = a | 0 = a
  • Si a < b entonces a - b es negativo, obtenemos max = 0 | b = b

Método 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);
}

Explicación:

  • La explicación de la máscara es la misma que para Método 1. Si a > b la máscara es 0x00..00, de lo contrario la máscara es 0xFF..FF
  • Si la máscara es 0x00..00, entonces (a ^ b) & mask es 0x00..00
  • Si la máscara es 0xFF..FF, entonces (a ^ b) & mask es a ^ b

Finalmente:

  • Si a >= b, obtenemos a ^ 0x00..00 = a
  • Si a < b, obtenemos a ^ a ^ b = b
 0
Author: Daniel Trugman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-08 22:39:53

La lógica descrita en un problema se puede explicar como si el 1er número es menor, entonces se restará 0, de lo contrario, la diferencia se restará del 1er número para obtener el 2do número. Encontré una solución matemática más que creo que es un poco más simple de entender este concepto.

Considerando a y b como números dados

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

De nuevo,la idea es encontrar k que es marchitarse 0 o 1 y multiplicarlo con diferencia de dos números.Y finalmente este número debe restarse de 1st número para producir el más pequeño de los dos números. PD: esta solución fallará en caso de que el 2do número sea cero

 -2
Author: SwANDp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-25 06:18:59
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);
 -2
Author: Sujith Mohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-10 18:52:50

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

 -2
Author: Leonid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-24 20:39:01

Hay una manera

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

Y uno

return (a>=b)?b:a;
 -3
Author: user246340,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-01 11:22:30

Supongo que podemos multiplicar los números con sus comparaciones bit a bit por ejemplo:

int max=(a>b)*a+(a<=b)*b;
 -4
Author: krantboy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-12 01:59:53