It seems, that you are looking for Gray codes
C# code
public static IEnumerable<int[]> GrayCodes(int length, int radix) {
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length));
if (radix < 0)
throw new ArgumentOutOfRangeException(nameof(radix));
if (0 == length || 0 == radix)
yield break;
static int digit(long n, int radix, int i) =>
(int)(Math.Floor(n / Math.Pow(radix, i)) % radix);
double count = Math.Pow(radix, length);
long max = count > long.MaxValue ? long.MaxValue : (long)count;
for (long i = 0; i < max; ++i) {
int[] result = new int[length];
int shift = 0;
for (int j = length - 1; j >= 0; j--) {
var x = (digit(i, radix, j) + shift) % radix;
shift += radix - x;
result[length - j - 1] = x;
}
yield return result;
}
}
Demo
var report = string.Join(Environment.NewLine, GrayCodes(2, 3)
.Select(g => string.Join(" ", g)));
Console.Write(report);
Outcome:
0 0
0 1
0 2
1 2
1 0
1 1
2 1
2 2
2 0
If you want to flatten the sequence, add SelectMany:
var report = string.Join(", ", GrayCodes(2, 3).SelectMany(g => g));
Console.WriteLine(report);
Console.WriteLine();
// Your case, length = 3, radix = 2
report = string.Join(", ", GrayCodes(3, 2).SelectMany(g => g));
Console.WriteLine(report);
Outcome:
0, 0, 0, 1, 0, 2, 1, 2, 1, 0, 1, 1, 2, 1, 2, 2, 2, 0
0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0
Edit: If "only one change at every step" means not only "in one position" (so 2 1 0 -> 2 1 2 is allowed) but in "in one position and by +1 or -1 only" you can still use Gray code. The algorithm (let me name it "To and Fro") is below.
- Start from all zeroes
0...000
- Start incrementing
nth digit while it's possible: 0..00 -> 0..01 -> .. -> 0..00dn
- Increment
n-1th digit and start decrementing nth digit: 0..00dn -> 0..01dn -> ... 0..011 -> 0..10
- Increment
n-1th digit and start incrementing nth digit: 0..10 -> 0..20 -> 0..021 -> ...0..2dn
- On
0..0dn-1dn increment n-2th index and start decrementing nth etc.
Demo
# start from 000
000 -> 001 -> 002 ->
# increment n-1 th digit, decrementing n-th
-> 012 -> 011 -> 010 ->
# increment n-1 th digit, incrementing n-th
-> 020 -> 021 -> 022 ->
# increment n-2 th digit, decrementing n-th
-> 122 -> 121 -> 120 ->
# decrement n-1 th digit, incrementing n-th
-> 110 -> 111 -> 112 ->
# decrement n-1 th digit, decrementing n-th
-> 102 -> 101 -> 100 ->
# increment n-2 th digit, incrementing n-th
-> 200 -> 201 -> 202 ->
# increment n-1 th digit, decrementing n-th
-> 212 -> 211 -> 210 ->
# increment n-1 th digit, incrementing n-th
-> 220 -> 221 -> 222
C# Code: (Please, fiddle)
public static IEnumerable<int[]> GrayCodes(int length, int radix) {
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length));
if (radix < 0)
throw new ArgumentOutOfRangeException(nameof(radix));
if (0 == length || 0 == radix)
yield break;
int[] signs = Enumerable.Repeat(1, length).ToArray();
int[] current = new int[length];
for (bool keep = true; keep; ) {
yield return current.ToArray();
keep = false;
for (int i = current.Length - 1; i >= 0; --i) {
int d = current[i] + signs[i];
if (d >= 0 && d < radix) {
current[i] = d;
for (int j = i + 1; j < signs.Length; ++j)
signs[j] = -signs[j];
keep = true;
break;
}
}
}
}
Demo:
Console.Write(string.Join(Environment.NewLine, GrayCodes(3, 3)
.Select(line => string.Join(" ", line))));
Outcome:
0 0 0
0 0 1
0 0 2
0 1 2
0 1 1
0 1 0
0 2 0
0 2 1
0 2 2
1 2 2
1 2 1
1 2 0
1 1 0
1 1 1
1 1 2
1 0 2
1 0 1
1 0 0
2 0 0
2 0 1
2 0 2
2 1 2
2 1 1
2 1 0
2 2 0
2 2 1
2 2 2