 UID: 11
 昵称: 超越平凡
 精华: 11
 积分: 339631
 帖子: 1469
 威望: 119297
 金钱: 317414 D币
 宣传币: 228706 X币
 踢楼币: 0 T币
 经验: 12级
 阅读权限: 80
 注册: 2007-7-14
 来自: hnlnjfc

状态:
|
|
楼主
大 中
小 发表于 2008-6-25 23:18 只看该作者
位运算简介及实用技巧
什么是位运算?3 s$ k) ]( S/ l3 x1 U8 r1 u
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
% u2 A/ h4 Y0 V1 h! L我爱电脑技术论坛 110
8 X! d \: ?, J- e3 R我爱电脑技术社区--打造最好的电脑技术自学交流平台AND 1011
0 }- S) z4 a9 D7 j打造最好的电脑自学交流论坛----------www.520diannao.com& G2 K5 ~8 v* z% Q% a: s" r4 k
0010 --> 2我爱电脑技术社区--打造最好的电脑技术自学交流平台8 Y5 ^0 H; e @7 E4 J: R
由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。
! a- K' y# T. h3 \; r$ I# U! e3 f- V( _
2 g/ V% V7 ^) q我爱电脑技术论坛我爱电脑技术论坛 J8 }3 `" m3 v( @. q% }
Pascal和C中的位运算符号
( d- C$ F0 X& W" e: J2 B! Owww.520diannao.com 下面的a和b都是整数类型,则:
; Q+ L& b/ P9 Z. Y1 b. V4 c电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站C语言 | Pascal语言
' r- O2 W% `. D* F0 swww.520diannao.com-------+-------------
; r$ P3 G: ^% A: m我爱电脑技术论坛a & b | a and b我爱电脑技术论坛4 g5 O9 Y( A: C" Z [
a | b | a or b
9 {+ f7 M/ B( M4 Ha ^ b | a xor b打造最好的电脑自学交流论坛' Z! K; o6 V6 t3 F
~a | not a我爱电脑技术论坛- O! j/ B% b% W; C5 I D
a << b | a shl b
$ B) @: O$ x) N打造最好的电脑自学交流论坛a >> b | a shr b
5 i1 {7 |0 C! W) c电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 注意C中的逻辑运算和位运算符号是不同的。520|1314=1834,但520||1314=1,因为逻辑运算时520和1314都相当于True。同样的,!a和~a也是有区别的。
/ p) w1 @4 X% Q6 V" a打造最好的电脑自学交流论坛
* ]# E) E$ ^( S' Y: v/ W打造最好的电脑自学交流论坛
' i# T. A: ^2 K Q各种位运算的使用" ^5 Y7 r8 g$ b0 b4 s1 g$ f; \
=== 1. and运算 === 打造最好的电脑自学交流论坛/ V+ @8 i( }& L! O6 @; C
and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数. 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站$ B# {/ C9 L7 }/ V. |
. l+ I) ]4 s+ a+ n7 ?. h === 2. or运算 ===
9 c* _+ \* K9 [电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。
2 N& E/ p# C9 @, s电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站打造最好的电脑自学交流论坛6 s! y* M% ?+ m( c; N
=== 3. xor运算 === 我爱电脑技术社区--打造最好的电脑技术自学交流平台& H0 I: F0 F6 a
xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
/ {& c) t8 n+ @) [$ c5 v5 f打造最好的电脑自学交流论坛 xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。 打造最好的电脑自学交流论坛& D! F, ^$ ^0 R/ q2 {. p6 M5 i
下面我们看另外一个东西。定义两个符号#和@(我怎么找不到那个圈里有个叉的字符),这两个符号互为逆运算,也就是说(x # y) @ y = x。现在依次执行下面三条命令,结果是什么? 我爱电脑技术社区--打造最好的电脑技术自学交流平台6 z5 ^: V2 G J! @2 ?& C* v4 a
复制内容到剪贴板 代码: x <- x # y
/ V4 L M8 W f/ \8 w我爱电脑技术论坛y <- x @ y我爱电脑技术社区--打造最好的电脑技术自学交流平台/ c/ c, i; u: |; c: Y d) y
x <- x @ y
" E6 @/ y) ]& Z6 q执行了第一句后x变成了x # y。那么第二句实质就是y <- x # y @ y,由于#和@互为逆运算,那么此时的y变成了原来的x。第三句中x实际上被赋值为(x # y) @ x,如果#运算具有交换律,那么赋值后x就变成最初的y了。这三句话的结果是,x和y的位置互换了。 (转载者注:怪不得有些东西可以这么用,这才是实质啊!)& t- m. Y' N) U2 l! E2 O7 C
加法和减法互为逆运算,并且加法满足交换律。把#换成+,把@换成-,我们可以写出一个不需要临时变量的swap过程(Pascal)。
C! s- n" q; w' S复制内容到剪贴板 代码: procedure swap(var a,b:longint);
& x! S- @8 I1 ^1 F, ^www.520diannao.combegin我爱电脑技术论坛; L* ^! t; I n8 l" i/ `
a:=a + b;
0 Q! E# v4 x5 g, @$ X) \www.520diannao.com b:=a - b;
% P+ h' P# ]$ ]( X7 twww.520diannao.com a:=a - b;
" ^% W a Q' u0 o1 D& G我爱电脑技术社区--打造最好的电脑技术自学交流平台end;我爱电脑技术论坛, w* s( ~" r4 \# C
好了,刚才不是说xor的逆运算是它本身吗?于是我们就有了一个看起来非常诡异的swap过程: 我爱电脑技术论坛; ~6 \$ Y; x6 v( \' r$ c
复制内容到剪贴板 代码: procedure swap(var a,b:longint);打造最好的电脑自学交流论坛: `6 Y' p- K3 C i9 O9 Z2 x, s) O
begin我爱电脑技术论坛) `- k( F0 `+ U/ i# u& J+ s, l
a:=a xor b;打造最好的电脑自学交流论坛- v! N |, U% |. @+ m A, K( n
b:=a xor b;
: k& C; m8 o8 {! b% o7 d. Z我爱电脑技术社区--打造最好的电脑技术自学交流平台 a:=a xor b;
m* c0 y. H# |2 w2 \# nend;
7 J5 d, H8 ?8 V打造最好的电脑自学交流论坛=== 4. not运算 === 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站% W. q9 E f; O9 G: u7 _: H
not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用$0000到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。
) @) e4 }( y2 D) b" O- ^' U' k复制内容到剪贴板 代码: var打造最好的电脑自学交流论坛 }7 |, M) C5 @/ u
a:word;我爱电脑技术社区--打造最好的电脑技术自学交流平台& A2 O9 W1 X$ T" l' {
begin
; q/ M, _% \2 i# k# t& }我爱电脑技术社区--打造最好的电脑技术自学交流平台 a:=100;
' U9 t3 H4 v1 e( k% |' J& t3 { a:=not a;
9 P+ P5 k% i6 o, g i- F我爱电脑技术论坛 writeln(a);我爱电脑技术社区--打造最好的电脑技术自学交流平台; j% ?2 Z% w E+ W1 p, n' a
end.
* m8 h2 P+ l _6 P! e# s复制内容到剪贴板 代码: #include <stdio.h>
% Z9 t9 d4 r5 _2 L* z* E/ s我爱电脑技术社区--打造最好的电脑技术自学交流平台int main()
& i1 [( B5 W2 M7 V. L+ Y% _打造最好的电脑自学交流论坛{
0 \5 ]4 o! x. Z# p3 m- v* ~2 p打造最好的电脑自学交流论坛 unsigned short a=100;
p! Z1 b7 V5 D3 r电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 a = ~a;www.520diannao.com2 h7 W9 e8 g9 v6 X
printf( "%d\n", a ); 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站# o. d7 |& `5 n, A
return 0;
' Z# j& X3 D/ W C& z$ k我爱电脑技术论坛}
7 H- ]9 L2 _# f; T' S! H电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。
. ^( P4 E2 W, `4 D4 p打造最好的电脑自学交流论坛
1 i7 W2 _9 P: I* M* q6 | P我爱电脑技术论坛 === 5. shl运算 ===
: k M5 N I+ Q T/ S2 l打造最好的电脑自学交流论坛 a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站, j7 \3 f- f$ {" Y
通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。 我爱电脑技术社区--打造最好的电脑技术自学交流平台3 [3 a$ L, B0 g4 ^4 ~. D" d
定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。 打造最好的电脑自学交流论坛# i! Q0 R9 B$ K1 F) Y, o, A/ ^
打造最好的电脑自学交流论坛# g# J5 E8 C1 w$ N
=== 6. shr运算 ===
9 S' Z/ t3 |/ d1 T打造最好的电脑自学交流论坛 和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。
7 M: K# o5 Z) p, D) D0 b4 l( z! K我爱电脑技术社区--打造最好的电脑技术自学交流平台
$ c: n, w5 l/ s1 s4 {: o1 p
& i3 X8 u; }/ s `/ e1 w0 S打造最好的电脑自学交流论坛位运算的简单应用
" S: w% R1 E- Z( l% P& O电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 有时我们的程序需要一个规模不大的Hash表来记录状态。比如,做数独时我们需要27个Hash表来统计每一行、每一列和每一个小九宫格里已经有哪些数了。此时,我们可以用27个小于2^9的整数进行记录。例如,一个只填了2和5的小九宫格就用数字18表示(二进制为000010010),而某一行的状态为511则表示这一行已经填满。需要改变状态时我们不需要把这个数转成二进制修改后再转回去,而是直接进行位操作。在搜索时,把状态表示成整数可以更好地进行判重等操作。 这道题是在搜索中使用位运算加速的经典例子。以后我们会看到更多的例子。
+ w; Q5 ?2 N' o) B7 ]6 T/ x 下面列举了一些常见的二进制位的变换操作。
8 E+ T, R6 D7 k; T0 ~' f我爱电脑技术论坛电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站2 z& R1 ^, U5 h' ?
功能 | 示例 | 位运算 www.520diannao.com; e! T9 B* Y- h4 j
----------------------+---------------------------+--------------------
' A5 }, t1 t; w我爱电脑技术论坛去掉最后一位 | (101101->10110) | x shr 1 打造最好的电脑自学交流论坛6 T" b$ ~( t0 k3 Z! n4 U
在最后加一个0 | (101101->1011010) | x shl 1 我爱电脑技术论坛1 l9 H* q2 Z& t* x
在最后加一个1 | (101101->1011011) | x shl 1+1
) l2 ^1 h' t, R3 X* {2 D把最后一位变成1 | (101100->101101) | x or 1 . V* f/ U9 j0 W0 u3 h: C) n; Y
把最后一位变成0 | (101101->101100) | x or 1-1
5 m. [' ]# \& e( i' W. Owww.520diannao.com最后一位取反 | (101101->101100) | x xor 1
. x! i7 }8 P- B/ v1 Q% \6 c电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站把右数第k位变成1 | (101001->101101,k=3) | x or (1 shl (k-1))
( M* R! v0 e& F" K' Q1 m电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站把右数第k位变成0 | (101101->101001,k=3) | x and not (1 shl (k-1)) www.520diannao.com4 v" K. h7 g& \4 G8 ^
右数第k位取反 | (101001->101101,k=3) | x xor (1 shl (k-1))
+ V* T( X+ E/ ]' P& _/ b: K# B3 h我爱电脑技术社区--打造最好的电脑技术自学交流平台取末三位 | (1101101->101) | x and 7 打造最好的电脑自学交流论坛5 ~. A7 @/ W" r% J0 P" z
取末k位 | (1101101->1101,k=5) | x and (1 shl k-1) 我爱电脑技术社区--打造最好的电脑技术自学交流平台) T# V& [6 H5 L$ Q) n4 G
取右数第k位 | (1101101->1,k=4) | x shr (k-1) and 1 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站$ [( @" o- ?7 G, x
把末k位变成1 | (101001->101111,k=4) | x or (1 shl k-1) 我爱电脑技术论坛, M* X" i% S5 M0 t# b
末k位取反 | (101001->100110,k=4) | x xor (1 shl k-1)
; u# ~% _6 d8 a7 n# j ^www.520diannao.com把右边连续的1变成0 | (100101111->100100000) | x and (x+1)
: V+ f0 j) u/ U, |我爱电脑技术论坛把右起第一个0变成1 | (100101111->100111111) | x or (x+1)
) F4 \! r( v8 [3 V, R1 H6 r1 P把右边连续的0变成1 | (11011000->11011111) | x or (x-1)
; G5 j5 G4 O2 E" ?' D打造最好的电脑自学交流论坛取右边连续的1 | (100101111->1111) | (x xor (x+1)) shr 1
3 e3 f1 ?: W: F我爱电脑技术论坛去掉右起第一个1的左边 | (100101000->1000) | x and (x xor (x-1)) 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站( m/ s$ f/ W: C( E% H E
www.520diannao.com) G+ H# z. G- _4 E( C, P- |; I+ ]
最后这一个在树状数组中会用到。 2 Q* ^' t+ J7 b% Q) j9 j* f
* c% v' R+ n0 d0 M4 \/ ~5 V3 o打造最好的电脑自学交流论坛
3 Y% b& P/ ~! ~/ ?" {打造最好的电脑自学交流论坛Pascal和C中的16进制表示
$ _+ @3 A3 u+ C" n3 A F1 C我爱电脑技术论坛 Pascal中需要在16进制数前加$符号表示,C中需要在前面加0x来表示。这个以后我们会经常用到。
* A5 m6 Z {, N! w; k) D" C我爱电脑技术社区--打造最好的电脑技术自学交流平台我爱电脑技术论坛# d7 X R- }$ r8 _5 S( r
整数类型的储存 www.520diannao.com& Y6 G1 j& x. X- M8 ~8 K" @* l4 J6 k
我们前面所说的位运算都没有涉及负数,都假设这些运算是在unsigned/word类型(只能表示正数的整型)上进行操作。但计算机如何处理有正负符号的整数类型呢?下面两个程序都是考察16位整数的储存方式(只是语言不同)。 我爱电脑技术论坛4 y$ w; V/ C, _; @- q' I
复制内容到剪贴板 代码: var我爱电脑技术社区--打造最好的电脑技术自学交流平台$ K: L. b# ^7 H/ O
a,b:integer;
! H7 d B* S6 U6 D: h打造最好的电脑自学交流论坛begin
# h' a) I9 ?* L: b; i, N, ~电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 a:=$0000;
+ V3 C! ]/ S* i9 _4 V b:=$0001;
/ u: S }, g8 T7 d电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 write(a,' ',b,' ');, h/ e% `* F' }; s
a:=$FFFE;我爱电脑技术论坛: Z* Z/ y; Z/ Q4 l5 ?
b:=$FFFF;
8 `2 [- T5 I9 K打造最好的电脑自学交流论坛 write(a,' ',b,' ');
, E0 l: {7 V# `) k0 qwww.520diannao.com a:=$7FFF;
2 T0 c ]! C8 ]8 S电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 b:=$8000;
- ?! J @/ d; m0 v writeln(a,' ',b);我爱电脑技术社区--打造最好的电脑技术自学交流平台/ \6 G* C- r. W: J
end.电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站; v% E' P) c& H M. I( r
复制内容到剪贴板 代码: #include <stdio.h>
" z. j) {+ G ^& X* u; Rwww.520diannao.comint main()www.520diannao.com* P \3 Q# }2 h1 T* J
{www.520diannao.com' C- P I$ s( _6 Q* P
short int a, b;
# u9 L$ a5 e( g# w a = 0x0000;
- x* a: {7 [1 o* c打造最好的电脑自学交流论坛 b = 0x0001;我爱电脑技术社区--打造最好的电脑技术自学交流平台, |4 b8 L( j# S
printf( "%d %d ", a, b );
3 A& b0 ?; Z: \) _8 S我爱电脑技术论坛 a = 0xFFFE;
2 z, m+ J& m4 ^我爱电脑技术社区--打造最好的电脑技术自学交流平台 b = 0xFFFF;我爱电脑技术论坛! [- g$ @& A, S* i7 v
printf( "%d %d ", a, b );我爱电脑技术社区--打造最好的电脑技术自学交流平台* h, g% F+ u. I8 j2 U2 Y% D/ d: U
a = 0x7FFF;www.520diannao.com& d: ^( d x+ I$ g+ f" {
b = 0x8000;我爱电脑技术论坛8 Z5 X% b2 @* H, Z6 L" h
printf( "%d %d\n", a, b );打造最好的电脑自学交流论坛- }9 e; K' g" Y$ ~
return 0;
3 y% ]. b% Z$ \打造最好的电脑自学交流论坛}
, M c1 A J1 Q i" F5 M& h两个程序的输出均为0 1 -2 -1 32767 -32768。其中前两个数是内存值最小的时候,中间两个数则是内存值最大的时候,最后输出的两个数是正数与负数的分界处。由此你可以清楚地看到计算机是如何储存一个整数的:计算机用$0000到$7FFF依次表示0到32767的数,剩下的$8000到$FFFF依次表示-32768到-1的数。32位有符号整数的储存方式也是类似的。稍加注意你会发现,二进制的第一位是用来表示正负号的,0表示正,1表示负。这里有一个问题:0本来既不是正数,也不是负数,但它占用了$0000的位置,因此有符号的整数类型范围中正数个数比负数少一个。对一个有符号的数进行not运算后,最高位的变化将导致正负颠倒,并且数的绝对值会差1。也就是说,not a实际上等于-a-1。这种整数储存方式叫做“补码”。
. e8 S' G! f! _www.520diannao.com打造最好的电脑自学交流论坛- D6 E5 k: r7 |; K2 C
===== 真正强的东西来了! ===== 我爱电脑技术社区--打造最好的电脑技术自学交流平台: }5 H0 n$ d8 N* }
打造最好的电脑自学交流论坛6 i' k, m4 p/ O' _: y# k
二进制中的1有奇数个还是偶数个
^: |/ B% @3 g; f我爱电脑技术论坛 我们可以用下面的代码来计算一个32位整数的二进制中1的个数的奇偶性,当输入数据的二进制表示里有偶数个数字1时程序输出0,有奇数个则输出1。例如,1314520的二进制101000000111011011000中有9个1,则x=1314520时程序输出1。
- W' S9 h P' O& W0 e4 F我爱电脑技术社区--打造最好的电脑技术自学交流平台复制内容到剪贴板 代码: var
% T3 i% p7 l$ @; K我爱电脑技术社区--打造最好的电脑技术自学交流平台 i,x,c:longint;
5 L2 {7 c! G. r7 f. n* V7 B# r" _4 d/ h我爱电脑技术论坛begin
6 }: a4 r+ |& ]9 e4 m, s# p打造最好的电脑自学交流论坛 readln(x);www.520diannao.com8 h! J5 `: d& k! \
c:=0;我爱电脑技术社区--打造最好的电脑技术自学交流平台9 h. m- Q7 U4 F3 X. b: \
for i:=1 to 32 do+ h6 n" L! c9 ?+ W. Q$ R t3 c
begin
) m# ~+ K0 l0 [! Y3 W3 L" Fwww.520diannao.com c:=c + x and 1;
/ a/ Y; H$ r4 W2 a+ N+ N我爱电脑技术社区--打造最好的电脑技术自学交流平台 x:=x shr 1;
( y; t6 p, r: A6 @2 E! X) u: b我爱电脑技术论坛 end;打造最好的电脑自学交流论坛+ p! R4 }* x2 a: C$ H2 A0 \& S
writeln( c and 1 );
- v0 L; \9 B P, p. D我爱电脑技术社区--打造最好的电脑技术自学交流平台end.
9 p, |0 T4 v T但这样的效率并不高,位运算的神奇之处还没有体现出来。
7 u: h0 E# c4 S打造最好的电脑自学交流论坛 同样是判断二进制中1的个数的奇偶性,下面这段代码就强了。你能看出这个代码的原理吗? 我爱电脑技术论坛- d b$ w% f0 ^
复制内容到剪贴板 代码: var
" s3 ~; o3 A2 A7 U电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 x:longint;
: m* G$ O: q8 E6 H, m6 d我爱电脑技术社区--打造最好的电脑技术自学交流平台begin
" m# N& A& K- W' Q我爱电脑技术论坛 readln(x);我爱电脑技术论坛1 c! R* T, a, k5 R8 M) k' b1 h
x:=x xor (x shr 1);
" _1 Y0 i! t& v! W* e7 V. o我爱电脑技术论坛 x:=x xor (x shr 2);
4 q/ W* ?- ]% V7 I( S2 hwww.520diannao.com x:=x xor (x shr 4);% y; h& L% S' P) F
x:=x xor (x shr 8);
( O: _& g$ h7 c# {- X9 f+ A; w打造最好的电脑自学交流论坛 x:=x xor (x shr 16);" o- v) R* f+ M- Y& S: _0 M# t7 t0 U+ `
writeln(x and 1);www.520diannao.com I; ^% {/ G) Y3 j# _8 h% E
end.
5 Q& Z& A5 K1 x. P2 c& s- y" n; f) o为了说明上面这段代码的原理,我们还是拿1314520出来说事。1314520的二进制为101000000111011011000,第一次异或操作的结果如下: 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站% U# @! a! e! Z9 c' G* ]
我爱电脑技术论坛) T1 p( k' h3 d& \5 {# v$ ~1 l+ p
00000000000101000000111011011000
! o, a7 O5 T% W" b, X+ T7 twww.520diannao.comXOR 0000000000010100000011101101100
3 Z/ d! @3 }, y4 u: z" D1 }0 o电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站---------------------------------------4 a/ T! B/ F7 z3 V) \6 E j/ {5 b
00000000000111100000100110110100电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站0 s# q. \4 S9 m" c& e) h
# I J$ Q, q7 f电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 得到的结果是一个新的二进制数,其中右起第i位上的数表示原数中第i和i+1位上有奇数个1还是偶数个1。比如,最右边那个0表示原数末两位有偶数个1,右起第3位上的1就表示原数的这个位置和前一个位置中有奇数个1。对这个数进行第二次异或的结果如下: www.520diannao.com. ?' N/ ]& X+ O) z7 X, B/ L1 I
; e1 g- U1 j4 I+ n, @ 00000000000111100000100110110100我爱电脑技术社区--打造最好的电脑技术自学交流平台8 U, V" E/ _- c4 S7 g0 T$ ^
XOR 000000000001111000001001101101
4 ^9 j( x9 w5 q' u( V6 h3 z; ~打造最好的电脑自学交流论坛---------------------------------------
. R0 ] b4 e5 e) o) I: K0 ?我爱电脑技术社区--打造最好的电脑技术自学交流平台 00000000000110011000101111011001
! h8 s) a, ~0 U: e& i' q1 }% Zwww.520diannao.com, Z; z- f2 a( P
结果里的每个1表示原数的该位置及其前面三个位置中共有奇数个1,每个0就表示原数对应的四个位置上共偶数个1。一直做到第五次异或结束后,得到的二进制数的最末位就表示整个32位数里有多少个1,这就是我们最终想要的答案。
3 Z' ?% d0 T8 F& Y2 @) P6 ]
( Z: z2 F$ b# E* |我爱电脑技术论坛
5 A" j( I% \. `9 t电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站计算二进制中的1的个数电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站* H. O; V+ S$ A6 U: I, A) V
同样假设x是一个32位整数。经过下面五次赋值后,x的值就是原数的二进制表示中数字1的个数。比如,初始时x为1314520(网友抓狂:能不能换一个数啊),那么最后x就变成了9,它表示1314520的二进制中有9个1。
% j d. u% ^$ y0 P电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站复制内容到剪贴板 代码: x := (x and $55555555) + ((x shr 1) and $55555555);
: V7 I5 ?0 O5 B- s; c. S/ _我爱电脑技术论坛x := (x and $33333333) + ((x shr 2) and $33333333); 打造最好的电脑自学交流论坛5 h% u* F! R9 `+ U, c& c
x := (x and $0F0F0F0F) + ((x shr 4) and $0F0F0F0F);
, v% g2 R9 C) }打造最好的电脑自学交流论坛x := (x and $00FF00FF) + ((x shr 8) and $00FF00FF); " a/ F' ?+ _" d4 }% w# r3 o/ s2 X
x := (x and $0000FFFF) + ((x shr 16) and $0000FFFF);
! T0 ?; F. ] U r8 O我爱电脑技术社区--打造最好的电脑技术自学交流平台为了便于解说,我们下面仅说明这个程序是如何对一个8位整数进行处理的。我们拿数字211(我们班某MM的生日)来开刀。211的二进制为11010011。
- U0 c/ `6 I6 s% n9 o- \我爱电脑技术论坛www.520diannao.com; D2 `( P0 \- Z1 X( ^
+---+---+---+---+---+---+---+---+ 我爱电脑技术社区--打造最好的电脑技术自学交流平台0 I( h* t+ i. c3 K
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | <---原数 我爱电脑技术社区--打造最好的电脑技术自学交流平台6 H2 ~' y3 G9 b" ^; i2 j4 l: E
+---+---+---+---+---+---+---+---+
4 y% ^( h0 \& M, W! t+ I% X打造最好的电脑自学交流论坛| 1 0 | 0 1 | 0 0 | 1 0 | <---第一次运算后 + J+ @2 l0 ]% i0 L. i
+-------+-------+-------+-------+
) |* m4 w- ]5 y! @6 L打造最好的电脑自学交流论坛| 0 0 1 1 | 0 0 1 0 | <---第二次运算后
1 z% g9 f$ d9 K. O6 A9 h8 f9 o我爱电脑技术论坛+---------------+---------------+
8 C J* t1 X' [" w4 W N电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站| 0 0 0 0 0 1 0 1 | <---第三次运算后,得数为5 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站6 X( X3 W. T8 Z% r7 Z- d
+-------------------------------+ 我爱电脑技术论坛 X2 U$ d! |5 @2 s( o8 w; _9 P* |7 K
我爱电脑技术社区--打造最好的电脑技术自学交流平台- Q# i+ l7 f/ Y# V
整个程序是一个分治的思想。第一次我们把每相邻的两位加起来,得到每两位里1的个数,比如前两位10就表示原数的前两位有2个1。第二次我们继续两两相加,10+01=11,00+10=10,得到的结果是00110010,它表示原数前4位有3个1,末4位有2个1。最后一次我们把0011和0010加起来,得到的就是整个二进制中1的个数。程序中巧妙地使用取位和右移,比如第二行中$33333333的二进制为00110011001100....,用它和x做and运算就相当于以2为单位间隔取数。shr的作用就是让加法运算的相同数位对齐。 打造最好的电脑自学交流论坛/ U! [( L7 g4 }0 [. [
2 l& D$ [/ a0 m' l, a: a打造最好的电脑自学交流论坛二分查找32位整数的前导0个数www.520diannao.com$ n3 n0 T/ p0 U' M7 \. W4 G, q" y
这里用的C语言,我直接Copy的Hacker's Delight上的代码。这段代码写成C要好看些,写成Pascal的话会出现很多begin和end,搞得代码很难看。程序思想是二分查找,应该很简单,我就不细说了。
. Z! Z+ R; Z( A+ p: q4 L) ~" M* E复制内容到剪贴板 代码: int nlz(unsigned x)www.520diannao.com6 u& d- n' W6 M' r! c
{我爱电脑技术社区--打造最好的电脑技术自学交流平台2 j( l- j4 {0 \
int n;我爱电脑技术社区--打造最好的电脑技术自学交流平台% ?& s& L% o7 U) H( Z, S6 {; x
: x9 \- f" K2 f8 m$ _我爱电脑技术社区--打造最好的电脑技术自学交流平台 if (x == 0) return(32);打造最好的电脑自学交流论坛& U2 G/ ^$ [/ f
n = 1;我爱电脑技术社区--打造最好的电脑技术自学交流平台0 q) F8 [% I, t9 j5 G
if ((x >> 16) == 0) {n = n +16; x = x <<16;}我爱电脑技术社区--打造最好的电脑技术自学交流平台6 @ ], w4 a7 h
if ((x >> 24) == 0) {n = n + 8; x = x << 8;}www.520diannao.com3 Y: v' U. I7 [3 X* }0 ~
if ((x >> 28) == 0) {n = n + 4; x = x << 4;}打造最好的电脑自学交流论坛4 g7 L3 Y2 c: i$ P- m3 }9 o
if ((x >> 30) == 0) {n = n + 2; x = x << 2;}
) o' ?4 z# l: r2 F: D9 @6 \我爱电脑技术论坛 n = n - (x >> 31);
5 z8 q# I, p2 Y电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 return n;电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站1 e! w z" V/ c% E5 U
}
% A6 o! [: T9 j" E- b我爱电脑技术社区--打造最好的电脑技术自学交流平台只用位运算来取绝对值
: h" s& l; H& E5 C4 b# F# a" _我爱电脑技术社区--打造最好的电脑技术自学交流平台 这是一个非常有趣的问题。假设x为32位整数,则x xor (not (x shr 31) + 1) + x shr 31的结果是x的绝对值,x shr 31是二进制的最高位,它用来表示x的符号。如果它为0(x为正),则not (x shr 31) + 1等于$00000000,异或任何数结果都不变;如果最高位为1(x为负),则not (x shr 31) + 1等于$FFFFFFFF,x异或它相当于所有数位取反,异或完后再加一。
3 y. L8 B. w& t5 _% e
6 g- `- j' h; Nwww.520diannao.com高低位交换
: \$ G' f7 |/ g- N E) r. ^% g我爱电脑技术论坛 这个题实际上是我出的,做为学校内部NOIp模拟赛的第一题。题目是这样: 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站4 L& `* p5 h9 E% v
引用: 给出一个小于2^32的正整数。这个数可以用一个32位的二进制数表示(不足32位用0补足)。我们称这个二进制数的前16位为“高位”,后16位为“低位”。将它的高低位交换,我们可以得到一个新的数。试问这个新的数是多少(用十进制表示)。
3 ]$ d! l. D, x* swww.520diannao.com 例如,数1314520用二进制表示为0000 0000 0001 0100 0000 1110 1101 1000(添加了11个前导0补足为32位),其中前16位为高位,即0000 0000 0001 0100;后16位为低位,即0000 1110 1101 1000。将它的高低位进行交换,我们得到了一个新的二进制数0000 1110 1101 1000 0000 0000 0001 0100。它即是十进制的249036820。 我爱电脑技术论坛' ]4 g* h% u2 U5 d4 [+ V( F
当时几乎没有人想到用一句位操作来代替冗长的程序。使用位运算的话两句话就完了。
0 W# x6 ~0 g f0 E$ k8 f |0 ^www.520diannao.com复制内容到剪贴板 代码: var
$ F h1 b6 u' N: I+ q6 X我爱电脑技术论坛 n:dword;电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站# u, r/ B V7 S2 {
beginwww.520diannao.com) Q8 J: {6 n, X/ X' P* ?
readln( n );
8 j+ c# W9 {& {# w电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 writeln( (n shr 16) or (n shl 16) );www.520diannao.com) n+ V% q% E& y% \
end.我爱电脑技术社区--打造最好的电脑技术自学交流平台: \4 X" ^9 A& a6 q) M; E
而事实上,Pascal有一个系统函数swap直接就可以用。 2 v0 J5 a, \# \1 M% l9 S
, e) f5 T: Q3 @* g' {# P打造最好的电脑自学交流论坛我爱电脑技术社区--打造最好的电脑技术自学交流平台( I& x+ x; S, ~8 v5 H
二进制逆序
7 @; B( L2 @4 G0 N% Z: O4 nwww.520diannao.com 下面的程序读入一个32位整数并输出它的二进制倒序后所表示的数。 我爱电脑技术社区--打造最好的电脑技术自学交流平台% C- Z' M) b8 \0 i( V! _
输入: 1314520 (二进制为00000000000101000000111011011000)
* b( d1 u. e3 E" ]; b7 G我爱电脑技术社区--打造最好的电脑技术自学交流平台 输出: 460335104 (二进制为00011011011100000010100000000000) $ H( v) |; {, j
复制内容到剪贴板 代码: var, G3 F2 f, K) [) \ r. W
x:dword;电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站/ r! b' i: V+ T, P+ u: M! B9 O
begin
( D& @: G5 C2 o- _" X* {. W7 Q3 w readln(x);
# h' R8 V* M7 m! y9 v x := (x and $55555555) shl 1 or (x and $AAAAAAAA) shr 1;打造最好的电脑自学交流论坛0 h( z1 K5 \4 Y- x; l
x := (x and $33333333) shl 2 or (x and $CCCCCCCC) shr 2;
" ^) A, ^. G. \9 d& R9 G8 Z3 D7 S: o电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 x := (x and $0F0F0F0F) shl 4 or (x and $F0F0F0F0) shr 4;
' ]$ u% z' s6 ^/ m4 K! D我爱电脑技术社区--打造最好的电脑技术自学交流平台 x := (x and $00FF00FF) shl 8 or (x and $FF00FF00) shr 8;我爱电脑技术社区--打造最好的电脑技术自学交流平台/ A g* M/ K" V# b2 V
x := (x and $0000FFFF) shl 16 or (x and $FFFF0000) shr 16;
1 f" g& b6 W( l; D( x: r& O7 T1 A打造最好的电脑自学交流论坛 writeln(x);电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站* d; N7 Y( K+ c
end.打造最好的电脑自学交流论坛% `0 u, |1 r! c p
它的原理和刚才求二进制中1的个数那个例题是大致相同的。程序首先交换每相邻两位上的数,以后把互相交换过的数看成一个整体,继续进行以2位为单位、以4位为单位的左右对换操作。我们再次用8位整数211来演示程序执行过程: 我爱电脑技术论坛; X" u2 f; ~- n8 M8 z( N$ V, F
+---+---+---+---+---+---+---+---+ 我爱电脑技术社区--打造最好的电脑技术自学交流平台8 q, V, h4 a/ S$ `* ?- V4 C
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | <---原数
7 [5 D" _; ?4 B/ x电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站+---+---+---+---+---+---+---+---+
g5 R$ W* s( ^* h| 1 1 | 1 0 | 0 0 | 1 1 | <---第一次运算后
5 R( ]; w$ @4 t+ v我爱电脑技术社区--打造最好的电脑技术自学交流平台+-------+-------+-------+-------+ 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站) `: @, V( N& F
| 1 0 1 1 | 1 1 0 0 | <---第二次运算后 打造最好的电脑自学交流论坛0 l+ T, g8 z+ s8 N5 Y9 u
+---------------+---------------+ www.520diannao.com- X0 \2 W9 ]+ O/ p7 Z
| 1 1 0 0 1 0 1 1 | <---第三次运算后
$ p6 P# e8 U! c, J+ W我爱电脑技术社区--打造最好的电脑技术自学交流平台+-------------------------------+
" r* O& U1 l' O5 r$ N- E* ?: [; U
6 N$ H0 g% I3 v. g7 z% b) ~6 l/ t3 g我爱电脑技术论坛Copyright也很强
8 V# q" t* L- r; Y" {; U' o复制内容到剪贴板 代码: writeln('Matrix' , 42 XOR 105 , '原创,转贴请注明出处');
5 F* X. x$ g8 _, T# {( iwww.520diannao.com今天我们来看两个稍微复杂一点的例子。 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 V* J8 |" a4 Y
www.520diannao.com. W& h5 U; Z( b" Q
n皇后问题位运算版电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站& r, b# v9 S' V, \) w$ ^
n皇后问题是啥我就不说了吧,学编程的肯定都见过。下面的十多行代码是n皇后问题的一个高效位运算程序,看到过的人都夸它牛。初始时,upperlim:=(1 shl n)-1。主程序调用test(0,0,0)后sum的值就是n皇后总的解数。拿这个去交USACO,0.3s,暴爽。 (转载者注:MS我用C++只有0.108s) 打造最好的电脑自学交流论坛2 j$ `. Z X* G& I
复制内容到剪贴板 代码: procedure test(row,ld,rd:longint);
( {) {( M# I/ y" S4 E( q Yvar我爱电脑技术社区--打造最好的电脑技术自学交流平台" J" L+ m2 b# S# ~1 K5 h- `
pos,p:longint;
# B+ K0 q: _& Q% z( D! F6 A, R8 ?电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站begin& [9 M2 I4 B+ l! w3 I. }$ S
8 T3 E0 p' |, t. o; X& }& |电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站{ 1} if row<>upperlim then
0 o! x5 @, F; i1 U. O; E7 H7 C电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站{ 2} begin
* c% O1 o3 V) M. H4 R' l打造最好的电脑自学交流论坛{ 3} pos:=upperlim and not (row or ld or rd);我爱电脑技术社区--打造最好的电脑技术自学交流平台0 `2 n! U/ n5 b* ?
{ 4} while pos<>0 do
+ Z$ J6 ^. w: R5 b2 W p{ 5} beginwww.520diannao.com" e+ H; T R& i% i
{ 6} p:=pos and -pos;
: y2 j4 s, l% T0 e$ {- }我爱电脑技术论坛{ 7} pos:=pos-p;电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站' V/ e/ J- V! `2 ]3 p
{ 8} test(row+p,(ld+p)shl 1,(rd+p)shr 1);我爱电脑技术论坛* s# O9 g6 f8 `, T' h
{ 9} end;我爱电脑技术社区--打造最好的电脑技术自学交流平台4 f ~5 N; Z$ ?! M7 }+ F
{10} end电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站4 E2 A) \0 d0 O7 I; @, ^+ q8 T
{11} else inc(sum);我爱电脑技术论坛# s: g+ @5 K* f+ W8 v- O5 o
$ M2 {* K, F4 Dwww.520diannao.comend;+ s, N3 d) G' l) p! E+ x
乍一看似乎完全摸不着头脑,实际上整个程序是非常容易理解的。这里还是建议大家自己单步运行一探究竟,实在没研究出来再看下面的解说。
& |: B* P7 x! \" C电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 我爱电脑技术论坛+ t* f# E* K0 F5 b- H' I1 V
/ x% {2 Z( g. u5 x `+ N. A8 M3 K$ l5 hwww.520diannao.com 和普通算法一样,这是一个递归过程,程序一行一行地寻找可以放皇后的地方。过程带三个参数,row、ld和rd,分别表示在纵列和两个对角线方向的限制条件下这一行的哪些地方不能放。我们以6x6的棋盘为例,看看程序是怎么工作的。假设现在已经递归到第四层,前三层放的子已经标在左图上了。红色、蓝色和绿色的线分别表示三个方向上有冲突的位置,位于该行上的冲突位置就用row、ld和rd中的1来表示。把它们三个并起来,得到该行所有的禁位,取反后就得到所有可以放的位置(用pos来表示)。前面说过-a相当于not a + 1,这里的代码第6行就相当于pos and (not pos + 1),其结果是取出最右边的那个1。这样,p就表示该行的某个可以放子的位置,把它从pos中移除并递归调用test过程。注意递归调用时三个参数的变化,每个参数都加上了一个禁位,但两个对角线方向的禁位对下一行的影响需要平移一位。最后,如果递归到某个时候发现row=111111了,说明六个皇后全放进去了,此时程序从第1行跳到第11行,找到的解的个数加一。
$ F% | H5 `" F$ P我爱电脑技术社区--打造最好的电脑技术自学交流平台
2 ^& g" j* v# G& e电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 ~~~~====~~~~===== 华丽的分割线 =====~~~~====~~~~ 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站! g" r$ Q! e7 @. w: k1 {' g
3 _. p7 v* Z" J( _% B电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站Gray码
7 x2 _7 u M, r' u4 h 假如我有4个潜在的GF,我需要决定最终到底和谁在一起。一个简单的办法就是,依次和每个MM交往一段时间,最后选择给我带来的“满意度”最大的MM。但看了dd牛的理论后,事情开始变得复杂了:我可以选择和多个MM在一起。这样,需要考核的状态变成了2^4=16种(当然包括0000这一状态,因为我有可能是玻璃)。现在的问题就是,我应该用什么顺序来遍历这16种状态呢?
+ a$ y8 ]* c' H1 S$ T& u电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 传统的做法是,用二进制数的顺序来遍历所有可能的组合。也就是说,我需要以0000->0001->0010->0011->0100->...->1111这样的顺序对每种状态进行测试。这个顺序很不科学,很多时候状态的转移都很耗时。比如从0111到1000时我需要暂时甩掉当前所有的3个MM,然后去把第4个MM。同时改变所有MM与我的关系是一件何等巨大的工程啊。因此,我希望知道,是否有一种方法可以使得,从没有MM这一状态出发,每次只改变我和一个MM的关系(追或者甩),15次操作后恰好遍历完所有可能的组合(最终状态不一定是1111)。大家自己先试一试看行不行。 打造最好的电脑自学交流论坛' ^8 Y# P% K; X
解决这个问题的方法很巧妙。我们来说明,假如我们已经知道了n=2时的合法遍历顺序,我们如何得到n=3的遍历顺序。显然,n=2的遍历顺序如下:
8 @. Z+ T1 F/ d8 ` _打造最好的电脑自学交流论坛我爱电脑技术论坛3 | u6 g A, u: F
00
9 u6 `/ }8 w0 E I4 u我爱电脑技术论坛01 % u" b9 c# ], d& f3 o) p& Q9 Q W
11
& Z. h/ j N3 Z( o电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站10
2 K2 W$ A: w3 s2 K7 V3 r) ~我爱电脑技术社区--打造最好的电脑技术自学交流平台
( }! s: G, u4 {0 x# ]3 I( q 你可能已经想到了如何把上面的遍历顺序扩展到n=3的情况。n=3时一共有8种状态,其中前面4个把n=2的遍历顺序照搬下来,然后把它们对称翻折下去并在最前面加上1作为后面4个状态:
9 m" y q& R, K我爱电脑技术社区--打造最好的电脑技术自学交流平台 你可能已经想到了如何把上面的遍历顺序扩展到n=3的情况。n=3时一共有8种状态,其中前面4个把n=2的遍历顺序照搬下来,然后把它们对称翻折下去并在最前面加上1作为后面4个状态:
$ M$ B# [7 x1 f! z: M% D3 ^我爱电脑技术社区--打造最好的电脑技术自学交流平台
# O7 c; }. o1 v% ^1 d; b打造最好的电脑自学交流论坛000
! B& ~' ?7 a& ~+ b/ o% f我爱电脑技术论坛001打造最好的电脑自学交流论坛' q) a5 ]8 g/ h0 K
011我爱电脑技术论坛, { \9 f* h0 A* M# b
010 ↑
4 o9 I. ]4 j3 R( s& U( G. I5 ?电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站-------- www.520diannao.com5 e* [, t4 ^+ x2 K$ h9 M& m
110 ↓
' {$ O5 `+ P* k111
; P" \2 ]; M- R, }, [0 C101我爱电脑技术论坛( @: ?& d, {: |$ z4 q0 i' p
100
7 c# |8 O" A( g9 \$ S& Q
( y+ @: P3 y3 f( M; q7 X. [ 用这种方法得到的遍历顺序显然符合要求。首先,上面8个状态恰好是n=3时的所有8种组合,因为它们是在n=2的全部四种组合的基础上考虑选不选第3个元素所得到的。然后我们看到,后面一半的状态应该和前面一半一样满足“相邻状态间仅一位不同”的限制,而“镜面”处则是最前面那一位数不同。再次翻折三阶遍历顺序,我们就得到了刚才的问题的答案:
5 Q( n+ y+ `# G$ ?www.520diannao.com我爱电脑技术论坛5 S) i+ R- l, x+ \
0000
) X# H- r" q2 g4 a6 g; q我爱电脑技术社区--打造最好的电脑技术自学交流平台0001
: u* F* z, w+ ^www.520diannao.com0011 www.520diannao.com# j; N; u" x5 L4 O9 u& b G8 y+ E
0010
s- W' i2 Z2 B! o1 z/ u5 P- _打造最好的电脑自学交流论坛0110 我爱电脑技术论坛. i& `8 }1 C' n' `" o
0111 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站2 b2 |% _6 e8 ?+ {6 l' B& d
0101 我爱电脑技术论坛2 p- {5 l$ O, k- a# C
0100
- O5 u3 l; ]% A7 T7 A# v电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站1100 打造最好的电脑自学交流论坛. N0 w+ k2 s0 o! u
1101
/ ~) |# @4 k" i* w* G( }3 z! T我爱电脑技术论坛1111 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站7 j* ^& W2 T2 p/ a
1110 我爱电脑技术论坛* \$ X. C1 B6 G/ q; l6 V7 R& J E& N3 A
1010
/ D" W& i& y. n) K( n1011 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站. {, Y5 z% ^$ o( t* X) h2 h* G
1001 我爱电脑技术论坛: V# @+ U$ ]4 D$ Y, u3 ^5 m9 T! \# M
1000 我爱电脑技术论坛) D) F& P G2 h5 h) q& h4 g
3 }0 }3 W) |: n* z9 C( m5 O我爱电脑技术论坛 这种遍历顺序作为一种编码方式存在,叫做Gray码(写个中文让蜘蛛来抓:格雷码)。它的应用范围很广。比如,n阶的Gray码相当于在n维立方体上的Hamilton回路,因为沿着立方体上的边走一步,n维坐标中只会有一个值改变。再比如,Gray码和Hanoi塔问题等价。Gray码改变的是第几个数,Hanoi塔就该移动哪个盘子。比如,3阶的Gray码每次改变的元素所在位置依次为1-2-1-3-1-2-1,这正好是3阶Hanoi塔每次移动盘子编号。如果我们可以快速求出Gray码的第n个数是多少,我们就可以输出任意步数后Hanoi塔的移动步骤。现在我告诉你,Gray码的第n个数(从0算起)是n xor (n shr 1),你能想出来这是为什么吗?先自己想想吧。 我爱电脑技术论坛* v& G. v9 e5 S; N' m% {! {" ~
www.520diannao.com) g( t. y; k! _& l5 M F
下面我们把二进制数和Gray码都写在下面,可以看到左边的数异或自身右移的结果就等于右边的数。 ; V) |, ]* X* ^
我爱电脑技术论坛0 Q f' C- r. i" R
二进制数 Gray码
& q4 c0 X p) Owww.520diannao.com 000 000
) w4 b, Z: k) l+ j4 { 001 001我爱电脑技术论坛3 g l4 |( v& H f! n E
010 011
9 `# f; g6 t6 H& J# y) L电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 011 010
9 N8 x1 z+ c. V- twww.520diannao.com 100 110
- f& u7 d* i5 \8 { f 101 111电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站- M" D# p! P; O3 m! f+ ?
110 101打造最好的电脑自学交流论坛7 I) J- ~, N4 M) u; `! o
111 100
: l6 D/ n7 I R- dwww.520diannao.com电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 Q! d3 G% J1 \( I2 c& i0 B
从二进制数的角度看,“镜像”位置上的数即是对原数进行not运算后的结果。比如,第3个数010和倒数第3个数101的每一位都正好相反。假设这两个数分别为x和y,那么x xor (x shr 1)和y xor (y shr 1)的结果只有一点不同:后者的首位是1,前者的首位是0。而这正好是Gray码的生成方法。这就说明了,Gray码的第n个数确实是n xor (n shr 1)。 电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站$ B1 h2 z m. Y% v3 T$ ~- X
打造最好的电脑自学交流论坛! A& s% a; R& e
今年四月份[]给我看了]这道题],是二维意义上的Gray码。题目大意是说,把0到2^(n+m)-1的数写成2^n * 2^m的矩阵,使得位置相邻两数的二进制表示只有一位之差。答案其实很简单,所有数都是由m位的Gray码和n位Gray码拼接而成,需要用左移操作和or运算完成。完整的代码如下: 8 C [2 b* Z/ X ~8 R8 E
复制内容到剪贴板 代码: var
# `) l$ l- N0 y电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站 x,y,m,n,u:longint;
5 z9 l( U. }+ q2 C; [* d9 K2 n& y我爱电脑技术论坛begin- ]) Q' M0 x7 |1 E! K
readln(m,n);电脑,技术,IT,学习,交流,网络安全,QQ,硬件,软件,编程,教程,建站8 F' X& N; o/ j8 ]9 [& h
for x:=0 to 1 shl m-1 do begin
4 L$ |4 n: x9 F0 G9 Q打造最好的电脑自学交流论坛 u:=(x xor (x shr 1)) shl n; //输出数的左边是一个m位的Gray码
8 q. o) w- }9 A: bwww.520diannao.com for y:=0 to 1 shl n-1 do我爱电脑技术社区--打造最好的电脑技术自学交流平台; R" d7 f1 ^, F( r4 W* O U
write(u or (y xor (y shr 1)),' '); //并上一个n位Gray码www.520diannao.com2 T# z( {4 _& c/ t
writeln;
* L: Q- M1 a# c! x+ E3 _www.520diannao.com end;打造最好的电脑自学交流论坛1 J9 ~+ q+ j! q$ {5 ~
end.我爱电脑技术论坛! Z& a% m3 G5 l& ^5 `( u+ u1 @8 c
|