Skip to content

Commit 76905ca

Browse files
committed
LZ78: Encoding, Decoding, Compression
1 parent f39931b commit 76905ca

4 files changed

Lines changed: 179 additions & 0 deletions

File tree

AlgorithmsLibrary/AlgorithmsLibrary.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
<Compile Include="CommonClasses\Exception.cs" />
5555
<Compile Include="CommonClasses\TreeNodePrinter.cs" />
5656
<Compile Include="Extensions\StringBuilderExtensions.cs" />
57+
<Compile Include="LZ78Algm\LZ78Algm.cs" />
58+
<Compile Include="LZ78Algm\LZ78CodeBlock.cs" />
5759
<Compile Include="Results\EncodedMessage.cs" />
5860
<Compile Include="HammingAlgm\HammingAlgm.cs" />
5961
<Compile Include="HammingAlgm\DecodedMessage.cs" />
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using AlgorithmsLibrary.CommonClasses;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Text.RegularExpressions;
7+
using System.Threading.Tasks;
8+
9+
namespace AlgorithmsLibrary
10+
{
11+
/// <summary>
12+
/// Здесь идея этого алгоритма.
13+
/// </summary>
14+
public static class LZ78Algm
15+
{
16+
public static IAlgmEncoded<List<LZ78CodeBlock>> Encode(string source)
17+
{
18+
string Buffer = ""; //строка для формирования ключа для словаря
19+
Dictionary<string, int> Dictionary = new Dictionary<string, int> { { "", 0 } };
20+
List<LZ78CodeBlock> EncodedString = new List<LZ78CodeBlock>(); // ответ
21+
for (int i = 0; i < source.Length; i++)
22+
{
23+
if (Dictionary.ContainsKey(Buffer + source[i]))
24+
{ // можем ли мы увеличить префикс
25+
Buffer += source[i];
26+
}
27+
else
28+
{
29+
EncodedString.Add(new LZ78CodeBlock(Dictionary[Buffer], source[i])); // добавляем пару в ответ
30+
Dictionary.Add(Buffer + source[i], Dictionary.Count); // добавляем слово в словарь
31+
Buffer = string.Empty;
32+
}
33+
}
34+
// если буффер не пуст - этот код уже был, нужно его добавить в конец словаря
35+
if (!Buffer.Equals(string.Empty))
36+
{
37+
var last_ch = Buffer.Last(); // берем последний символ буффера, как "новый" символ
38+
Buffer = Buffer.Remove(Buffer.Length - 1); // удаляем последний символ из буфера
39+
EncodedString.Add(new LZ78CodeBlock(Dictionary[Buffer], last_ch)); // добавляем пару в ответ
40+
}
41+
42+
return new EncodedMessage<List<LZ78CodeBlock>>(EncodedString, CalculateCompressionRatio(source, EncodedString));
43+
}
44+
45+
private static List<LZ78CodeBlock> ParseEncodedString(string encodedString)
46+
{
47+
List<LZ78CodeBlock> encodedStringParsed = new List<LZ78CodeBlock>();
48+
// вид кодового блока:
49+
//({0},{2})...({0},{2})
50+
//парсит всю строку на блоки
51+
//globalCode - проверяет всю строку, подходит ли она для декодирования
52+
Regex globalCode = new Regex(@"(?=^)(([(](\d+)([,])(.|\n|\r|\t)[)])|(\s)|(\n)|(\r))+(?=$)");
53+
Regex regex = new Regex(@"([(](\d+)([,])(.|\n|\r|\t)[)])"); //регулярка кодового блока
54+
Regex intRegex = new Regex(@"\d+"); //регулярка цыфры
55+
if (!globalCode.IsMatch(encodedString))
56+
{
57+
throw new CodingException();
58+
}
59+
60+
MatchCollection matches = regex.Matches(encodedString);
61+
foreach (Match match in matches)
62+
{
63+
string codeBlock = match.Value;
64+
MatchCollection matchesBlock = intRegex.Matches(codeBlock);
65+
encodedStringParsed.Add(new LZ78CodeBlock(int.Parse(matchesBlock[0].Value), codeBlock[codeBlock.Length - 2]));
66+
}
67+
68+
return encodedStringParsed;
69+
}
70+
71+
private static double CalculateCompressionRatio(string sourceString, List<LZ78CodeBlock> compressionString)
72+
{
73+
//Считаем что в стандартной кодировке один символ = 8бит
74+
double countBitsSourceString = 8 * sourceString.Length;
75+
76+
double countBitsCompressionString = 0;
77+
foreach (LZ78CodeBlock compression in compressionString)
78+
{
79+
int countBitsOffset = Convert.ToString(compression.Position, 2).Length;
80+
int countBitsChar = 8;
81+
82+
countBitsCompressionString += countBitsOffset + countBitsOffset + countBitsChar;
83+
}
84+
85+
return Math.Round(countBitsSourceString / countBitsCompressionString, 3);
86+
}
87+
88+
public static IAlgmEncoded<string> Decode(string encodedString)
89+
{
90+
List<LZ78CodeBlock> encodedStringParsed = ParseEncodedString(encodedString);
91+
92+
StringBuilder resultDecoding = new StringBuilder(string.Empty);
93+
94+
List<string> dict = new List<string> { string.Empty }; // словарь, слово с номером 0 — пустая строка
95+
foreach (LZ78CodeBlock code in encodedStringParsed)
96+
{
97+
var word = dict[code.Position] + code.Char; // составляем слово из уже известного из словаря и новой буквы
98+
resultDecoding.Append(word); // приписываем к ответу
99+
dict.Add(word); // добавляем в словарь
100+
}
101+
102+
string decodedString = resultDecoding.ToString();
103+
return new EncodedMessage<string>(decodedString, CalculateCompressionRatio(decodedString, encodedStringParsed));
104+
}
105+
}
106+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace AlgorithmsLibrary
4+
{
5+
public class LZ78CodeBlock : IEquatable<LZ78CodeBlock>
6+
{
7+
public int Position { get; private set; }
8+
public char Char { get; private set; }
9+
10+
public LZ78CodeBlock(int Position, char Char)
11+
{
12+
this.Position = Position;
13+
this.Char = Char;
14+
}
15+
16+
public override string ToString()
17+
{
18+
return string.Format("({0},{1})", Position, Char);
19+
}
20+
21+
public bool Equals(LZ78CodeBlock other)
22+
{
23+
return Position == other.Position && Char == other.Char;
24+
}
25+
}
26+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using AlgorithmsLibrary;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace UnitTestProject
10+
{
11+
public class LZ78AlgmUnitTest
12+
{
13+
[Fact]
14+
public void EncodeStringWithRepeatsLongString()
15+
{
16+
var result = LZ78Algm.Encode("abracadabraabracadabra");
17+
18+
var expectedCodeBlocks = new List<LZ78CodeBlock> {
19+
new LZ78CodeBlock(0, 'a'),
20+
new LZ78CodeBlock(0, 'b'),
21+
new LZ78CodeBlock(0, 'r'),
22+
new LZ78CodeBlock(1, 'c'),
23+
new LZ78CodeBlock(1, 'd'),
24+
new LZ78CodeBlock(1, 'b'),
25+
new LZ78CodeBlock(3, 'a'),
26+
new LZ78CodeBlock(6, 'r'),
27+
new LZ78CodeBlock(4, 'a'),
28+
new LZ78CodeBlock(0, 'd'),
29+
new LZ78CodeBlock(8, 'a'),
30+
};
31+
32+
Assert.Equal(expectedCodeBlocks, result.GetAnswer());
33+
}
34+
35+
[Fact]
36+
public void DecodedStringWithRepeatsLongString()
37+
{
38+
var result = LZ78Algm.Decode("(0,a)(0,b)(0,r)(1,c)(1,d)(1,b)(3,a)(6,r)(4,a)(0,d)(8,a)");
39+
40+
var expectedCodeBlocks = "abracadabraabracadabra";
41+
42+
Assert.Equal(expectedCodeBlocks, result.GetAnswer());
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)