Skip to content

Commit 71ba5e3

Browse files
committed
Improve solution 2018-02, part 2's performance
Abort calculating the edit distance as soon as is clear that it gets larger than the one we search for (which is 1). This is much faster than the previous caching approach. (On an MS Surface Pro 8: from 20 seconds down to < 100 ms.)
1 parent db07188 commit 71ba5e3

File tree

2 files changed

+27
-15
lines changed

2 files changed

+27
-15
lines changed

src/main/kotlin/de/ronny_h/aoc/year2018/day02/InventoryManagementSystem.kt

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,35 @@ class InventoryManagementSystem : AdventOfCode<String>(2018, 2) {
2121
override fun part2(input: List<String>): String {
2222
val minPair = input
2323
.combinations()
24-
.minBy { lev(it.first, it.second) }
24+
.minBy { lev(it.first, it.second, 1) }
2525

2626
return minPair.first.filterIndexed { i, char ->
2727
minPair.second[i] == char
2828
}
2929
}
3030
}
3131

32-
private val levCache = mutableMapOf<Pair<String, String>, Int>()
33-
3432
/**
35-
* The Levenshtein distance (an edit distance).
33+
* @return The Levenshtein distance (an edit distance) between the Strings [a] and [b].
34+
* @param maxDistance Specifies an upper bound for the calculated distance. The function aborts if this value is exceeded
35+
* returning the distance calculated so far (which might be much higher than [maxDistance]).
36+
* @param distanceSoFar For recursive calls, only. The distance calculated to the current level.
3637
*/
37-
fun lev(a: String, b: String): Int {
38-
levCache[a to b]?.let { return it }
39-
38+
fun lev(a: String, b: String, maxDistance: Int = Int.MAX_VALUE, distanceSoFar: Int = 0): Int {
4039
if (b.isEmpty()) return a.length
4140
if (a.isEmpty()) return b.length
41+
if (distanceSoFar > maxDistance) return 0
4242

4343
val aTail = a.substring(1)
4444
val bTail = b.substring(1)
4545

46-
val tailResult = lev(aTail, bTail)
47-
levCache[aTail to bTail] = tailResult
48-
4946
if (a.first() == b.first()) {
50-
return tailResult
47+
return lev(aTail, bTail, maxDistance, distanceSoFar)
5148
}
5249

53-
val aTailResult = lev(aTail, b)
54-
val bTailResult = lev(a, bTail)
55-
levCache[aTail to b] = aTailResult
56-
levCache[a to bTail] = bTailResult
50+
val aTailResult = lev(aTail, b, maxDistance, distanceSoFar + 1)
51+
val bTailResult = lev(a, bTail, maxDistance, distanceSoFar + 1)
52+
val tailResult = lev(aTail, bTail, maxDistance, distanceSoFar + 1)
5753

5854
return 1 + min(min(aTailResult, bTailResult), tailResult)
5955
}

src/test/kotlin/de/ronny_h/aoc/year2018/day02/InventoryManagementSystemTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,20 @@ class InventoryManagementSystemTest : StringSpec({
4848
lev(a, b) shouldBe distance
4949
}
5050
}
51+
52+
"The Levenshtein distance with an upper bound" {
53+
forAll(
54+
row("", "", 1, 0),
55+
row("", "a", 1, 1),
56+
row("a", "", 1, 1),
57+
row("abc", "abc", 1, 0),
58+
row("abc", "a_c", 1, 1),
59+
row("abc", "axbyc", 1, 2),
60+
row("abc", "axbycz", 1, 2),
61+
row("abc", "xyz", 1, 2),
62+
row("abc", "abcxyz", 1, 3),
63+
) { a, b, maxDistance, distance ->
64+
lev(a, b, maxDistance) shouldBe distance
65+
}
66+
}
5167
})

0 commit comments

Comments
 (0)