You only need four bytes
You can fit the whole thing into an unsigned, 32-bit int, which is only four bytes.
To pack a series of values you can usually use multiplication and addition, like this:
uint encodedValue = 0;
foreach (int element in input)
{
encodedValue *= 11;
encodedValue += element;
}
This would encode a stream of numbers between 0 and 10 inclusive. You then run a similar loop to decode, but in reverse, and instead of multiplying you divide, and instead of adding you take the modulus.
for (int i=0; i<11; i++)
{
output[10-i] = encodedValue % 11;
output /= 11;
}
Hopefully this procedure is somewhat familiar to you (although usually the base is 16 or 256, and is often done with logical shift left/right and logical AND instead of modulus. Both of these are just shortcuts. The underlying math is the same.)
But we can do better. While the first element can be any of 11 possible values (and therefore has an "alphabet" with count = 11), we know that the second element will be one of 10 possible values (with an alphabet with count = 10). Each step reduces the size of the alphabet. So you can reduce the multiplier as you go.
static uint Encode(byte[] input)
{
var alphabet = Enumerable.Range(0,11).ToList();
uint encodedValue = 0;
byte multiplier = 10;
for (int i=0; i<10; i++)
{
var element = input[i];
var index = alphabet.IndexOf(element);
alphabet.RemoveAt(index);
encodedValue += (uint)index;
encodedValue *= (multiplier--);
}
return encodedValue;
}
Notice we are only encoding ten values. The eleventh can be inferred because the other ten possible values are already taken.
The output of this process is a single, unsigned 32-bit integer, which requires only four bytes.
Decoding is a little more difficult since we have to reproduce the original alphabets, which has to be done forwards, even though everything else is backwards. So we do one pass backwards (to reverse the math) and one pass forward (to determine values based on the alphabet).
static byte[] Decode(uint input)
{
var result = new byte[11];
uint divisor = 2;
var tmp = new int[11];
var alphabet = Enumerable.Range(0,11).ToList();
for (int i=0; i<10; i++)
{
tmp[i] = (int)(input % divisor);
input /= (divisor++);
}
for (int j=9; j>=0; j--)
{
var index = tmp[j];
var number = alphabet[index];
result[9-j] = (byte)number;
alphabet.RemoveAt(index);
}
result[10] = (byte)alphabet[0];
return result;
}
Here is a working example on DotNetFiddle that demonstrates the procedure. You can click "Run" repeatedly to see that the decoded value always matches the input, even though the only data passed to it is a uint
.