Gray Code
Problem Overview
| Attribute | Value |
|---|---|
| Difficulty | Medium |
| Category | Bit Manipulation |
| Time Limit | 1 second |
| Key Technique | XOR Formula / Recursive Construction |
| CSES Link | Gray Code |
Learning Goals
After solving this problem, you will be able to:
- Understand the mathematical formula for Gray code:
i ^ (i >> 1) - Apply bit manipulation to generate sequences with specific properties
- Recognize the mirror/reflection property of Gray codes
- Convert between binary and Gray code representations
Problem Statement
Problem: Generate a Gray code sequence of n-bit strings where consecutive strings differ by exactly one bit.
Input:
- Line 1: An integer n (the number of bits)
Output:
- Print 2^n lines, each containing an n-bit Gray code string
- Any valid Gray code sequence is accepted
Constraints:
- 1 <= n <= 16
Example
Input:
3
Output:
000
001
011
010
110
111
101
100
Explanation: Each consecutive pair differs by exactly one bit:
- 000 -> 001 (bit 0 changes)
- 001 -> 011 (bit 1 changes)
- 011 -> 010 (bit 0 changes)
- 010 -> 110 (bit 2 changes)
- 110 -> 111 (bit 0 changes)
- 111 -> 101 (bit 1 changes)
- 101 -> 100 (bit 0 changes)
Intuition: How to Think About This Problem
Pattern Recognition
Key Question: How can we generate a sequence where adjacent values differ by exactly one bit?
The crucial insight is that Gray code has a direct mathematical formula: for any integer i, its Gray code is i ^ (i >> 1). This formula naturally produces a sequence where consecutive values differ by exactly one bit.
Why Does i ^ (i >> 1) Work?
Consider what happens when we go from i to i + 1:
- When incrementing, the rightmost 0 bit becomes 1, and all bits to its right (which were 1s) become 0
- The XOR with the right-shifted value “smooths” this transition, ensuring only one bit changes
Example: i = 5 (binary: 101)
i = 101
i >> 1 = 010
XOR = 111 (Gray code for 5)
i = 6 (binary: 110)
i = 110
i >> 1 = 011
XOR = 101 (Gray code for 6)
111 and 101 differ by exactly one bit!
Alternative: Mirror Construction
Another way to understand Gray code is through the mirror property:
- For n=1: [0, 1]
- For n bits: Take (n-1)-bit Gray code, prepend 0 to all, then prepend 1 to the reversed sequence
n=1: [0, 1]
n=2: [00, 01] + [11, 10] = [00, 01, 11, 10]
n=3: [000, 001, 011, 010] + [110, 111, 101, 100]
Solution 1: XOR Formula (Optimal)
Key Insight
The Trick: Gray code for integer i is simply
i ^ (i >> 1). This O(1) formula generates each code directly.
Algorithm
- Loop through all integers from 0 to 2^n - 1
- For each integer i, compute Gray code as
i ^ (i >> 1) - Convert the result to an n-bit binary string
Dry Run Example
Let’s trace through with input n = 3:
For n = 3, we generate 2^3 = 8 codes (indices 0 to 7)
i = 0: 0 ^ (0 >> 1) = 0 ^ 0 = 0 = 000
i = 1: 1 ^ (1 >> 1) = 1 ^ 0 = 1 = 001
i = 2: 2 ^ (2 >> 1) = 2 ^ 1 = 3 = 011
i = 3: 3 ^ (3 >> 1) = 3 ^ 1 = 2 = 010
i = 4: 4 ^ (4 >> 1) = 4 ^ 2 = 6 = 110
i = 5: 5 ^ (5 >> 1) = 5 ^ 2 = 7 = 111
i = 6: 6 ^ (6 >> 1) = 6 ^ 3 = 5 = 101
i = 7: 7 ^ (7 >> 1) = 7 ^ 3 = 4 = 100
Verification (adjacent codes differ by 1 bit):
000 -> 001: bit 0 flips
001 -> 011: bit 1 flips
011 -> 010: bit 0 flips
010 -> 110: bit 2 flips
110 -> 111: bit 0 flips
111 -> 101: bit 1 flips
101 -> 100: bit 0 flips
Visual Diagram
Index (i) Binary i >> 1 XOR Result Gray Code
--------- ------ ------ ---------- ---------
0 000 000 000 000
1 001 000 001 001
2 010 001 011 011
3 011 001 010 010
4 100 010 110 110
5 101 010 111 111
6 110 011 101 101
7 111 011 100 100
XOR operation visualized for i=6:
1 1 0 (i = 6)
^ 0 1 1 (i >> 1 = 3)
-------
1 0 1 (Gray code = 5)
Code
Python:
def solve():
n = int(input())
for i in range(1 << n): # 1 << n = 2^n
gray = i ^ (i >> 1)
print(format(gray, f'0{n}b'))
solve()
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(2^n * n) | 2^n codes, each taking O(n) to print |
| Space | O(1) | No additional data structures needed |
Solution 2: Recursive Mirror Construction
Key Insight
The Trick: Build n-bit Gray code by prepending 0 to (n-1)-bit code, then prepending 1 to its reverse.
Algorithm
- Base case: 1-bit Gray code is [“0”, “1”]
- Recursively get (n-1)-bit Gray code
- Prepend “0” to all codes
- Prepend “1” to reversed codes
- Concatenate both lists
Code
Python:
def gray_code_recursive(n):
if n == 1:
return ['0', '1']
prev = gray_code_recursive(n - 1)
# Prepend 0 to original, prepend 1 to reversed
return ['0' + code for code in prev] + ['1' + code for code in reversed(prev)]
def solve():
n = int(input())
for code in gray_code_recursive(n):
print(code)
solve()
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(2^n * n) | Building 2^n strings of length n |
| Space | O(2^n * n) | Storing all Gray code strings |
Common Mistakes
Mistake 1: Wrong Bit Shift Direction
# WRONG
gray = i ^ (i << 1) # Left shift instead of right shift
# CORRECT
gray = i ^ (i >> 1) # Right shift
Problem: Left shift produces incorrect results; the formula specifically requires right shift.
Fix: Always use right shift (>>) in the Gray code formula.
Mistake 2: Incorrect Binary Padding
# WRONG
print(bin(gray)[2:]) # Missing leading zeros
# CORRECT
print(format(gray, f'0{n}b')) # Properly padded to n bits
Problem: Without padding, codes like 1 print as “1” instead of “001” for n=3. Fix: Use format specifier to ensure n-bit output.
Mistake 3: Off-by-One in Loop Range
# WRONG
for i in range(2**n - 1): # Missing the last code
# CORRECT
for i in range(2**n): # Or equivalently: range(1 << n)
Problem: Missing the last Gray code in the sequence. Fix: Loop should cover all 2^n values from 0 to 2^n - 1.
Edge Cases
| Case | Input | Expected Output | Why |
|---|---|---|---|
| Minimum n | n = 1 | 0 then 1 |
Simplest Gray code |
| Power of 2 codes | n = 2 | 4 codes | 00, 01, 11, 10 |
| Maximum n | n = 16 | 65536 codes | Tests efficiency |
| Single bit change | Any n | Adjacent differ by 1 bit | Core Gray code property |
When to Use This Pattern
Use This Approach When:
- Generating sequences where adjacent elements differ minimally
- Encoding data for error reduction in transmission
- Solving Hamiltonian path problems on hypercubes
- Tower of Hanoi variations
Don’t Use When:
- Standard binary representation is required
- Order of bits must follow natural binary ordering
- Problem requires different adjacency constraints
Pattern Recognition Checklist:
- Need sequence where neighbors differ by exactly 1 bit? -> Gray code
- Need to enumerate subsets in minimal-change order? -> Gray code
- Need Hamiltonian path on n-dimensional hypercube? -> Gray code
Related Problems
Easier (Do These First)
| Problem | Why It Helps |
|---|---|
| Bit Strings | Basic bit counting |
| Two Sets | Understanding binary subsets |
Similar Difficulty
| Problem | Key Difference |
|---|---|
| Gray Code (LeetCode 89) | Same concept, return as integers |
| Circular Permutation | Gray code with specific starting point |
Harder (Do These After)
| Problem | New Concept |
|---|---|
| Creating Strings | Permutation generation |
| Apple Division | Subset enumeration with optimization |
Key Takeaways
- The Core Idea: Gray code for integer i is
i ^ (i >> 1)- a simple XOR operation - Time Optimization: Direct formula gives O(1) per code vs O(2^n) backtracking
- Space Trade-off: XOR approach uses O(1) space; recursive uses O(2^n)
- Pattern: This belongs to the “bit manipulation” pattern family
Practice Checklist
Before moving on, make sure you can:
- Write the XOR formula from memory:
i ^ (i >> 1) - Explain why adjacent Gray codes differ by exactly one bit
- Implement both iterative and recursive solutions
- Handle proper binary string formatting with leading zeros
Additional Resources
- CP-Algorithms: Gray Code
- Wikipedia: Gray Code
- CSES Gray Code - Binary code generation