如何反转字节中的ON位?

时间:2008-08-13 13:00:49

标签: language-agnostic

我正在读Joel的书,他在那里提出了面试问题:

  

编写程序以反转给定字节中的“ON”位。

我只能想到使用C的解决方案。

在这里询问,这样你就可以告诉我如何用Non C方式(如果可能的话)

17 个答案:

答案 0 :(得分:21)

我主张技巧问题。 :)反转所有位意味着触发器,但只有明显的位意味着:

return 0;

答案 1 :(得分:14)

  

这个问题的具体含义是什么?

好问题。如果反转“ON”位意味着仅反转“ON”位,那么无论输入是什么,您总是会得到0。如果它意味着反转 all 这些位,即将所有1改为0并将所有0改为1,这是我最初读取它的方式,那么这只是一个按位NOT或补码。基于C语言的补充运算符~就是这样做的。例如:

unsigned char b = 102;      /* 0x66, 01100110 */
unsigned char reverse = ~b; /* 0x99, 10011001 */

答案 2 :(得分:4)

反转C#中的位顺序:

byte ReverseByte(byte b)
{
    byte r = 0;
    for(int i=0; i<8; i++)
    {
        int mask = 1 << i;
        int bit = (b & mask) >> i;
        int reversedMask = bit << (7 - i);
        r |= (byte)reversedMask;
    }
    return r;
}

我确信有更聪明的方法可以做到这一点,但在这个精确的情况下,面试问题是为了确定你是否知道按位操作,所以我猜这个解决方案是可行的。

在一次采访中,面试官通常想知道你是如何找到解决方案的,你解决问题的技巧是什么,如果它是干净的还是黑客攻击。因此,不要提出太多聪明的解决方案,因为可能意味着您事先在互联网上找到了它。不要试图假装你也不知道它,并且你只是想出答案,因为你是天才,如果她弄清楚,因为你基本上是在撒谎,这将是最糟糕的。

答案 3 :(得分:4)

这个问题的具体含义是什么?

反向是否将1设置为0,反之亦然?

或者是否意味着 00001100 - &gt; 00110000 你在字节中反转它们的顺序?或者也许只是将第一个1到最后一个的部分反转?即。 00110101 - &gt; 00101011

假设它意味着在整个字节中反转位顺序,这里是一个x86汇编程序版本:

; al is input register
; bl is output register

xor bl, bl      ; clear output

; first bit
rcl al, 1       ; rotate al through carry
rcr bl, 1       ; rotate carry into bl

; duplicate above 2-line statements 7 more times for the other bits

不是最佳解决方案,表查找速度更快。

答案 4 :(得分:3)

如果您正在谈论将1转换为0,将0转换为1,则使用Ruby:

n = 0b11001100
~n

如果您的意思是颠倒顺序:

n = 0b11001100
eval("0b" + n.to_s(2).reverse)

如果你的意思是计算其他用户提到的on位:

n = 123
count = 0
0.upto(8) { |i| count = count + n[i] }

♥Ruby

答案 5 :(得分:3)

  

我可能记错了,但是我   以为乔尔的问题是关于   计算“开”位而不是   扭转它们。

你走了:

#include <stdio.h>

int countBits(unsigned char byte);

int main(){
  FILE* out = fopen( "bitcount.c" ,"w");

  int i;
  fprintf(out, "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n");

  fprintf(out, "int bitcount[256] = {");
  for(i=0;i<256;i++){
    fprintf(out, "%i", countBits((unsigned char)i));
    if( i < 255 ) fprintf(out, ", ");
  }
  fprintf(out, "};\n\n");

  fprintf(out, "int main(){\n");

  fprintf(out, "srand ( time(NULL) );\n");
  fprintf(out, "\tint num = rand() %% 256;\n");
  fprintf(out, "\tprintf(\"The byte %%i has %%i bits set to ON.\\n\", num, bitcount[num]);\n");

  fprintf(out, "\treturn 0;\n");
  fprintf(out, "}\n");
  fclose(out);

  return 0;
}

int countBits(unsigned char byte){
  unsigned char mask = 1;
  int count = 0;
  while(mask){
    if( mask&byte ) count++;
    mask <<= 1;
  }
  return count;
}

答案 6 :(得分:2)

这是一个直接从OpenJDK剪切和粘贴的版本,这很有趣,因为它不涉及循环。另一方面,与我发布的Scheme版本不同,此版本仅适用于32位和64位数字。 : - )

32位版本:

public static int reverse(int i) {
    // HD, Figure 7-1
    i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
    i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
    i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
    i = (i << 24) | ((i & 0xff00) << 8) |
        ((i >>> 8) & 0xff00) | (i >>> 24);
    return i;
}

