web-dev-qa-db-fra.com

Expliquez cet extrait qui trouve le maximum de deux entiers sans utiliser if-else ou tout autre opérateur de comparaison?

Trouvez le maximum de deux nombres. Vous ne devez pas utiliser if-else ou tout autre opérateur de comparaison. J'ai trouvé cette question sur le tableau d'affichage en ligne, donc je pensais que je devrais demander à StackOverflow

EXEMPLE Entrée: 5, 10 Sortie: 10

J'ai trouvé cette solution, quelqu'un peut-il m'aider à comprendre ces lignes de code

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

Disséquons ceci. Cette première ligne semble être simple: elle stocke la différence de a et b. Cette valeur est négative si a < b et est non négative sinon. Il y a en fait un bogue ici - si la différence entre les nombres a et b est si grande qu'elle ne peut pas tenir dans un entier, cela conduira à un comportement indéfini - oups! Alors supposons que cela n'arrive pas ici.

Dans la ligne suivante, qui est

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

l'idée est de vérifier si la valeur de c est négative. Dans pratiquement tous les ordinateurs modernes, les nombres sont stockés dans un format appelé complément de deux dans lequel le bit le plus élevé du nombre est 0 si le nombre est positif et 1 si le nombre est négatif. De plus, la plupart des ints sont en 32 bits. (c >> 31) décale le nombre de 31 bits, laissant le bit le plus élevé du nombre à la place du bit le plus bas. La prochaine étape, qui consiste à prendre ce nombre et à le remplacer par 1 (dont la représentation binaire est égale à 0 partout sauf le dernier bit), efface tous les bits les plus élevés et vous donne simplement le bit le plus bas. Étant donné que le bit le plus bas de c >> 31 est le bit le plus élevé de c, il lit le bit le plus élevé de c comme étant 0 ou 1. Étant donné que le bit le plus élevé est 1 iff c est 1, cela permet de vérifier si c est négatif (1) ou positif (0). En combinant ce raisonnement avec le précédent, k est égal à 1 si a < b et à 0 sinon.

La dernière étape consiste à faire ceci:

int max = a - k * c;

Si a < b, alors k == 1 et k * c = c = a - b, et ainsi de suite

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

Quel est le bon max, depuis a < b. Sinon, si a >= b, alors k == 0 et

a - k * c = a - 0 = a

Quel est également le correct max.

116
templatetypedef

C'est parti: (a + b) / 2 + |a - b| / 2

28
mike.dld

Utilisez des hacks au niveau des bits

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

Si vous savez que INT_MIN <= x - y <= INT_MAX,, vous pouvez utiliser ce qui suit, ce qui est plus rapide car (x - y) ne doit être évalué qu'une seule fois.

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

Source: Bit Twiddling Hacks de Sean Eron Anderson

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

Ceci est basé sur la même technique que la solution de mike.dld , mais c'est moins "évident" ici ce que je fais. Une opération "abs" donne l'impression que vous comparez le signe de quelque chose, mais je profite ici du fait que sqrt () vous retournera toujours la racine carrée positive. enracinant à nouveau, en ajoutant a + b et en divisant par 2.

Vous verrez que cela fonctionne toujours: par exemple, l'exemple utilisateur de 10 et 5 vous donne sqrt (100 + 25 - 100) = 5, puis ajoutez 10 et 5 vous donne 20 et divisez par 2 vous donne 10.

Si nous utilisons 9 et 11 comme nombres, nous aurions (sqrt (121 + 81 - 198) + 11 + 9)/2 = (sqrt (4) + 20)/2 = 22/2 = 11

11
CashCow

La réponse la plus simple est ci-dessous. 

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

Cette solution évite la multiplication . M sera soit 0x00000000 ou 0xffffffff

4
vikky.rk

En utilisant l'idée changeante pour extraire le signe tel que posté par d'autres, voici une autre manière:

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

Cela pousse les deux nombres dans un tableau avec le nombre maximum donné par l'élément de tableau dont l'index est le bit de signe de la différence entre les deux nombres.

Notez que:

  1. La différence (a - b) peut déborder.
  2. Si les nombres ne sont pas signés et que l'opérateur >> fait référence à un décalage logique à droite, le & 1 est inutile.
3
Ani

Voici comment je pense que je ferais le travail. Ce n'est pas aussi lisible que vous le voudriez, mais quand vous commencez par "comment puis-je faire X sans utiliser la façon évidente de le faire, vous devez vous attendre à ce que . En théorie, cela donne aussi une certaine portabilité, mais il faudrait trouver un système assez inhabituel pour voir un problème.

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

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

