From Gossip@caterpillar

Algorithm Gossip: 格雷碼(Gray Code)

說明

Gray Code是一個數列集合,每個數使用二進位來表示,假設使用n位元來表示每個數好了,任兩個數之間只有一個位元值不同,例如以下為3位元的Gray Code:
000 001 011 010 110 111 101 100

由定義可以知道,Gray Code的順序並不是唯一的,例如將上面的數列反過來寫,也是一組Gray Code:
100 101 111 110 010 011 001 000

Gray Code是由貝爾實驗室的Frank Gray在1940年代提出的,用來在使用PCM(Pusle Code Modulation)方法傳送訊號時避免出錯,並於1953年三月十七日取得美國專利。

解法

由於Gray Code相鄰兩數之間只改變一個位元,所以可觀 察Gray Code從1變0或從0變1時的位置,假設有4位元的Gray Code如下:
0000 0001 0011 0010 0110 0111 0101 0100
1100 1101 1111 1110 1010 1011 1001 1000

觀察奇數項的變化時,我們發現無論它是第幾個Gray Code,永遠只改變最右邊的位元,如果是1就改為0,如果是0就改為1。

觀察偶數項的變化時,我們發現所改變的位元,是由右邊算來第一個1的左邊位元。

以上兩個變化規則是固定的,無論位元數為何;所以只要判斷位元的位置是奇數還是偶數,就可以決定要改變哪一個位元的值,為了程式撰寫方便,將陣列索引 0當作最右邊的值,而在列印結果時,是由索引數字大的開始反向列印。

將2位元的Gray Code當作平面座標來看,可以構成一個四邊形,您可以發現從任一頂點出發,繞四邊形周長繞一圈,所經過的頂點座標就是一組Gray Code,所以您可以得到四組Gray Code。

同樣的將3位元的Gray Code當作平面座標來看的話,可以構成一個正立方體,如果您可以從任一頂點出發,將所有的邊長走過,並不重複經過頂點的話,所經過的頂點座標順序之組合也就是一組Gray Code。


實作:C    Java    Python    Scala    Ruby

  • C
#include <stdio.h> 
#include <stdlib.h>

#define MAXBIT 20
#define TRUE 1
#define CHANGE_BIT(x) x = ((x) == '0' ? '1' : '0')
#define NEXT(x) x = (1 - (x))

int main(void) {
char digit[MAXBIT];
int bits;
printf("輸入位元數:");
scanf("%d", &bits);

int i;
for(i = 0; i < bits; i++) {
digit[i] = '0';
printf("0");
}

printf("\n");

int odd = TRUE;

while(1) {
if(odd)
CHANGE_BIT(digit[0]);
else {
// 計算第一個1的位置
int j;
for(j = 0; j < bits && digit[j] == '0'; j++) ;
if(j == bits - 1) // 最後一個Gray Code
break;
CHANGE_BIT(digit[j + 1]);
}
int j;
for(j = bits - 1; j >= 0; j--)
printf("%c", digit[j]);

printf("\n");
NEXT(odd);
}
return 0;
}

  • Java
import java.util.*;
public class GrayCode implements Iterable<char[]>, Iterator<char[]> {
private char[] digit;
private boolean first = true;
private boolean odd = true;

public GrayCode(int length) {
digit = new char[length];
for(int i = 0; i < length; i++) {
digit[i] = '0';
}
}

public Iterator<char[]> iterator() {
return this;
}

public boolean hasNext() {
return count() != digit.length - 1;
}

public char[] next() {
if(first)
first = false;
else {
if(odd) charge(0);
else if(hasNext()) charge(count() + 1); // 最後一個 Gray Code
odd = ! odd;
}
return reverse();
}

public void remove() {
throw new RuntimeException("Unsupported operation");
}

private int count() {
int count;
// 計算第一個1的位置
for(count = 0;
(count < digit.length) && (digit[count] == '0');
count++) ;
return count;
}

private void charge(int i) {
digit[i] = ((digit[i] == '0') ? '1' : '0');
}

private char[] reverse() {
char[] tmp = new char[digit.length];
int i = digit.length - 1;
for(char c : digit) {
tmp[i] = c;
i--;
}
return tmp;
}

public static void main(String[] args) {
for(char[] digit : new GrayCode(4)) {
for(char c: digit)
System.out.print(c);
System.out.println();
}
}
}

  • Python
class GrayCode:
def __init__(self, length):
self.__digit = ['0'] * length
self.__first = True
self.__odd = True

def __iter__(self):
return self

def __next__(self):
if not self.__hasNext():
raise StopIteration

if self.__first:
self.__first = False
else:
if self.__odd:
self.__charge(0)
else:
if self.__hasNext():
self.__charge(self.__count() + 1)
self.__odd = not self.__odd
return self.__reverse()

def __hasNext(self):
return self.__count() != len(self.__digit) - 1

def __charge(self, i):
if self.__digit[i] == '0':
self.__digit[i] = '1'
else:
self.__digit[i] = '0'

def __count(self):
count = 0
while(count < len(self.__digit) and self.__digit[count] == '0'):
count += 1
return count

def __reverse(self):
tmp = []
for i in range(len(self.__digit) - 1, -1, -1):
tmp.append(self.__digit[i])
return tmp

for digit in GrayCode(4):
for i in range(0, len(digit)):
print(digit[i], end="")
print()

  • Scala
class GrayCode(length: Int) extends Iterator[Array[Char]] {
private var first = true
private var odd = true
private val digit = (for(i <- 0 until length) yield '0').toArray

private def count() = {
var c = 0
while(c < digit.length && digit(c) == '0') {
c += 1
}
c
}

private def charge(i: Int) {
digit(i) = if(digit(i) == '0') '1' else '0'
}

def hasNext() = {
count != digit.length - 1
}

def next() = {
if(first) {
first = false
}
else {
if(odd) charge(0)
else if(hasNext()) charge(count + 1)
odd = !odd
}
digit.reverse
}
}

for(digit <- new GrayCode(4)) {
digit.foreach(print)
println()
}

  • Ruby
class GrayCode
def initialize(length)
@digit = Array.new(length, "0")
@first = true
@odd = true
end

def each
while hasNext
if @first
@first = false
else
if @odd
charge(0)
else
if hasNext
charge(count() + 1)
end
end
@odd = ! @odd
end
yield(reverse)
end
end

def hasNext
count() != @digit.length - 1
end
private :hasNext

def charge(i)
if @digit[i] == "0"
@digit[i] = "1"
else
@digit[i] = "0"
end
end
private :charge

def count
c = 0
while c < @digit.length && @digit[c] == "0"
c += 1
end
c
end
private :count

def reverse
tmp = []
(@digit.length - 1).downto(0) { |i|
tmp << @digit[i]
}
tmp
end
private :reverse
end

GrayCode.new(4).each { |code|
code.each { |b|
print b
}
puts
}