【问题标题】:Algorithm to find the most significant bit [duplicate]找到最高有效位的算法[重复]
【发布时间】:2013-06-06 08:16:26
【问题描述】:

我的一个朋友在一次采访中被问到以下问题:“给定一个二进制数,找到最高有效位”。我立即想到了以下解决方案,但不确定是否正确。

即,将字符串分成两部分,并将两部分转换为十进制。如果左子数组十进制为0,则在右子数组中进行二分查找,寻找1。

这是我的另一个问题。最高有效位是二进制数中最左边的 1 吗?你能给我举个例子,当 0 是最重要的位时,并给出一个例子和解释。

编辑:

下面的答案似乎有些混乱,因此我正在更新问题以使其更准确。面试官说“你有一个网站,你从那里接收数据,直到最高有效位指示停止传输数据”你会如何告诉程序停止数据传输”

【问题讨论】:

  • MSB在哪一边取决于你的CPU架构是Big-endian还是Little-endian。
  • 许多平台都有一个 CountLeadingZero 操作码可以非常快速地完成此操作。这对于除法和对数以 2 为底的数学很有用。
  • 例如:号码0。此外,您需要指定负数的表示方式。
  • 感谢大家的反馈,我并不是不尊重,但您所说的确实不能回答我的问题。我能问你更具体吗?谢谢
  • 大多数芯片组都有some sort of instruction 来执行此操作。

标签: algorithm search binary


【解决方案1】:

您也可以使用位移。伪代码:

number = gets
bitpos = 0
while number != 0
  bitpos++             # increment the bit position
  number = number >> 1 # shift the whole thing to the right once
end
puts bitpos

如果数字为零,则 bitpos 为零。

【讨论】:

  • 你的意思是while number != 0? (我不认为伪代码应该遵循 C 的特性。)
  • @svick 0 = 假且非零,因为我认为真早于 C
  • 我看不懂这个伪代码。想解释一下第 3、5 和 6 行代表什么?
  • 哈哈哈猜它不像我当时想象的那么普遍......@user2398832 #3: while number != 0, #5: shift the number to the right by 1 bit, #6: output bitpos
  • @user2398832:现在更清楚了吗?
【解决方案2】:

仅使用类 C 语言指令来查找单词中的最高有效位(即计算 log2 并向下舍入)可以通过使用基于 De Bruijn 序列的相当知名的方法来完成。例如,对于 32 位值

unsigned ulog2(uint32_t v)
{ /* Evaluates [log2 v] */
  static const unsigned MUL_DE_BRUIJN_BIT[] = 
  {
     0,  9,  1, 10, 13, 21,  2, 29, 11, 14, 16, 18, 22, 25,  3, 30,
     8, 12, 20, 28, 15, 17, 24,  7, 19, 27, 23,  6, 26,  5,  4, 31
  };

  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;

  return MUL_DE_BRUIJN_BIT[(v * 0x07C4ACDDu) >> 27];
}

然而,在实践中,更简单的方法(如展开的二分搜索)通常也能发挥同样的效果或更好。

