
。這里的g(k)表示在格雷碼表里排在第k位的格雷碼。其實(shí),因?yàn)?
以
開頭,格雷碼的無限序列是所有非負(fù)整數(shù)的某個(gè)排列:
包含公式(4)中最前面的2n個(gè)整數(shù)。只不過每個(gè)整數(shù)前可能填充0,使之長度為N。再把整數(shù)轉(zhuǎn)換成字符串,就生成了相應(yīng)的格雷碼。比如說當(dāng)N=3時(shí),第二位格雷碼是001,相當(dāng)于在公式(4)里的第二個(gè)整數(shù)(1)2前面補(bǔ)上兩個(gè)0后將其轉(zhuǎn)換為字符串,使得該字符串的長度等于3。
時(shí),我們可以從公式(3)得出g(k) = 2n + g(2n – 1 – r )。這個(gè)結(jié)論的推導(dǎo)也挺容易:既然k > 2n, 那么我們知道g(k)一定在
里面。既然
表示將
反轉(zhuǎn),那
中對應(yīng)第r位的格雷碼剛和等于
中第2n-r-1位的格雷碼。最后,在g(r)前添一個(gè)1,正好相當(dāng)于g(r)+(1000…000)2,共n個(gè)0。換成10進(jìn)制,就得到g(k) = 2n + g(2n – 1 – r )了。根據(jù)這個(gè)公式,我們可以得出一個(gè)非常有用的結(jié)論:給出整數(shù)k和它的二進(jìn)制形式(…b2b1b0)2,以及第k位的格雷碼g(k)的二進(jìn)制形式(…a2a1a0)2,我們可以用數(shù)學(xué)歸納法證明下面的關(guān)系:
表示位運(yùn)算中的異或運(yùn)算。也就是說,只有當(dāng)兩個(gè)位不一樣時(shí)他們異或的結(jié)果為1。不然就為0。比如說,當(dāng)k = 3651時(shí),它的二進(jìn)制形式是(111001000011)2。那第k位的格雷碼g(k)就是 k ^ (k >> 1) = 2402 = (100101100010)2。證明其實(shí)也不難,利用公式g(k) = 2n + g(2n – 1 – r )對n做歸納就行。有興趣的老大們可以自己去做。網(wǎng)上也有答案。
從j開始展開,看看能得到什么:



等于0, 而0在異或運(yùn)算中是identity, 也就是說任何一個(gè)值與0異或還是等于自身。這個(gè)等式看來是無窮項(xiàng),但因?yàn)?
遲早會(huì)等于0, 所以它其實(shí)是有限的,可以計(jì)算。
對應(yīng)編程里的位移運(yùn)算:k >> 1,所以實(shí)現(xiàn)起來也方便。我們立刻有了新的算法,異常簡潔:defgray_g(n)
return [] if n ==0
return (0..((1<<n) -1)).collect{|k|sprintf(“%0#{n}b”, (k ^ (k>>1)))}
end


def gray_pos(gray_code)
pos = g = gray_code.to_i(2)
while g > 0
g >>= 1
pos ^= g
end
return pos
end
(從TAOCP截的圖)
。參考公式(6),這相當(dāng)于把把k變成
,因?yàn)橹挥衚0變了。顯然我們希望當(dāng)k的最后一位是1,也就是當(dāng)k為奇數(shù)時(shí)按規(guī)則a)執(zhí)行。這樣k變成k-1,離我們的目標(biāo)近了一步。另外一方面,根據(jù)規(guī)則b),只有形為(…x100…00)2的九連環(huán)可以移動(dòng)x代表的那個(gè)環(huán)。換句話說,如果一個(gè)長度為n的九連環(huán)里某個(gè)環(huán)所在位置以(10j-1)2結(jié)尾(這里(10j-1)2表示一個(gè)二進(jìn)制數(shù),第一位是1,后面跟了j – 1個(gè)0), 1 <= j < n, 則該環(huán)可以移動(dòng)。該環(huán)對應(yīng)aj+1, 所以移動(dòng)后該環(huán)的狀態(tài)變成
。根據(jù)公式(6),我們知道bj, bj-1,… , b0, 都包含aj+1項(xiàng)。所以說移動(dòng)該環(huán)后,k變成了
。舉個(gè)例子哈。上面九連環(huán)圖里從右數(shù)第3個(gè)環(huán)可以被解下。用數(shù)字來表達(dá),就是(1011000)2可以變成(1001000)2。具體到右數(shù)第三左數(shù)第5個(gè)環(huán),我們可以說a4從1變成0。解環(huán)前k等于gray_pos(0b101100) = 55=(110111)2, 解下環(huán)后k變成了
= gray_pos(0b100100) = 56 = (111000)2。同執(zhí)行規(guī)則a)一樣,我們希望執(zhí)行規(guī)則b)后k變?。?
應(yīng)該等于k – 1。要達(dá)到這個(gè)目標(biāo),k必須是2j的倍數(shù),但不能是2j+1的倍數(shù)。這是因?yàn)樾稳?xxx…10j)2的數(shù)才是2j的倍數(shù)。它和2j+1-1異或后,才能等于(xxx…01j )2,也就是k-1。我們可以把j和k的關(guān)系表達(dá)為:
叫作ruler function,有興趣的老大們可以參見這里的第13頁,印張第8頁,公式32到45。那節(jié)專講位運(yùn)算的技巧,可以和IBM某編譯器牛人出的Hacker‘s Delight以及這個(gè)網(wǎng)頁一塊兒看。做嵌入式的老大和雅好底層優(yōu)化的老大們應(yīng)該可以獲益不少。不過這里俺就不跑題了。我們只需要知道
返回k靠右連續(xù)0的個(gè)數(shù)。比如說
。從九連環(huán)可以自由移動(dòng)的那頭給環(huán)的移動(dòng)次序編號(hào):0, 1, 2, …, , 那把環(huán)的狀態(tài)從00000…0變成1111…11的移動(dòng)順序就是
。那對應(yīng)的算法也就很直觀了:設(shè)定長度為n的序列,該算法從(0, 0, 0…, 0)開始,逐次訪問每個(gè)組合:(an-1, …, a0)2。每一步只改變一位,相當(dāng)于移動(dòng)一個(gè)環(huán)。對每一步i, 移動(dòng)對應(yīng)的環(huán)相當(dāng)于找到
對應(yīng)的環(huán),然后對它的值求補(bǔ)(異或)。我們也不需要每次通過求異或判斷奇偶來決定執(zhí)行規(guī)則a)還是規(guī)則b):維護(hù)一個(gè)奇偶值,parity就行了:

32: def alg_g(n)
33: step = Array.new(n, 0)
34: result = []
35: parity = 0
36:
37: loop do
38: result << step.join
39: parity = 1 - parity
40:
41: if parity == 1
42: j = 0
43: else
44: 0.upto(n-1) do |i|
45: if step[i] == 1
46: j = i+1
47: break
48: end
49: end
50: end
51:
52: return result.reverse if j == n
53:
54: step[j] = 1 - step[j]
55: end
56: end


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1558391