64位版本:

public static long reverse(long i) {
    // HD, Figure 7-1
    i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;
    i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;
    i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;
    i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
    i = (i << 48) | ((i & 0xffff0000L) << 16) |
        ((i >>> 16) & 0xffff0000L) | (i >>> 48);
    return i;
}

答案 7 :(得分:2)

伪代码..

while (Read())
  Write(0);

答案 8 :(得分:2)

  

byte ReverseByte(byte b) { return b ^ 0xff; }

如果^在你的语言中是异或的,那么这是有效的,但如果它是AND,则通常是这样的。

答案 9 :(得分:2)

经典的Bit Hacks页面有几种(非常聪明的)方法可以做到这一点,但它都在C语言中。任何从C语法(特别是Java)派生的语言都可能有类似的方法。我相信我们会在这个帖子中得到一些Haskell版本;)

答案 10 :(得分:1)

我可能记错了,但我认为Joel的问题是计算“on”位而不是反转它们。

答案 11 :(得分:1)

这是用于补充位的强制性Haskell soln,它使用库函数,补码:

import Data.Bits
import Data.Int

i = 123::Int
i32 = 123::Int32
i64 = 123::Int64
var2 = 123::Integer

test1 = sho i
test2 = sho i32
test3 = sho i64
test4 = sho var2 -- Exception

sho i = putStrLn $ showBits i ++ "\n" ++ (showBits $complement i)
showBits  v = concatMap f (showBits2 v) where
   f False = "0"
   f True  = "1"
showBits2 v = map (testBit v) [0..(bitSize v - 1)]

答案 12 :(得分:1)

我修改了palmsey的第二个例子,消除了一个错误并消除了eval

n = 0b11001100
n.to_s(2).rjust(8, '0').reverse.to_i(2)

如果要按位反转的数字是固定长度的位字段,rjust很重要 - 没有它,0b00101010的反转将是0b10101而不是正确的0b01010100。 (显然,8应该用有问题的长度代替。)我刚被这个绊倒了。

答案 13 :(得分:1)

如果问题意味着翻转所有位,并且不允许使用类似C的运算符,例如XOR和NOT,那么这将起作用:

bFlipped = -1 - bInput;

答案 14 :(得分:1)

  

在这里询问,这样你就可以用非C方式(如果可能的话)告诉我怎么做

假设您的号码为10101010.要将1更改为0(反之亦然),您只需使用XOR:

 10101010
^11111111
 --------
 01010101

手工操作就像你得到的“非C”一样。

然而,根据问题的措辞,它听起来似乎关闭“ON”位...在这种情况下答案是零(如已经提到的那样)(当然除外)问题实际上是要求交换位的顺序。)

答案 15 :(得分:0)

由于问题是非C方式,这是一个方案实施,从SLIB高兴地抄袭:

(define (bit-reverse k n)
  (do ((m (if (negative? n) (lognot n) n) (arithmetic-shift m -1))
       (k (+ -1 k) (+ -1 k))
       (rvs 0 (logior (arithmetic-shift rvs 1) (logand 1 m))))
      ((negative? k) (if (negative? n) (lognot rvs) rvs))))

(define (reverse-bit-field n start end)
  (define width (- end start))
  (let ((mask (lognot (ash -1 width))))
    (define zn (logand mask (arithmetic-shift n (- start))))
    (logior (arithmetic-shift (bit-reverse width zn) start)
            (logand (lognot (ash mask start)) n))))

重写为C(对于不熟悉Scheme的人),它看起来像这样(理解在Scheme中,数字可以任意大):

int
bit_reverse(int k, int n)
{
    int m = n < 0 ? ~n : n;
    int rvs = 0;
    while (--k >= 0) {
        rvs = (rvs << 1) | (m & 1);
        m >>= 1;
    }
    return n < 0 ? ~rvs : rvs;
}

int
reverse_bit_field(int n, int start, int end)
{
    int width = end - start;
    int mask = ~(-1 << width);
    int zn = mask & (n >> start);
    return (bit_reverse(width, zn) << start) | (~(mask << start) & n);
}

答案 16 :(得分:0)

反转位。 例如,我们有一个由01101011表示的数字。现在,如果我们反转这些位,那么这个数字将变为11010110.现在要实现这一点,你应该首先知道如何在一个数字中交换两位。 在一个数字中交换两位: - 将这两个位与一个XOR进行异或,看看结果是否不同。如果它们不是,那么两个位都是相同的,否则将具有XOR的位都异或并保存在其原始数字中; 现在为了扭转这个数字 因为我比Numberofbits / 2少    交换(数量,I,NumberOfBits-1-I);