【讨论】:

    【解决方案3】:

    编辑后的问题确实非常不同,虽然不是很清楚。你是谁”?网站或从网站读取数据的程序的程序员?如果您是网站,您可以通过发送一个设置了最高有效位的值(但什么,可能是一个字节?)来使程序停止。只是 OR 或 ADD 那个位。如果你是程序员,你测试你收到的值的最重要的位,并在它被设置时停止读取。对于无符号字节,您可以进行类似的测试

    bool stop = received_byte >= 128;
    or
    bool stop = received_byte & 128;
    

    对于有符号字节,您可以使用

    bool stop = received_byte < 0;
    or
    bool stop = received_byte & 128;
    

    如果您读取的不是字节而是 32 位字,则 128 将更改为 (1 &lt;&lt; 31)。

    【讨论】:

      【解决方案4】:

      这是一种方法(但不一定是最有效的,特别是如果您的平台有一个单指令解决方案来查找第一个或计数前导零或类似的东西),假设二进制补码有符号整数和32 位整数宽度。

      int mask = (int)(1U<<31); // signed integer with only bit 32 set
      while (! n & mask)        // n is the int we're testing against
        mask >>= 1;             // take advantage of sign fill on right shift of negative number
      mask = mask ^ (mask << 1) // isolate first bit that matched with n
      

      如果您想要第一个的位位置,只需添加一个从 31 开始并在每次循环迭代时递减的整数计数器。

      这样做的一个缺点是如果n == 0,它是一个无限循环,所以事先测试为零。

      【讨论】:

        【解决方案5】:

        如果您对 C/C++ 解决方案感兴趣,可以查看 "Matters Computational" by Jörg Arndt 这本书,其中在“1.6.1 隔离最高的一个并找到它的索引”一节中定义了这些函数:

        static inline ulong highest_one_idx(ulong x)
        // Return index of highest bit set.
        // Return 0 if no bit is set.
        {
        #if defined  BITS_USE_ASM
            return  asm_bsr(x);
        #else  // BITS_USE_ASM
        
        #if  BITS_PER_LONG == 64
        #define MU0 0x5555555555555555UL  // MU0 == ((-1UL)/3UL) == ...01010101_2
        #define MU1 0x3333333333333333UL  // MU1 == ((-1UL)/5UL)   == ...00110011_2
        #define MU2 0x0f0f0f0f0f0f0f0fUL  // MU2 == ((-1UL)/17UL)  == ...00001111_2
        #define MU3 0x00ff00ff00ff00ffUL  // MU3 == ((-1UL)/257UL)  == (8 ones)
        #define MU4 0x0000ffff0000ffffUL  // MU4 == ((-1UL)/65537UL) == (16 ones)
        #define MU5 0x00000000ffffffffUL  // MU5 == ((-1UL)/4294967297UL) == (32 ones)
        #else
        #define MU0 0x55555555UL  // MU0 == ((-1UL)/3UL) == ...01010101_2
        #define MU1 0x33333333UL  // MU1 == ((-1UL)/5UL)   == ...00110011_2
        #define MU2 0x0f0f0f0fUL  // MU2 == ((-1UL)/17UL)  == ...00001111_2
        #define MU3 0x00ff00ffUL  // MU3 == ((-1UL)/257UL)  == (8 ones)
        #define MU4 0x0000ffffUL  // MU4 == ((-1UL)/65537UL) == (16 ones)
        #endif
        
            ulong r = (ulong)ld_neq(x, x & MU0)
                + ((ulong)ld_neq(x, x & MU1) << 1)
                + ((ulong)ld_neq(x, x & MU2) << 2)
                + ((ulong)ld_neq(x, x & MU3) << 3)
                + ((ulong)ld_neq(x, x & MU4) << 4);
        #if  BITS_PER_LONG > 32
            r += ((ulong)ld_neq(x, x & MU5) << 5);
        #endif
            return r;
        
        #undef MU0
        #undef MU1
        #undef MU2
        #undef MU3
        #undef MU4
        #undef MU5
        #endif
        }
        

        asm_bsr 的实现位置取决于您的处理器架构

        // i386
        static inline ulong asm_bsr(ulong x)
        // Bit Scan Reverse: return index of highest one.
        {
            asm ("bsrl %0, %0" : "=r" (x) : "0" (x));
            return x;
        }
        

        或

        // AMD64
        static inline ulong asm_bsr(ulong x)
        // Bit Scan Reverse
        {
            asm ("bsrq %0, %0" : "=r" (x) : "0" (x));
            return x;
        }
        

        到这里获取代码:http://jjj.de/bitwizardry/bitwizardrypage.html

        编辑:

        这是函数ld_neq的源代码中的定义:

        static inline bool ld_neq(ulong x, ulong y)
        // Return whether floor(log2(x))!=floor(log2(y))
        { return ( (x^y) > (x&y) ); }
        

        【讨论】:

          【解决方案6】:

          我不知道这太棘手了:)

          我会将二进制数转换为 dec,然后直接返回该数字的以 2 为底的对数(从 float 转换为 int)。

          解决方法是从右边开始的(返回的数字+1)位。

          据我所知,你的答案是最左边的 1

          【讨论】:

          • 您的算法存在舍入误差。那不好。如果你根本不做任何舍入,那是完全错误的,因为位置总是整数。
          • @JanDvorak 如果您截断而不是舍入,则它是正确的,直接强制转换会这样做......
          • 是的,我在考虑截断... lapsus。但是如果数字是 0 仍然是一个错误
          • @BobVale 仍然没有。舍入误差有时会导致您将结果低估一倍
          • 好吧,德沃夏克,你有解决办法吗?您似乎在不提供您自己的解决方案的情况下对人们的答案投反对票:)
          【解决方案7】:

          我认为这是一个棘手的问题。最重要的位总是 1 :-)。如果面试官喜欢横向思维,那么这个答案应该是赢家!

          【讨论】:

          • 即使解释是正确的(我相信不是),答案也是错误的。
          • 这是否意味着我的解决方案是正确的?由于我在进行二分搜索,因此它具有 O(lgn) 复杂性。
          • @Stochasticly 不,它不是,这就像说零中最重要的数字不能是 1,因为 零中没有 1
          • @harold zero 显然是一个特例。对于非零数字,二进制数的最高位 digit 将是 1。但如果问题的解释是最高位的 位置 ,那么像丹尼斯的答案就是需要的。
          • 当然,这里没有争论。我不认为 OP 或面试官打算将此作为答案,但我认为这很有趣并且没有投反对票。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-23
          • 1970-01-01
          相关资源
          最近更新 更多