에듀테크 프로젝트 대시보드
그레이 코드 (Gray Code)
보통 이진수는 7 → 8 처럼 값이 조금만 올라도 0111 → 1000 으로 네 비트가 한꺼번에 뒤집힌다. 그레이 코드는 순서를 재배열해서 이웃한 값끼리 항상 딱 1비트만 다르게 만든 코드다. 로터리 엔코더, 카노 맵, ADC, 유전 알고리즘에서 쓰인다.
0111 → 1000 순간, 아주 짧게 1111(=15)이나 0000(=0) 같은 엉뚱한 값이 읽힐 수 있다. 1비트만 바뀌면 그런 과도상태 오류가 생기지 않는다.
0부터 15까지 셀 때, 한 칸 올라갈 때마다 바뀐 비트를 빨갛게 표시했다. 이진 코드의 "비트 변화 합계"와 그레이 코드의 합계를 비교해 보자.
변환은 XOR 한 방이면 된다. 이진수를 오른쪽으로 1칸 민 것과 원래 값을 XOR 한다:
거꾸로 그레이 → 이진은 앞 비트부터 누적 XOR:
위 이진수 비트를 클릭해 켜고 끄면, 아래 그레이 코드가 실시간으로 계산된다.
회전하는 원판에 동심원 트랙을 파고, 각 각도마다 광센서 3개가 트랙의 검정/흰색을 읽어 3비트 값을 만든다. 원판을 드래그해서 돌려 보자. 이진 원판은 경계에서 센서 정렬이 조금만 어긋나도 순간적으로 튀는 값을 뱉는다. 그레이 원판은 어떤 경계에서도 1비트만 바뀌므로 최악이라도 인접한 두 값 중 하나로만 읽힌다.
회전 각도: 0°
센서 3개 raw 판독: 000
디코딩된 위치: 0 / 실제 섹터: 0
—
"정렬 오차"를 올리면 3개의 센서가 트랙 경계를 약간씩 다른 타이밍에 넘는 상황을 흉내 낸다.
그레이 코드의 정식 명칭은 반사 이진 코드다. n비트 코드에서 (n+1)비트 코드를 만드는 규칙:
거울로 뒤집었기 때문에 이어 붙이는 경계에서도 (접두 비트를 뺀) 나머지가 똑같아 1비트만 바뀐다. 목록의 처음과 끝도 1비트만 다르다(순환 코드).
# 정수 → 그레이 코드 정수 def to_gray(n): return n ^ (n >> 1) # 그레이 코드 정수 → 원래 정수 (앞에서부터 누적 XOR) def from_gray(g): b = g while g > 0: g >>= 1 b ^= g return b # n비트 그레이 코드 순서 전체 (반사 구성) def gray_sequence(bits): seq = ["0", "1"] for _ in range(bits - 1): seq = ["0" + x for x in seq] + ["1" + x for x in reversed(seq)] return seq print([to_gray(i) for i in range(8)]) # [0, 1, 3, 2, 6, 7, 5, 4]
b ^ (b >> 1) 이 1비트 규칙을 보장할까? — 이진수에서 k와 k+1은 "맨 뒤에 이어진 1들과 그 앞 0 한 개"가 뒤집힌다(예: 00111 → 01000). 이 값을 x >> 1 과 XOR 하면, 뒤집힌 비트 덩어리가 이웃 비트와 상쇄되어 정확히 한 자리만 살아남는다.
로터리/리니어 엔코더 — 모터 축 각도, CNC 위치 센서. 절대위치 엔코더 원판은 거의 그레이 코드다.
카노 맵 (Karnaugh Map) — 행·열 라벨을 그레이 순서(00 01 11 10)로 놓아, 이웃 칸이 항상 1변수만 달라지게 만든다. → 카노 맵
ADC / 통신 — 아날로그 양자화 경계에서 발생하는 비트 오류를 ±1 LSB로 제한한다.
유전 알고리즘 — 실수 파라미터를 그레이 코드로 인코딩하면 작은 변이(1비트 뒤집기)가 작은 값 변화로 이어져 "해밍 절벽"을 피한다.
클록 도메인 크로싱 — 비동기 FIFO의 읽기/쓰기 포인터를 그레이 코드로 전달하면, 다른 클록에서 샘플링해도 최대 1비트 오차라 안전하다.