解释此片段,该片段不使用if-else或任何其他比较运算符就可以找到两个整数的最大值?


78

查找两个数字的最大值。您不应使用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;  
}

1
我会考虑一下符号位作弊,因为这基本上与处理器的作用相同<
starblue 2011年

7
至少对于C ++,从5.8.3开始,讨论E1 >> E2:“如果E1具有带符号的类型和负值,则结果值是实现定义的。”,因此“ c >> 31”可能会或可能不会转移从most-到最低显著位符号位....
托尼德尔罗伊

1
而且也没有说第31位是符号位。
Antti Haapala

3
无论如何,这个问题要关闭,因为没有人读的问题文本,也不是C的标签
安蒂哈帕拉

只是因为没有其他人注意到它。这是变相形式在此处发现的著名的可笑黑客。
kaartic '17

Answers:


121
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且iffc是1,因此这是一种检查是否为c是负数(1)还是正数(0)。将此推理与上述内容结合使用,k如果为1 ,则为1 a < b,否则为0。

最后一步是这样做:

int max = a - k * c;

如果a < b,则k == 1k * 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

这也是正确的最高


7
所有这些都假设没有溢出。
彼得·泰勒

4
@templatetypedef,假设a = 0x80000000(int的最小值)且b =1。c是什么?
彼得·泰勒

4
@Peter Taylor-很好。请注意,我没有想到这个答案。我只是在解释OP的代码。:-)但您是正确的,如果数字相距太远,这会中断。
templatetypedef

1
@templatetypedef,我知道:当您发布答案时,我正在写一个非常相似的答案。我只是指出是为了OP的利益。
彼得·泰勒

3
@Ani,最高位移动到它经过的每个位置。另一种选择是max = a + (c >> 31) * c
Peter Taylor

28

开始了: (a + b) / 2 + |a - b| / 2


1
您知道背后的数学逻辑吗?
Senthil Kumaran

6
@ mike.did:您可以| a-b | 没有任何条件?
templatetypedef

8
假设绝对值是可用于此问题的运算符有点费力。从数学上来说,这是一个很好的答案,但我怀疑它会被接受。
基思·欧文

5
-1例如,整数错误(3 + 2) / 2 + |3 - 2| / 2 = 2 + 0 = 2 != 3
starblue 2011年

4
@starblue:((3 + 2)+ | 3-2 |)/ 2 = 3从这里看起来很漂亮。
Michael Foukarakis 2011年

19

使用按位黑客

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)

来源:Sean Eron Anderson的《 Bit Twiddling Hacks》


14
请注意,第一个建议打破了“无比较运算符”约束。
Matthieu M.

12
(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


a * a很容易溢出
Dvole

8

最简单的答案如下。

#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


4

使用转移的想法来提取他人张贴的标志,这是另一种方法:

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

这会将两个数字推入一个数组,该数组具有由array-element给出的最大数字,该数组元素的索引是两个数字之间的差的符号位。

请注意:

  1. 差异(a - b)可能会溢出。
  2. 如果数字是无符号的,并且>>操作员参考逻辑右移,& 1则不需要。

4

这就是我认为自己会做的事情。它的可读性不尽如人意,但是当您从“如何在不使用X的明显方式下做X的情况下开始时,您就必须对此有所期待。从理论上讲,这也放弃了某些可移植性,但是您d必须找到一个非常不寻常的系统才能看到问题。

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

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

确实比问题中显示的优点有一些优势。首先,它计算出正确的移位大小,而不是为32位整数硬编码。其次,对于大多数编译器,我们可以预期所有乘法都会在编译时发生,因此在运行时剩下的只是琐碎的位操作(减法和移位),然后是加载和返回。简而言之,即使在最小的微控制器上,这也肯定是相当快的,因为最小的微控制器上原始的乘法必须在运行时进行,因此虽然在台式机上可能相当快,但通常会相当快。在小型微控制器上慢一点。


2

这些行正在执行以下操作:

c是ab。如果c为负,则a <b。

k是c的第32位,它是c的符号位(假定为32位整数。如果在具有64位整数的平台上执行此代码将不起作用)。它向右移了31位,以删除最右边的31位,将符号位保留在最右边,然后再将其与1相加,以删除所有的位(如果c为负,则将用1填充)。因此,如果c为负,则k将为1;如果c为正,则k将为0。

那么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数等),那么您实际上必须使用比较运算符。比特技巧通常不会做到这些。