Cela présente certains avantages par rapport à celui présenté dans la question. Tout d'abord, il calcule la taille correcte du décalage, au lieu d'être codé en dur pour les ints 32 bits. Deuxièmement, avec la plupart des compilateurs, nous pouvons nous attendre à ce que toute la multiplication se produise au moment de la compilation, de sorte que tout ce qui reste au moment de l'exécution est une manipulation de bits triviale (soustraction et décalage) suivie d'un chargement et d'un retour. En bref, il est presque certain que ce sera assez rapide, même sur le plus petit microcontrôleur, où la multiplication utilisée à l'origine devait se produire au moment de l'exécution. Ainsi, même si c'est probablement assez rapide sur un ordinateur de bureau, ce sera souvent assez difficile. peu plus lent sur un petit microcontrôleur.

3
Jerry Coffin

Voici ce que font ces lignes:

c est a-b. si c est négatif, a <b.

k est le 32ème bit de c qui est le bit de signe de c (en supposant que les entiers 32 bits. Si cela est fait sur une plate-forme avec des entiers 64 bits, ce code ne fonctionnera pas). Il est décalé de 31 bits vers la droite pour supprimer les 31 bits les plus à droite, en laissant le bit de signe à l'endroit le plus à droite, puis en appuyant sur 1 pour supprimer tous les bits à gauche (qui sera rempli avec 1 si c est négatif). Donc, k sera 1 si c est négatif et 0 si c est positif.

Alors max = a - k * c. Si c est 0, cela signifie a> = b, donc max est a - 0 * c = a. Si c vaut 1, cela signifie que a <b et ensuite a - 1 * c = a - (a - b) = a - a + b = b.

Dans l'ensemble, il suffit d'utiliser le bit de signe de la différence pour éviter d'utiliser des opérations supérieures à ou inférieures aux opérations. C'est honnêtement un peu ridicule de dire que ce code n'utilise pas de comparaison. c est le résultat de la comparaison de a et b. Le code n'utilise tout simplement pas d'opérateur de comparaison. Vous pouvez faire la même chose dans de nombreux codes d'assemblage en soustrayant simplement les nombres, puis en sautant en fonction des valeurs définies dans le registre d'état.

Je devrais également ajouter que toutes ces solutions supposent que les deux nombres sont des nombres entiers. S'il s'agit de flottants, de doubles ou de quelque chose de plus compliqué (BigInts, Rational numbers, etc.), vous devez utiliser un opérateur de comparaison. Les astuces ne conviennent généralement pas à ceux-là.

2
Keith Irwin

fonction getMax () sans opération logique -

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

Explication:

Permet de briser le 'max' en morceaux,

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

Donc, la fonction devrait ressembler à ceci-

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

À présent,

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 nombre entier positif, le premier bit (bit de signe) est - 0 ; en négatif c'est- 1 . En décalant les bits vers la droite (>>), le premier bit peut être capturé.

Pendant le décalage à droite, l’espace vide est rempli par le bit de signe. So 01110001 >> 2 = 00011100 , alors que 10110001 >> 2 = 11101100 .

En conséquence, pour un décalage de nombre de 8 bits, 7 bits produisent soit - 1 1 1 1 1 1 1 [0 ou 1] pour négatif, ou 0 0 0 0 0 0 0 [0 ou 1] pour le positif.

Maintenant, si l’opérationOUest effectuée avec 00000001 (= 1) , le nombre négatif donne- 11111111 (= -1) et positif- 00000001 (= 1) .

Alors,

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 )

Finalement,

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

Le code que je fournis est pour trouver le maximum entre deux nombres, les nombres peuvent être de n'importe quel type de données (entier, flottant). Si les nombres entrés sont égaux, la fonction renvoie le nombre.

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

La description

  • La première chose que la fonction prend les arguments comme double et a le type de retour comme double. La raison en est que pour créer une fonction unique pouvant trouver le maximum pour tous les types. Lorsque des nombres de type entier sont fournis ou que l’un est un entier et que l’autre est le nombre à virgule flottante, la fonction peut également être utilisée pour la conversion implicite également pour trouver le nombre maximal d’entiers.
  • La logique de base est simple, supposons que nous avons deux nombres a & b si ab> 0 (c’est-à-dire que la différence est positive) alors a est maximum sinon si ab = 0 alors les deux sont égaux et si ab <0 (c’est-à-dire que diff est - ve) b est maximum.
  • Le bit de signe est enregistré en tant que bit le plus significatif (MSB) dans la mémoire. Si MSB est 1 et vice-versa. Pour vérifier si le bit de poids fort est égal à 1 ou à 0, nous le déplaçons sur la position du bit de poids faible et bit à bit & avec 1, si le résultat est 1, le nombre est -ve sinon non. est + ve. Ce résultat est obtenu par la déclaration:

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

