Standard Huffman coding is inefficient for streams of natural numbers when:

  • The range is large or unbounded,
  • Frequencies are flat (e.g. each number appears once),
  • The integers to encode are large.

This motivates the design of universal codes: prefix-free binary encodings optimized for all positive integers, under the assumption that smaller values are more likely.

Minimal Binary Code

The minimal binary code of a number is its standard binary representation without leading zeros, ensuring the most significant bit is always 1.

For example, the minimal binary code of is just 100011001 with no leading zeroes.

Elias Omega Code For Positive Integers

Elias Omega encoding assigns prefix-free binary codewords to any positive integer . To perform this method, define:

  1. as the minimal binary codeword of a positive integer, and
  2. as the length (number of bits) of a binary string.

The codeword for any positive integer is then built recursively as a concatenation of minimal binary representations:

Where each is the length of the minimal binary representation of the previous length, where is the length of the minimal binary representation of the input integer . In other words:

The recursion continues until , i.e. the final encoded length is only one bit long.

???

Lets denote the binary code for each as length components and that of as the code component

Starting from the code component, the number of bits is floor(log binary(N)) + 1, i.e. . Each subsequent length component is another logarithmic surrounding i.e. is or , is , and this is continues until

The minus one in guarantees that the length of the code component is strictly greater than length of the length components, and each length component is strictly greater than the length of the preceding length component

Note that the codeword is called code (why?) due to its repeated , and these log star codes are shown to be universal codes for positive integers.

However this is not prefix free.

Decoding can be problematic using just minimal binary codes. Encoding and integers using directly their minimal binary codes poses a problem. During decoding, we cannot deifferentiate between the length components and the actual code component of is no prefix free, due to the use of minimal binary codes,

Issues With This Representation

The resultant binary string poses some issues however, first of all, there is no method of differentiating the length components from each other and from the code component.

For ,

Suppose we are given the Elias Omega encoded string for :

Decoding proceeds by interpreting the sequence as a chain of length components followed by the original value .

When reading the first character, we do not know whether 1 could be 1, or the start of 11, or of 111, etc.

In general, it is impossible to delineate the boundaries of each component

Additionally, another issue that arises is that these binary strings are not prefix-free because all parts, both the length components and the actual number, are encoded using minimal binary, which lacks self-delimiting structure. Minimal binary codes can be prefixes of one another (e.g., 1 is a prefix of 10), so when these are concatenated without boundaries, there’s no way to unambiguously tell where one component ends and the next begins. As a result, one codeword could be the prefix of another, violating prefix-freeness and making decoding ambiguous.

Differentiating Components & Ensuring Prefix Freeness

To make the binary codeword prefix free, as minimal binary codes have their most significant bit equal to 1, a signalling method can be used to differentiate length components, by flipping the most significant bit to zero.

For , we would thus have

If the code used only minimal binary representations, it wouldn’t be prefix-free. This is because one number’s code can be the prefix of another’s, for example, the binary code for is 1, which is a prefix of , whose code is 10.

Even when we concatenate length components (which are also minimal binary codes) before flipping any bits, prefix-freeness still fails. For instance, the Elias Omega code for is 1, while that for is 110; clearly, the codeword for is a prefix of that for .

Prefix-freeness is restored by modifying the encoding: flip the most significant bit of each length component from 1 to 0. This guarantees that all length components begin with a 0, while the final code component (the actual number) always begins with a 1. This clean separation makes it impossible for a length component to be confused with a code component, ensuring no codeword is a prefix of another.

This works because minimal binary codes have no leading zeroes, so any component that begins with 0 must be a length component, and any segment that begins with 1 must be the final value. Since the Elias Omega code builds up in increasing bit-length, prefix conflicts are structurally avoided.

For

The binary representation is , thus we have:

The length , thus . As , flipping the most significant bit results in .

The length , thus . As , flipping the most significant bit results in .

The length , thus . As , flipping the most significant bit results in .

The length , therefore stop, the above is your prefix-free code word.

Decoding Elias Omega Codes

When decoding a variable-length encoded integer, we don’t know how many length components the codeword and what is. But we do know that the very last length component, , in the codeword has length = (1)dec, and it is encoded with 0 bit.

But note that the most-significant bit of the component can be 1, only when

Input: codeword[1. . . ]
Initialize: readlen = (1)dec, component = , pos = 1
component = codeword[pos . . . pos + readlen − 1]

If the most-significant bit of component is 1, then N = (component)dec. STOP.

Else, if the most-significant bit of component is 0, then flip 0 → 1 and reset pos = pos + readlen, readlen = (component)dec + 1.

Repeat from step 3 (until N is decoded when step 4 is true).

For code_word = 0010000100111001...

Look at the most significant bit, here it is 0, so we know its a length component. Flip the bit to get:

The current length component in decimal form is , so the length of the next component is bits, so read the next bits and see if the most significant bit is 0 or 1, if its a 1 we found our code component, but here it is 0, so flip and read again:

As , the length of the next component is , reading the next bits, the most significant bit is 0, so flip and read again:

As , the length of the next component is , reading the next bits, the most significant bit is 1, so we are at our code component, thus the next bits can be decoded into the integer

Then for the rest , repeat the algorithm, looking at the most significant bit first (to see if its a 0 or 1).

Elias Omega Code For Non-Negative Integers

The set of positive integers (Z>0) is isomorphic to the set of non-negative integers (Z≥0) meaning… They are both countable sets that share a one-to-one correspondence (bijection).

So Elias omega codewords for non-negative integers applies the same method but for any integer z ∈ Z≥0, assign it the Elias Omega codeword for in positive range. That is:

  • Codeword for z = 0 in Z≥0 is same as the codeword for z = 1 in Z>0.
  • Codeword for z = 1 in Z≥0 is same as the codeword for z = 2 in Z>0.
  • Codeword for z = 2 in Z≥0 is same as the codeword for z = 3 in Z>0.
  • Codeword for z = 3 in Z≥0 is same as the codeword for z = 4 in Z>0.

This can be done for any countable sets, for all integers you can have a total ordering

  • Codeword for z = 0 in Z≥0 is same as the codeword for z = 1 in Z>0.
  • Codeword for z = -1 in Z≥0 is same as the codeword for z = 2 in Z>0.
  • Codeword for z = 1 in Z≥0 is same as the codeword for z = 3 in Z>0.
  • Codeword for z = -2 in Z≥0 is same as the codeword for z = 4 in Z>0.
  • Codeword for z = 2 in Z≥0 is same as the codeword for z = 5 in Z>0.