第1题 用按位与和取反实现按位异或
要求如下:
bitXor - x^y using only ~ and &
Example: bitXor(4, 5) = 1
Legal ops: ~ &
Max ops: 14
Rating: 1
如果学过数电或者是数理逻辑的话,这题应该不难,如果是数电的题目,实际上就是让你用与门和非门来实现异或。对于数理逻辑而言,也就是用与和非来实现异或,我们知道,异或就是不同则为1,相同则为0,对于单独的一位而言,就是1和0或者0和1这两种组合可以异或得到1,而对于位向量而言,也就是x&~y
或者~x&y
。这里需要一个或操作,但是并不允许我们使用,那么思路就是用与和非来实现或,在离散数学中我们知道,与和非这两个联结词的集合是极小全功能的,等价于与或非这三个联结词的集合,这是因为或是可以用与和非表示的,也就是x|y = ~(~x&~y)
,这里的证明可以使用 De Morgan’s laws 。于是只需要把这里的x和y替换成上面的两个就行了。
代码如下:
int bitXor(int x, int y) {
/*
Since "x bitXor y" equals "(x & ~y) | (~x & y)",
we just need to implement "|" with just "&" and "~".
And "x | y" equals "~(~x & ~y)".
*/
int x_and_noty = x & ~y;
int notx_and_y = ~x & y;
int not_x_and_noty = ~x_and_noty;
int not_notx_and_y = ~notx_and_y;
int x_bitXor_y = ~(not_x_and_noty & not_notx_and_y);
return x_bitXor_y;
}
第2题 返回补码中的最小值Tmin
要求如下:
tmin - return minimum two's complement integer
Legal ops: ! ~ & ^ | + << >>
Max ops: 4
Rating: 1
这题并不难,因为
T
m
i
n
Tmin
Tmin 实际上就是100..0
,一个1后面跟着31个0(因为int是32位的)。于是只需要将00..1
,也就是十进制数值的+1,左移31位,就可以得到了。
代码如下:
int tmin(void) {
/*
The bit expression of Tmin is 100..00, with one '1' followed by thirty-one '0'.
So we just need to shift 000..01 left 31 positions,
so that the '1' will be at the leftmost position, namely, Tmin.
*/
int one = 1;
return one << 31;
}
第3题 判断是否为Tmax(0x7fffffff)
要求如下:
isTmax - returns 1 if x is the maximum, two's complement number,and 0 otherwise
Legal ops: ! ~ & ^ | +
Max ops: 10
Rating: 2
虽然看上去还挺简单的,但是由于受的限制比较多,只允许用位运算和加法,所以难度系数就提高了,作者一开始没有看清题目,然后用了移位操作,写完之后运行检测程序才发现这一点。
先来分析一下Tmax有什么特别的地方,那么就会发现,如果你给它加上个1会如何呢,本来是0后面全是1,然后就变成了1后面全是0,这时候如果把+1前和+1后异或一下,那么就变成了全为1。对于别的数而言有这样的特点么,那就只有-1有这个特点了,因为-1的位表示是全1,那么+1之后就变成了全0,除此之外还有么?应该是没有了。
不妨直观上思考一下,为什么只有2个数有这样的特点。分2种情况考虑——最低位为0和最低位为1。如果最低位为0,那么+1之后再异或必然得到1,毕竟其他位都不变,只变了1位;如果最低位为1,那么+1之后就会进行进位,如果不是上面说的2个数,那么必然会到某一位就进位停止了,而剩下的高位不变,于是必然高位会为0。
所以可以利用这样的特点解决这道题,于是我们只需要从2种情况中区分出Tmax了,对于-1而言,+1之后就会变为0,所以我们依旧可以利用+1后的结果,对这个结果取一个非,对于C语言,非0即1,所以用!
作用在+1后的结果,如果为1,也就不是我们想要的(因为本来是+1后取个非如果为1,说明+1后为0,则原来是-1),如果为0,就是了。最后,我们把这个取非的结果和之前的判断结果做异或操作,这样,我们就得到了一种解法。
代码如下:
int isTmax(int x) {
int xPlusOne = x + 1;
int notTmaxOrMinusOne = !(~(x ^ xPlusOne));
return notTmaxOrMinusOne ^ !xPlusOne;
}
总共用了6个操作符,算是中规中矩的一种做法了,说实话看上去还存在挺多冗余的,只做为参考,并非最优解(全部做完之后会去看看最优解怎么做的,然后补充到这里)。
第4题 判断奇数位是否都是1(偶数位不管)
要求如下:
allOddBits - return 1 if all odd-numbered bits in word set to 1
Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
Legal ops: ! ~ & ^ | + << >>
Max ops: 12
Rating: 2
不是说只有全是a才返回1,偶数位可以为1
有个比较简单的思路,就是使用掩码,这里可以这么解决是因为掩码全是A,而且可以使用移位操作,那么就可以通过0xAA
进行扩展,变成全为A(8个A),如下:
如果用0xAAAAAAAA
作为掩码做与操作,如果结果为0xAAAAAAAA
,也就是说原来的奇数位都为1。于是我们只需要判断是否结果为0xAAAAAAAA
,只需要将结果和掩码做个异或,如果得到0,也就说明结果就是0xAAAAAAAA
。
代码如下:
int allOddBits(int x) {
/*
if x and 0xAAAAAAAA equals 0xAAAAAAAA,
then all bits are odd.
*/
int twoA = 0xAA;
int fourA = twoA + (twoA << 8);
int eightA = fourA + (fourA << 16);
return !((eightA & x) ^ eightA);
}
第5题 用位操作实现相反数
要求如下:
negate - return -x
Example: negate(1) = -1.
Legal ops: ! ~ & ^ | + << >>
Max ops: 5
Rating: 2
这题如果知道补码如何得到相反数就十分简单了,如果不知道这个规律还是比较难的。对于补码而言,如果要从x得到-x,需要按位取反然后+1即可。
代码如下:
int negate(int x) {
return ~x + 1;
}