Ici, pour obtenir le bit de signe du bit de plus fort poids en bit de poids faible, nous le décalons à droite sur k-1 bits (où k est le nombre de bits nécessaires pour enregistrer un nombre entier dans la mémoire, qui dépend du type de système). Ici k = sizeof (int) * 8 comme sizeof () donne le nombre d'octets nécessaires pour sauvegarder un entier pour obtenir non. de bits, nous le multiplions par 8. Après le décalage à droite, nous appliquons le bitwise & avec 1 pour obtenir le résultat.

  • Maintenant, après avoir obtenu le résultat (supposons-le comme r) 1 (for -ve diff) et 0 (for + ve diff), nous multiplions le résultat par la différence des deux nombres, la logique est donnée comme suit:

    1. si a> b, alors a-b> 0, c’est-à-dire, donc le résultat est 0 (c.-à-d., r = 0). Donc a- (a-b) * r => a- (a-b) * 0, ce qui donne «a» comme maximum.
    2. si a <b, alors a-b <0, c’est -ve, donc le résultat est 1 (c.-à-d. r = 1). Donc a- (a-b) * r => a- (a-b) * 1 => a-a + b => b, ce qui donne «b» comme maximum.
  • Maintenant, il reste deux points 1. l'utilisation de la boucle while et 2. la raison pour laquelle j'ai utilisé la variable 'int_diff' en tant qu'entier. Pour répondre correctement à ces questions, nous devons comprendre certains points:

    1. Les valeurs de type flottant ne peuvent pas être utilisées en tant qu'opérande pour les opérateurs au niveau du bit.
    2. En raison de la raison ci-dessus, nous devons obtenir la valeur dans une valeur entière pour obtenir le signe de la différence en utilisant des opérateurs au niveau du bit. Ces deux points décrivent le besoin de la variable 'int_diff' en tant que type entier.
    3. Supposons maintenant que nous trouvions la différence dans la variable 'diff'. Il existe maintenant 3 possibilités pour les valeurs de 'diff' quel que soit le signe de ces valeurs. (une). | diff |> = 1, (b). 0 <| diff | <1, (c). | diff | == 0.
    4. Lorsque nous attribuons une valeur double à la variable entière, la partie décimale est perdue.
    5. Dans le cas (a), la valeur de 'int_diff'> 0 (c'est-à-dire 1,2, ...). Pour les deux autres cas, int_diff = 0.
    6. La condition (temp_diff-int_diff) || 0.0 vérifie si diff == 0 afin que les deux nombres soient égaux.
    7. Si diff! = 0, nous vérifions si int_diff | 0 est vrai, c'est-à-dire que la casse (b) est vraie
    8. Dans la boucle while, nous essayons d'obtenir la valeur de int_diff comme non nulle, de sorte que la valeur de int_diff ait aussi le signe de diff.
0
ashesh

// En C #, vous pouvez utiliser la bibliothèque mathématique pour exécuter une fonction min ou max.

en utilisant le système;

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

}

}

statique 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 then (ab) sera négatif, le signe retournera -1, en ajoutant 1, nous obtenons l'indice 0 qui est b, si b = a alors ab sera 0, +1 donnera 1 index donc peu importe si nous retournons a ou b, quand a> b alors ab sera positif et le signe retournera 1, en ajoutant 1, nous obtenons l'index 2 où a est stocké. 

0
Raj Saraf

Voici quelques méthodes bit-twiddling pour obtenir le maximum de deux valeurs intégrales:

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

Explication:

  • (a - b) >> SIGN_BIT_SHIFT - Si a > b, alors a - b est positif, le bit de signe est donc 0 et le masque est 0x00.00. Sinon, a < b donc a - b est négatif, le bit de signe est 1 et après déplacement, nous obtenons un masque de 0xFF..FF
  • (a & ~ mask) - Si le masque est 0xFF..FF, alors ~mask est 0x00..00 et cette valeur est 0. Sinon, ~mask est 0xFF..FF et la valeur est a
  • (b & mask) - Si le masque est 0xFF..FF, alors cette valeur est b. Sinon, mask est 0x00..00 et la valeur est 0.

Finalement:

  • Si a >= b alors a - b est positif, nous obtenons max = a | 0 = a
  • Si a < b alors a - b est négatif, nous obtenons max = 0 | b = b

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

Explication:

  • L'explication de masque est la même que pour Méthode 1 . Si a > b le masque est 0x00..00, sinon le masque est 0xFF..FF
  • Si le masque est 0x00..00, alors (a ^ b) & mask est 0x00..00
  • Si le masque est 0xFF..FF, alors (a ^ b) & mask est a ^ b

Finalement:

  • Si a >= b, nous obtenons a ^ 0x00..00 = a
  • Si a < b, nous obtenons a ^ a ^ b = b
0
Daniel Trugman