In a lot of texts regarding compression you will run across various
conventions of syntax to describe the coding of data. Much of them are
used in fields like automata
theory, group theory and
statistics. Although, the field of coding
theory itself has a long
and rich
history in
the 20st century; much of the terminology is borrowed from fields that
gave rise to it.
I find the best way to demonstrate the following definitions is by
example. Let us take the text "abbcc" as a sample.
Alphabet: The set of symbols that we will be encoding.
Form:
Σ={a1,a2,...,an}
Example:
Σ={a1,a2,a3}
where
a1=a,a2=b,a3=c
String: A permutation of symbols from our alphabet. From our
example alphabet, strings like "a", "aba", "abc" or "cccb"
could all be possible. Using regular expression syntax, we can
describe this in our example.
Form:
w
Example:
w={a,b,c}∗
Probabilities: The set of probabilities for the alphabet. This
is also known as their weights and can sometimes be denoted as w
instead of p.
Form:
P={p1,p2,...,pn}
Example:
P={p1,p2,p3}
where
p1=51,p2=52,p3=52
since 'a' occurs once, 'b' occurs twice and 'c' occurs
twice out of 5 symbols all together.
Codewords: The set of codewords that we will create for every
symbol in our alphabet.\
Form:
C={c1,c2,...,cn}
Example:
C={c1,c2,c3}
where
c1=00,c2=01,c3=1
for symbols
a1,a2a3
respectively. Another way to look at it is that we are providing
a bijective function:
f:Σ→C.
Standard coding of information for most computers today is byte aligned.
For example, in character codes there is
ASCII, which codes the entire
English alpha-numeric system in a single byte. While UTF-8 has proven to
be a much better standard due to better localization support and
variable width codes, ASCII serves our purposes for this discussion.
ASCII is considered a fixed width coding scheme:
When performance is at stake, it is best to code information atomically
aligned to the computer architecture that it is being used for. This
means allocating and defining information in terms of bytes (defined as
8-bits) for the majority of systems today. Although, if space is the
priority, performance can take a hit to shift around
bits
along the byte boundaries and create variable width codes that do not
byte align.
Note: The ellipses in the alphabet denote the control characters (eg.
CR, LB) and grammatical structure symbols (eg. #, $, %) contained in
the ASCII code page. The alphabets will reflect this in the ellipses.
Coming back to the example of ASCII, if we wish to encode a string, w,
like "aaabb", it will take up 5 bytes. Although we only really need
one bit to encode our information if we assign 0 to 'a' and 1 to
'b'.
We just took a 5 byte ASCII encoded string and compressed it down to
under a byte. Now let us take another string, w, and give it
"aaabbcc". Three alphabet symbols need to be coded; it will take at
least 2 bits. At first glance, this assignment would make sense:
Although this gives us an ambiguous string since it could either be
"aaabbcc", "aaabbabab", "aacbcc" or any other permutation of our
codeword set when we attempt to decompress it. The reason for this
ambiguity is due to the fact that the codeword for 'a' (0), is used to
start the codeword for 'c' (01).
This ensures that we maintain the one-to-one mapping between our
alphabet and codewords; we can translate alphabet symbols to codewords
and codewords back to alphabet symbols without confusion.
So how do you decide which symbols, get which codewords? Also, since the
size of the codeword is critical to the compression ratio (average
codeword length to average symbol length), designing an algorithm to
weigh the more frequently used symbols is important. In our previous
example, it would be poor design to give 'a' the longest codeword
because it is the most used symbol in the string. This is where the
Huffman Coding algorithm comes into play.
If you need further examples, you can look
here,
here and
here. Once the
you get a grasp of the algorithm, you will see the simplicity and beauty
of it.
If you want to see a naive implementation of the Huffman Coding
algorithm, I posted some source code on my GitHub
account. Below is the
central code to the algorithm execution.
Introduction # I was working on implementing a simple compression tool based on naive Huffman coding and initially assumed all of my prefix codes would be at or under a byte long. Although I failed to account for the worst case of Huffman coding, which can yield codes up to 255 bits in length.
Introduction # If you are already familiar with finite automata, then understanding a Markov chain will not be as hard. If you are not familiar with one, you can think of finite automata as a flowchart, with states and paths leading from one state to another based on some decision being made. An example could be a room with a light switch; there are the two states of having the light on or off. Using the light switch will transition you between the two states.
I was recently working on solving problem 64 for project Euler and one of the needed algorithms was a detection of cycles in a list. This routine works for detecting cycles in lists of cardinality greater than 2.
\