1

没有任何逻辑运算的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 1 [0或1]表示负数,或0 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 getMax(int a, int b){
    int i[] = {a, b};
    return i[( (i[0]-i[1]) >> (sizeof(int)*8 - 1) ) & 1 ];
}

0

静态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得到索引b为b,如果b = a则ab为0,+ 1将给出1索引,所以这无关紧要如果我们返回a或b,则当a> b时,ab将为正,符号将返回1,加1便得到存储a的索引2。


0
#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

我提供的代码用于查找两个数字之间的最大值,这些数字可以是任何数据类型(整数,浮点数)。如果输入数字相等,则该函数返回该数字。

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为- ve)b是最大值。
  • 符号位被另存为存储器中的最高有效位(MSB)。如果MSB为1,反之亦然。要检查MSB是1还是0,我们将MSB移到LSB位置,然后按位&移至1,如果结果为1,则数字为-ve否则为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),我们将结果乘以两个数字的差,逻辑如下:

    1. 如果a> b,则ab> 0,即+ ve,因此结果为0(即r = 0)。因此a-(ab)* r => a-(ab)* 0,它给出的最大值为'a'。
    2. 如果a <b,则ab <0,即-ve,因此结果为1(即r = 1)。所以a-(ab)* r => a-(ab)* 1 => a-a + b => b,这使'b'最大。
  • 现在剩下两个要点:1.使用while循环和2.为什么我使用变量'int_diff'作为整数。为了正确回答这些问题,我们必须了解一些要点:

    1. 浮点类型值不能用作按位运算符的操作数。
    2. 由于上述原因,我们需要使用整数运算符来获取整数值以获取差异符号。这两点描述了将变量“ int_diff”作为整数类型的必要性。
    3. 现在假设我们找到了变量“ diff”的差异,因为“ diff”的值存在3种可能性,而与这些值的符号无关。(一种)。| diff |> = 1,(b)。0 <| diff | <1(c)。| diff | == 0。
    4. 当我们给整数变量赋一个双精度值时,小数部分会丢失。
    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是否为true,即case(b)为true
    8. 在while循环中,我们尝试将int_diff的值设为非零,以便int_diff的值也获得diff的符号。

0

这是几种位旋转方法,以获取两个整数值的最大值:

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

说明:

  • (一-二)>> 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。否则~mask0xFF..FF和,值为a
  • (b和遮罩)-如果遮罩为0xFF..FF,则该值为b。否则,mask0x00..00,值为0

最后:

  • 如果a >= ba - b是肯定的,我们得到max = a | 0 = a
  • 如果a < ba - 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

//在C#中,您可以使用数学库执行最小或最大函数

使用系统;

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

}

}


小心:OP问题被标记为[c]而不是[c#]
Alex Yu

0

没有逻辑运算符,没有库(JS)

function (x, y) {
    let z = (x - y) ** 2;
    z = z ** .5;
    return (x + y + z) / 2
}

-2

一个问题中描述的逻辑可以解释为:如果第一个数字较小,则将减去0,否则将从第一个数字中减去差以获得第二个数字。我发现了另一个数学解决方案,我认为它更容易理解这个概念。

将a和b视为给定数字

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

再一次,我们的想法是找到0或1的k,然后将其乘以两个数字之差。最后,应从第一个数字中减去该数字,以得出两个数字中较小的一个。PS如果第二个数字为零,此解决方案将失败


-3

有一种方法

 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;

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

-4

猜猜我们可以将数字乘以按位比较,例如:

int max=(a>b)*a+(a<=b)*b;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.