Skip to content

Commit 0019a72

Browse files
committed
Улучшен Levenshtein.DistanceFast, добавлены тесты
В методе DistanceFast оптимизирована работа с длинами строк и добавлены проверки на null. Добавлен файл LevenshteinTests.cs с подробными юнит-тестами для Distance и DistanceFast, покрывающими различные случаи и свойства алгоритма.
1 parent f8ac136 commit 0019a72

2 files changed

Lines changed: 341 additions & 11 deletions

File tree

MathCore/Text/Levenshtein.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,21 @@ public static int Distance(string S1, string S2)
149149
/// </example>
150150
public static int DistanceFast(string source, string target)
151151
{
152-
var cost_matrix = new int[source.Length + 1][];
152+
if (source is not { Length: var source_length }) throw new ArgumentNullException(nameof(source));
153+
if (target is not { Length: var target_length }) throw new ArgumentNullException(nameof(target));
154+
155+
var cost_matrix = new int[source_length + 1][];
153156
for (var i = 0; i < cost_matrix.Length; i++)
154-
cost_matrix[i] = new int[target.Length + 1];
155-
//var cost_matrix = Enumerable
156-
// .Range(0, source.Length + 1)
157-
// .Select(_ => new int[target.Length + 1])
158-
// .ToArray();
157+
cost_matrix[i] = new int[target_length + 1];
159158

160-
for (var i = 1; i <= source.Length; ++i)
159+
for (var i = 1; i <= source_length; ++i)
161160
cost_matrix[i][0] = i;
162161

163-
for (var i = 1; i <= target.Length; ++i)
162+
for (var i = 1; i <= target_length; ++i)
164163
cost_matrix[0][i] = i;
165164

166-
for (var i = 1; i <= source.Length; ++i)
167-
for (var j = 1; j <= target.Length; ++j)
165+
for (var i = 1; i <= source_length; ++i)
166+
for (var j = 1; j <= target_length; ++j)
168167
{
169168
var insert = cost_matrix[i][j - 1] + 1;
170169
var delete = cost_matrix[i - 1][j] + 1;
@@ -173,6 +172,6 @@ public static int DistanceFast(string source, string target)
173172
cost_matrix[i][j] = Math.Min(Math.Min(insert, delete), edit);
174173
}
175174

176-
return cost_matrix[source.Length][target.Length];
175+
return cost_matrix[source_length][target_length];
177176
}
178177
}
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
using MathCore.Text;
2+
3+
namespace MathCore.Tests.Text;
4+
5+
[TestClass]
6+
public class LevenshteinTests
7+
{
8+
#region Distance Method Tests
9+
10+
[TestMethod]
11+
public void Distance_BothEmptyStrings_ReturnsZero()
12+
{
13+
// Arrange & Act
14+
var result = Levenshtein.Distance("", "");
15+
16+
// Assert
17+
Assert.AreEqual(0, result);
18+
}
19+
20+
[TestMethod]
21+
public void Distance_FirstStringEmpty_ReturnsSecondStringLength()
22+
{
23+
// Arrange & Act
24+
var result = Levenshtein.Distance("", "abc");
25+
26+
// Assert
27+
Assert.AreEqual(3, result);
28+
}
29+
30+
[TestMethod]
31+
public void Distance_SecondStringEmpty_ReturnsFirstStringLength()
32+
{
33+
// Arrange & Act
34+
var result = Levenshtein.Distance("abc", "");
35+
36+
// Assert
37+
Assert.AreEqual(3, result);
38+
}
39+
40+
[TestMethod]
41+
public void Distance_IdenticalStrings_ReturnsZero()
42+
{
43+
// Arrange & Act
44+
var result = Levenshtein.Distance("abc", "abc");
45+
46+
// Assert
47+
Assert.AreEqual(0, result);
48+
}
49+
50+
[TestMethod]
51+
public void Distance_SingleCharacterDifference_ReturnsOne()
52+
{
53+
// Arrange & Act
54+
var result = Levenshtein.Distance("abc", "adc");
55+
56+
// Assert
57+
Assert.AreEqual(1, result);
58+
}
59+
60+
[TestMethod]
61+
public void Distance_Kitten_Sitting_ReturnsThree()
62+
{
63+
// Arrange - k→s (1), e→i (2), +g (3)
64+
// Act
65+
var result = Levenshtein.Distance("kitten", "sitting");
66+
67+
// Assert
68+
Assert.AreEqual(3, result);
69+
}
70+
71+
[TestMethod]
72+
public void Distance_Saturday_Sunday_ReturnsThree()
73+
{
74+
// Arrange & Act
75+
var result = Levenshtein.Distance("saturday", "sunday");
76+
77+
// Assert
78+
Assert.AreEqual(3, result);
79+
}
80+
81+
[TestMethod]
82+
public void Distance_Book_Back_ReturnsTwo()
83+
{
84+
// Arrange & Act
85+
var result = Levenshtein.Distance("book", "back");
86+
87+
// Assert
88+
Assert.AreEqual(2, result);
89+
}
90+
91+
[TestMethod]
92+
public void Distance_Intention_Execution_ReturnsFive()
93+
{
94+
// Arrange & Act
95+
var result = Levenshtein.Distance("intention", "execution");
96+
97+
// Assert
98+
Assert.AreEqual(5, result);
99+
}
100+
101+
[TestMethod]
102+
public void Distance_SingleCharacterStrings_ReturnOne()
103+
{
104+
// Arrange & Act
105+
var result = Levenshtein.Distance("a", "b");
106+
107+
// Assert
108+
Assert.AreEqual(1, result);
109+
}
110+
111+
[TestMethod]
112+
public void Distance_SingleCharacterIdentical_ReturnZero()
113+
{
114+
// Arrange & Act
115+
var result = Levenshtein.Distance("a", "a");
116+
117+
// Assert
118+
Assert.AreEqual(0, result);
119+
}
120+
121+
[TestMethod]
122+
public void Distance_Algorithm_Altruistic_ReturnSix()
123+
{
124+
// Arrange & Act
125+
var result = Levenshtein.Distance("algorithm", "altruistic");
126+
127+
// Assert
128+
Assert.AreEqual(6, result);
129+
}
130+
131+
#endregion
132+
133+
#region DistanceFast Method Tests
134+
135+
[TestMethod]
136+
public void DistanceFast_BothEmptyStrings_ReturnsZero()
137+
{
138+
// Arrange & Act
139+
var result = Levenshtein.DistanceFast("", "");
140+
141+
// Assert
142+
Assert.AreEqual(0, result);
143+
}
144+
145+
[TestMethod]
146+
public void DistanceFast_FirstStringEmpty_ReturnsSecondStringLength()
147+
{
148+
// Arrange & Act
149+
var result = Levenshtein.DistanceFast("", "abc");
150+
151+
// Assert
152+
Assert.AreEqual(3, result);
153+
}
154+
155+
[TestMethod]
156+
public void DistanceFast_SecondStringEmpty_ReturnsFirstStringLength()
157+
{
158+
// Arrange & Act
159+
var result = Levenshtein.DistanceFast("abc", "");
160+
161+
// Assert
162+
Assert.AreEqual(3, result);
163+
}
164+
165+
[TestMethod]
166+
public void DistanceFast_IdenticalStrings_ReturnsZero()
167+
{
168+
// Arrange & Act
169+
var result = Levenshtein.DistanceFast("abc", "abc");
170+
171+
// Assert
172+
Assert.AreEqual(0, result);
173+
}
174+
175+
[TestMethod]
176+
public void DistanceFast_SingleCharacterDifference_ReturnsOne()
177+
{
178+
// Arrange & Act
179+
var result = Levenshtein.DistanceFast("abc", "adc");
180+
181+
// Assert
182+
Assert.AreEqual(1, result);
183+
}
184+
185+
[TestMethod]
186+
public void DistanceFast_Kitten_Sitting_ReturnsThree()
187+
{
188+
// Arrange & Act
189+
var result = Levenshtein.DistanceFast("kitten", "sitting");
190+
191+
// Assert
192+
Assert.AreEqual(3, result);
193+
}
194+
195+
[TestMethod]
196+
public void DistanceFast_Saturday_Sunday_ReturnsThree()
197+
{
198+
// Arrange & Act
199+
var result = Levenshtein.DistanceFast("saturday", "sunday");
200+
201+
// Assert
202+
Assert.AreEqual(3, result);
203+
}
204+
205+
[TestMethod]
206+
public void DistanceFast_Book_Back_ReturnsTwo()
207+
{
208+
// Arrange & Act
209+
var result = Levenshtein.DistanceFast("book", "back");
210+
211+
// Assert
212+
Assert.AreEqual(2, result);
213+
}
214+
215+
[TestMethod]
216+
public void DistanceFast_Intention_Execution_ReturnsFive()
217+
{
218+
// Arrange & Act
219+
var result = Levenshtein.DistanceFast("intention", "execution");
220+
221+
// Assert
222+
Assert.AreEqual(5, result);
223+
}
224+
225+
[TestMethod]
226+
public void DistanceFast_SingleCharacterStrings_ReturnOne()
227+
{
228+
// Arrange & Act
229+
var result = Levenshtein.DistanceFast("a", "b");
230+
231+
// Assert
232+
Assert.AreEqual(1, result);
233+
}
234+
235+
[TestMethod]
236+
public void DistanceFast_SingleCharacterIdentical_ReturnZero()
237+
{
238+
// Arrange & Act
239+
var result = Levenshtein.DistanceFast("a", "a");
240+
241+
// Assert
242+
Assert.AreEqual(0, result);
243+
}
244+
245+
[TestMethod]
246+
public void DistanceFast_Algorithm_Altruistic_ReturnSix()
247+
{
248+
// Arrange & Act
249+
var result = Levenshtein.DistanceFast("algorithm", "altruistic");
250+
251+
// Assert
252+
Assert.AreEqual(6, result);
253+
}
254+
255+
[TestMethod]
256+
public void DistanceFast_LargeStrings_PerformsCorrectly()
257+
{
258+
// Arrange - create large strings
259+
var long_str1 = new string('a', 1000) + "xyz";
260+
var long_str2 = new string('a', 1000) + "abc";
261+
262+
// Act
263+
var result = Levenshtein.DistanceFast(long_str1, long_str2);
264+
265+
// Assert
266+
Assert.AreEqual(3, result);
267+
}
268+
269+
#endregion
270+
271+
#region Consistency Tests
272+
273+
[TestMethod]
274+
[DataRow("", "")]
275+
[DataRow("a", "b")]
276+
[DataRow("abc", "def")]
277+
[DataRow("kitten", "sitting")]
278+
[DataRow("saturday", "sunday")]
279+
[DataRow("book", "back")]
280+
[DataRow("intention", "execution")]
281+
public void Distance_and_DistanceFast_ProduceConsistentResults(string s1, string s2)
282+
{
283+
// Arrange & Act
284+
var distance1 = Levenshtein.Distance(s1, s2);
285+
var distance2 = Levenshtein.DistanceFast(s1, s2);
286+
287+
// Assert
288+
Assert.AreEqual(distance1, distance2);
289+
}
290+
291+
[TestMethod]
292+
public void Distance_IsSymmetric()
293+
{
294+
// Arrange & Act
295+
var distance1 = Levenshtein.Distance("abc", "def");
296+
var distance2 = Levenshtein.Distance("def", "abc");
297+
298+
// Assert
299+
Assert.AreEqual(distance1, distance2);
300+
}
301+
302+
[TestMethod]
303+
public void DistanceFast_IsSymmetric()
304+
{
305+
// Arrange & Act
306+
var distance1 = Levenshtein.DistanceFast("abc", "def");
307+
var distance2 = Levenshtein.DistanceFast("def", "abc");
308+
309+
// Assert
310+
Assert.AreEqual(distance1, distance2);
311+
}
312+
313+
[TestMethod]
314+
public void Distance_SatisfiesTriangleInequality()
315+
{
316+
// Arrange
317+
var str1 = "kitten";
318+
var str2 = "sitting";
319+
var str3 = "mitten";
320+
321+
// Act
322+
var d12 = Levenshtein.Distance(str1, str2);
323+
var d23 = Levenshtein.Distance(str2, str3);
324+
var d13 = Levenshtein.Distance(str1, str3);
325+
326+
// Assert
327+
Assert.IsTrue(d13 <= d12 + d23, $"Triangle inequality violated: {d13} > {d12} + {d23}");
328+
}
329+
330+
#endregion
331+
}

0 commit comments

Comments
 (0)