algorithm-discovery-engine 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ads/__init__.py +85 -0
- ads/benchmark.py +84 -0
- ads/problems.py +190 -0
- ads/problems_advanced.py +203 -0
- ads/structures.py +370 -0
- ads/structures_advanced.py +385 -0
- algo_discovery/__init__.py +19 -0
- algo_discovery/__main__.py +38 -0
- algo_discovery/engine.py +64 -0
- algo_discovery/features.py +115 -0
- algo_discovery/hypotheses.py +251 -0
- algo_discovery/models.py +63 -0
- algorithm_discovery_engine-1.0.0.dist-info/METADATA +287 -0
- algorithm_discovery_engine-1.0.0.dist-info/RECORD +27 -0
- algorithm_discovery_engine-1.0.0.dist-info/WHEEL +4 -0
- algorithm_discovery_engine-1.0.0.dist-info/entry_points.txt +2 -0
- algorithm_discovery_engine-1.0.0.dist-info/licenses/LICENSE +21 -0
- gui/__init__.py +3 -0
- gui/__main__.py +26 -0
- gui/app.py +407 -0
- gui/core.py +149 -0
- synth/__init__.py +22 -0
- synth/__main__.py +66 -0
- synth/corpus.py +222 -0
- synth/discovery.py +198 -0
- synth/grammar.py +174 -0
- synth/search.py +432 -0
ads/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Algorithms & Data Structures solving engine (Python).
|
|
2
|
+
|
|
3
|
+
Two tiers are provided for every problem:
|
|
4
|
+
|
|
5
|
+
* :mod:`ads.problems` / :mod:`ads.structures` — the *regular* tier:
|
|
6
|
+
readable, deliberately simple implementations that mirror the Java, C++
|
|
7
|
+
and Rust ports 1:1.
|
|
8
|
+
* :mod:`ads.problems_advanced` / :mod:`ads.structures_advanced` — the
|
|
9
|
+
*advanced* tier: memory- and speed-optimized variants (rolling DP,
|
|
10
|
+
in-place sorts, slot-allocated nodes, manual heaps).
|
|
11
|
+
|
|
12
|
+
A single catalogue (``catalog/problems.json`` at the repo root) holds the
|
|
13
|
+
shared test vectors used by all four languages.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from ads.problems import (
|
|
17
|
+
binary_search,
|
|
18
|
+
edit_distance,
|
|
19
|
+
graph_bfs,
|
|
20
|
+
graph_dfs,
|
|
21
|
+
knapsack_01,
|
|
22
|
+
lcs,
|
|
23
|
+
max_subarray,
|
|
24
|
+
merge_sort,
|
|
25
|
+
quick_sort,
|
|
26
|
+
two_sum,
|
|
27
|
+
)
|
|
28
|
+
from ads.problems_advanced import (
|
|
29
|
+
binary_search_advanced,
|
|
30
|
+
edit_distance_advanced,
|
|
31
|
+
graph_bfs_advanced,
|
|
32
|
+
graph_dfs_advanced,
|
|
33
|
+
knapsack_01_advanced,
|
|
34
|
+
lcs_advanced,
|
|
35
|
+
max_subarray_advanced,
|
|
36
|
+
merge_sort_advanced,
|
|
37
|
+
quick_sort_advanced,
|
|
38
|
+
two_sum_advanced,
|
|
39
|
+
)
|
|
40
|
+
from ads.structures import BST, LinkedList, MinHeap, Queue, Stack, Trie
|
|
41
|
+
from ads.structures_advanced import (
|
|
42
|
+
BSTAdvanced,
|
|
43
|
+
LinkedListAdvanced,
|
|
44
|
+
MinHeapAdvanced,
|
|
45
|
+
QueueAdvanced,
|
|
46
|
+
StackAdvanced,
|
|
47
|
+
TrieAdvanced,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__version__ = "1.0.0"
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"BST",
|
|
54
|
+
"BSTAdvanced",
|
|
55
|
+
"LinkedList",
|
|
56
|
+
"LinkedListAdvanced",
|
|
57
|
+
"MinHeap",
|
|
58
|
+
"MinHeapAdvanced",
|
|
59
|
+
"Queue",
|
|
60
|
+
"QueueAdvanced",
|
|
61
|
+
"Stack",
|
|
62
|
+
"StackAdvanced",
|
|
63
|
+
"Trie",
|
|
64
|
+
"TrieAdvanced",
|
|
65
|
+
"binary_search",
|
|
66
|
+
"binary_search_advanced",
|
|
67
|
+
"edit_distance",
|
|
68
|
+
"edit_distance_advanced",
|
|
69
|
+
"graph_bfs",
|
|
70
|
+
"graph_bfs_advanced",
|
|
71
|
+
"graph_dfs",
|
|
72
|
+
"graph_dfs_advanced",
|
|
73
|
+
"knapsack_01",
|
|
74
|
+
"knapsack_01_advanced",
|
|
75
|
+
"lcs",
|
|
76
|
+
"lcs_advanced",
|
|
77
|
+
"max_subarray",
|
|
78
|
+
"max_subarray_advanced",
|
|
79
|
+
"merge_sort",
|
|
80
|
+
"merge_sort_advanced",
|
|
81
|
+
"quick_sort",
|
|
82
|
+
"quick_sort_advanced",
|
|
83
|
+
"two_sum",
|
|
84
|
+
"two_sum_advanced",
|
|
85
|
+
]
|
ads/benchmark.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Cross-language benchmark workload (mirrors Java/C++/Rust benches).
|
|
2
|
+
|
|
3
|
+
Fixed seed + fixed sizes so `engine/runner.py bench` can tabulate per-algorithm
|
|
4
|
+
wall time. Run directly with `python -m ads.benchmark`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import random
|
|
10
|
+
import time
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
|
|
13
|
+
from ads import problems, problems_advanced
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _random_ints(count: int, seed: int) -> list[int]:
|
|
17
|
+
rng = random.Random(seed)
|
|
18
|
+
return [rng.randrange(1_000_000_000) for _ in range(count)]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _random_string(length: int, seed: int) -> str:
|
|
22
|
+
rng = random.Random(seed)
|
|
23
|
+
return "".join(rng.choice("abcdefgh") for _ in range(length))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _chain_graph(size: int) -> list[list[int]]:
|
|
27
|
+
return [
|
|
28
|
+
[neighbor for neighbor in (i - 1, i + 1) if 0 <= neighbor < size]
|
|
29
|
+
for i in range(size)
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _time_it(label: str, task: Callable[[], object]) -> None:
|
|
34
|
+
started = time.perf_counter()
|
|
35
|
+
task()
|
|
36
|
+
elapsed_us = (time.perf_counter() - started) * 1_000_000
|
|
37
|
+
print(f"{label}: {elapsed_us:.0f} us")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run() -> None:
|
|
41
|
+
data = _random_ints(200_000, 42)
|
|
42
|
+
sorted_data = list(range(1_000_000))
|
|
43
|
+
|
|
44
|
+
_time_it("merge_sort", lambda: problems.merge_sort(data))
|
|
45
|
+
_time_it("quick_sort", lambda: problems.quick_sort(_random_ints(200_000, 42)))
|
|
46
|
+
_time_it(
|
|
47
|
+
"max_subarray",
|
|
48
|
+
lambda: problems_advanced.max_subarray_advanced(_random_ints(500_000, 7)),
|
|
49
|
+
)
|
|
50
|
+
_time_it("two_sum", lambda: problems.two_sum(_random_ints(50_000, 9), -1))
|
|
51
|
+
_time_it(
|
|
52
|
+
"binary_search",
|
|
53
|
+
lambda: problems.binary_search(sorted_data, sorted_data[len(sorted_data) // 2]),
|
|
54
|
+
)
|
|
55
|
+
_time_it(
|
|
56
|
+
"lcs",
|
|
57
|
+
lambda: problems_advanced.lcs_advanced(
|
|
58
|
+
_random_string(2_000, 11), _random_string(2_000, 13)
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
_time_it(
|
|
62
|
+
"knapsack_01",
|
|
63
|
+
lambda: problems_advanced.knapsack_01_advanced(
|
|
64
|
+
5_000, _random_ints(1_000, 21), _random_ints(1_000, 31)
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
_time_it(
|
|
68
|
+
"edit_distance",
|
|
69
|
+
lambda: problems_advanced.edit_distance_advanced(
|
|
70
|
+
_random_string(1_000, 41), _random_string(1_000, 43)
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
_time_it(
|
|
74
|
+
"graph_bfs",
|
|
75
|
+
lambda: problems_advanced.graph_bfs_advanced(_chain_graph(100_000), 0, 99_999),
|
|
76
|
+
)
|
|
77
|
+
_time_it(
|
|
78
|
+
"graph_dfs",
|
|
79
|
+
lambda: problems_advanced.graph_dfs_advanced(_chain_graph(100_000), 0, 99_999),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
run()
|
ads/problems.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Regular Python tier: readable baseline implementations.
|
|
2
|
+
|
|
3
|
+
These mirror the Java / C++ / Rust ports 1:1 and favour clarity over raw
|
|
4
|
+
performance. The advanced tier (:mod:`ads.problems_advanced`) provides the
|
|
5
|
+
memory/speed-optimized variants.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections import deque
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def two_sum(nums: list[int], target: int) -> list[int]:
|
|
14
|
+
"""Indices of the two numbers summing to *target* (or ``[-1, -1]``)."""
|
|
15
|
+
seen: dict[int, int] = {}
|
|
16
|
+
for i, value in enumerate(nums):
|
|
17
|
+
complement = target - value
|
|
18
|
+
if complement in seen:
|
|
19
|
+
j = seen[complement]
|
|
20
|
+
return [j, i] if j < i else [i, j]
|
|
21
|
+
seen[value] = i
|
|
22
|
+
return [-1, -1]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def binary_search(sorted_nums: list[int], target: int) -> int:
|
|
26
|
+
"""Index of the first occurrence of *target* (lower bound), or ``-1``."""
|
|
27
|
+
low, high = 0, len(sorted_nums)
|
|
28
|
+
while low < high:
|
|
29
|
+
mid = (low + high) // 2
|
|
30
|
+
if sorted_nums[mid] < target:
|
|
31
|
+
low = mid + 1
|
|
32
|
+
else:
|
|
33
|
+
high = mid
|
|
34
|
+
if low < len(sorted_nums) and sorted_nums[low] == target:
|
|
35
|
+
return low
|
|
36
|
+
return -1
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _merge(left: list[int], right: list[int]) -> list[int]:
|
|
40
|
+
merged: list[int] = []
|
|
41
|
+
i = j = 0
|
|
42
|
+
while i < len(left) and j < len(right):
|
|
43
|
+
if left[i] <= right[j]:
|
|
44
|
+
merged.append(left[i])
|
|
45
|
+
i += 1
|
|
46
|
+
else:
|
|
47
|
+
merged.append(right[j])
|
|
48
|
+
j += 1
|
|
49
|
+
merged.extend(left[i:])
|
|
50
|
+
merged.extend(right[j:])
|
|
51
|
+
return merged
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def merge_sort(nums: list[int]) -> list[int]:
|
|
55
|
+
"""Return a new array sorted ascending (classic merge sort)."""
|
|
56
|
+
if len(nums) <= 1:
|
|
57
|
+
return list(nums)
|
|
58
|
+
mid = len(nums) // 2
|
|
59
|
+
return _merge(merge_sort(nums[:mid]), merge_sort(nums[mid:]))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _partition(nums: list[int], low: int, high: int) -> int:
|
|
63
|
+
pivot = nums[high]
|
|
64
|
+
i = low
|
|
65
|
+
for j in range(low, high):
|
|
66
|
+
if nums[j] < pivot:
|
|
67
|
+
nums[i], nums[j] = nums[j], nums[i]
|
|
68
|
+
i += 1
|
|
69
|
+
nums[i], nums[high] = nums[high], nums[i]
|
|
70
|
+
return i
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def quick_sort(nums: list[int]) -> list[int]:
|
|
74
|
+
"""Return a new array sorted ascending (in-place quick sort behind the scenes)."""
|
|
75
|
+
values = list(nums)
|
|
76
|
+
|
|
77
|
+
def sort_region(low: int, high: int) -> None:
|
|
78
|
+
if low >= high:
|
|
79
|
+
return
|
|
80
|
+
split = _partition(values, low, high)
|
|
81
|
+
sort_region(low, split - 1)
|
|
82
|
+
sort_region(split + 1, high)
|
|
83
|
+
|
|
84
|
+
sort_region(0, len(values) - 1)
|
|
85
|
+
return values
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def max_subarray(nums: list[int]) -> int:
|
|
89
|
+
"""Maximum contiguous subarray sum (Kadane). Empty -> 0."""
|
|
90
|
+
best = 0 if not nums else nums[0]
|
|
91
|
+
running = 0
|
|
92
|
+
for value in nums:
|
|
93
|
+
running = max(value, running + value)
|
|
94
|
+
best = max(best, running)
|
|
95
|
+
return best
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def lcs(a: str, b: str) -> int:
|
|
99
|
+
"""Longest common subsequence length (full-table DP)."""
|
|
100
|
+
rows, cols = len(a) + 1, len(b) + 1
|
|
101
|
+
table = [[0] * cols for _ in range(rows)]
|
|
102
|
+
for i in range(1, rows):
|
|
103
|
+
for j in range(1, cols):
|
|
104
|
+
if a[i - 1] == b[j - 1]:
|
|
105
|
+
table[i][j] = table[i - 1][j - 1] + 1
|
|
106
|
+
else:
|
|
107
|
+
table[i][j] = max(table[i - 1][j], table[i][j - 1])
|
|
108
|
+
return table[rows - 1][cols - 1]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def knapsack_01(capacity: int, weights: list[int], values: list[int]) -> int:
|
|
112
|
+
"""Maximum value with the 0/1 knapsack constraint (full-table DP)."""
|
|
113
|
+
dp = [[0] * (capacity + 1) for _ in range(len(weights) + 1)]
|
|
114
|
+
for item in range(1, len(weights) + 1):
|
|
115
|
+
weight, value = weights[item - 1], values[item - 1]
|
|
116
|
+
row_before, row_now = dp[item - 1], dp[item]
|
|
117
|
+
for cap in range(capacity + 1):
|
|
118
|
+
if weight <= cap:
|
|
119
|
+
row_now[cap] = max(row_before[cap], row_before[cap - weight] + value)
|
|
120
|
+
else:
|
|
121
|
+
row_now[cap] = row_before[cap]
|
|
122
|
+
return dp[len(weights)][capacity]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def edit_distance(a: str, b: str) -> int:
|
|
126
|
+
"""Levenshtein distance (full-table DP): insert/delete/substitute."""
|
|
127
|
+
rows, cols = len(a) + 1, len(b) + 1
|
|
128
|
+
table = [[0] * cols for _ in range(rows)]
|
|
129
|
+
for i in range(rows):
|
|
130
|
+
table[i][0] = i
|
|
131
|
+
for j in range(cols):
|
|
132
|
+
table[0][j] = j
|
|
133
|
+
for i in range(1, rows):
|
|
134
|
+
for j in range(1, cols):
|
|
135
|
+
cost = 0 if a[i - 1] == b[j - 1] else 1
|
|
136
|
+
table[i][j] = min(
|
|
137
|
+
table[i - 1][j] + 1,
|
|
138
|
+
table[i][j - 1] + 1,
|
|
139
|
+
table[i - 1][j - 1] + cost,
|
|
140
|
+
)
|
|
141
|
+
return table[rows - 1][cols - 1]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _parse_adjacency(encoded: str) -> list[list[int]]:
|
|
145
|
+
adj: list[list[int]] = []
|
|
146
|
+
for row in encoded.split(";"):
|
|
147
|
+
parts = row.split(":")
|
|
148
|
+
neighbors: list[int] = []
|
|
149
|
+
if parts[1].strip():
|
|
150
|
+
neighbors = [int(n) for n in filter(None, parts[1].split())]
|
|
151
|
+
adj.append(neighbors)
|
|
152
|
+
return adj
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def graph_bfs(adj: list[list[int]], start: int, target: int) -> int:
|
|
156
|
+
"""Shortest path length in edges from *start* to *target*, ``-1`` unreachable."""
|
|
157
|
+
if start == target:
|
|
158
|
+
return 0
|
|
159
|
+
if start >= len(adj):
|
|
160
|
+
return -1
|
|
161
|
+
distances = [-1] * len(adj)
|
|
162
|
+
distances[start] = 0
|
|
163
|
+
pending = deque([start])
|
|
164
|
+
while pending:
|
|
165
|
+
node = pending.popleft()
|
|
166
|
+
for neighbor in adj[node]:
|
|
167
|
+
if distances[neighbor] != -1:
|
|
168
|
+
continue
|
|
169
|
+
distances[neighbor] = distances[node] + 1
|
|
170
|
+
if neighbor == target:
|
|
171
|
+
return distances[neighbor]
|
|
172
|
+
pending.append(neighbor)
|
|
173
|
+
return -1
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def graph_dfs(adj: list[list[int]], start: int, target: int) -> bool:
|
|
177
|
+
"""Whether *target* is reachable from *start* (iterative depth-first search)."""
|
|
178
|
+
|
|
179
|
+
def visit(node: int, seen: set[int] | None = None) -> bool:
|
|
180
|
+
seen = set() if seen is None else seen
|
|
181
|
+
if node == target:
|
|
182
|
+
return True
|
|
183
|
+
seen.add(node)
|
|
184
|
+
return any(
|
|
185
|
+
neighbor not in seen and visit(neighbor, seen) for neighbor in adj[node]
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if not adj and start != target:
|
|
189
|
+
return False
|
|
190
|
+
return visit(start)
|
ads/problems_advanced.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Advanced Python tier: performance-optimized variants.
|
|
2
|
+
|
|
3
|
+
Each function is behaviorally equivalent to its :mod:`ads.problems`
|
|
4
|
+
counterpart but trades verbosity for lower constant factors and better
|
|
5
|
+
space usage (rolling DP rows, in-place sorts, iterative graph walks).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from bisect import bisect_left
|
|
11
|
+
from collections import deque
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def two_sum_advanced(nums: list[int], target: int) -> list[int]:
|
|
15
|
+
"""Two-sum with a single pass and early exit (same contract)."""
|
|
16
|
+
seen: dict[int, int] = {}
|
|
17
|
+
for i, value in enumerate(nums):
|
|
18
|
+
other = target - value
|
|
19
|
+
if other in seen:
|
|
20
|
+
j = seen[other]
|
|
21
|
+
return [min(j, i), max(j, i)]
|
|
22
|
+
seen[value] = i
|
|
23
|
+
return [-1, -1]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def binary_search_advanced(sorted_nums: list[int], target: int) -> int:
|
|
27
|
+
"""Lower-bound binary search delegated to the C-accelerated bisect module."""
|
|
28
|
+
index = bisect_left(sorted_nums, target)
|
|
29
|
+
if index < len(sorted_nums) and sorted_nums[index] == target:
|
|
30
|
+
return index
|
|
31
|
+
return -1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _merge_into(
|
|
35
|
+
source: list[int], left: int, mid: int, right: int, buffer: list[int]
|
|
36
|
+
) -> None:
|
|
37
|
+
buffer[left:right] = source[left:right]
|
|
38
|
+
i, j, k = left, mid, left
|
|
39
|
+
while i < mid and j < right:
|
|
40
|
+
if buffer[i] <= buffer[j]:
|
|
41
|
+
source[k] = buffer[i]
|
|
42
|
+
i += 1
|
|
43
|
+
else:
|
|
44
|
+
source[k] = buffer[j]
|
|
45
|
+
j += 1
|
|
46
|
+
k += 1
|
|
47
|
+
while i < mid:
|
|
48
|
+
source[k] = buffer[i]
|
|
49
|
+
i += 1
|
|
50
|
+
k += 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def merge_sort_advanced(nums: list[int]) -> list[int]:
|
|
54
|
+
"""In-place merge sort over a private copy using a single aux buffer."""
|
|
55
|
+
values = list(nums)
|
|
56
|
+
buffer = [0] * len(values)
|
|
57
|
+
|
|
58
|
+
width = 1
|
|
59
|
+
while width < len(values):
|
|
60
|
+
left = 0
|
|
61
|
+
while left < len(values):
|
|
62
|
+
mid = min(left + width, len(values))
|
|
63
|
+
right = min(left + 2 * width, len(values))
|
|
64
|
+
if mid < right:
|
|
65
|
+
_merge_into(values, left, mid, right, buffer)
|
|
66
|
+
left += 2 * width
|
|
67
|
+
width *= 2
|
|
68
|
+
return values
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def quick_sort_advanced(nums: list[int]) -> list[int]:
|
|
72
|
+
"""Iterative in-place quick sort with median-of-three pivot (Lomuto)."""
|
|
73
|
+
values = list(nums)
|
|
74
|
+
stack: list[tuple[int, int]] = [(0, len(values) - 1)]
|
|
75
|
+
|
|
76
|
+
def median_of_three(lo: int, hi: int) -> int:
|
|
77
|
+
mid = (lo + hi) // 2
|
|
78
|
+
a, b, c = values[lo], values[mid], values[hi]
|
|
79
|
+
if (a < b) != (a < c):
|
|
80
|
+
return lo
|
|
81
|
+
if (b < a) != (b < c):
|
|
82
|
+
return mid
|
|
83
|
+
return hi
|
|
84
|
+
|
|
85
|
+
while stack:
|
|
86
|
+
low, high = stack.pop()
|
|
87
|
+
while low < high:
|
|
88
|
+
pivot_index = median_of_three(low, high)
|
|
89
|
+
values[pivot_index], values[high] = values[high], values[pivot_index]
|
|
90
|
+
pivot = values[high]
|
|
91
|
+
i = low
|
|
92
|
+
for j in range(low, high):
|
|
93
|
+
if values[j] < pivot:
|
|
94
|
+
values[i], values[j] = values[j], values[i]
|
|
95
|
+
i += 1
|
|
96
|
+
values[i], values[high] = values[high], values[i]
|
|
97
|
+
if i - low < high - i:
|
|
98
|
+
stack.append((i + 1, high))
|
|
99
|
+
high = i - 1
|
|
100
|
+
else:
|
|
101
|
+
stack.append((low, i - 1))
|
|
102
|
+
low = i + 1
|
|
103
|
+
return values
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def max_subarray_advanced(nums: list[int]) -> int:
|
|
107
|
+
"""Kadane with stream-friendly fold (same contract, no pre-scan)."""
|
|
108
|
+
best: int | None = None
|
|
109
|
+
running = 0
|
|
110
|
+
for value in nums:
|
|
111
|
+
running = max(value, running + value)
|
|
112
|
+
best = running if best is None else max(best, running)
|
|
113
|
+
return 0 if best is None else best
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def lcs_advanced(a: str, b: str) -> int:
|
|
117
|
+
"""LCS with O(min(m, n)) space via two rolling rows."""
|
|
118
|
+
if len(a) < len(b):
|
|
119
|
+
a, b = b, a
|
|
120
|
+
previous = [0] * (len(b) + 1)
|
|
121
|
+
for i in range(1, len(a) + 1):
|
|
122
|
+
current = [0] * (len(b) + 1)
|
|
123
|
+
for j in range(1, len(b) + 1):
|
|
124
|
+
if a[i - 1] == b[j - 1]:
|
|
125
|
+
current[j] = previous[j - 1] + 1
|
|
126
|
+
else:
|
|
127
|
+
up, left = previous[j], current[j - 1]
|
|
128
|
+
current[j] = up if up >= left else left
|
|
129
|
+
previous = current
|
|
130
|
+
return previous[len(b)]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def knapsack_01_advanced(capacity: int, weights: list[int], values: list[int]) -> int:
|
|
134
|
+
"""0/1 knapsack with O(capacity) space via a single rolling row."""
|
|
135
|
+
row = [0] * (capacity + 1)
|
|
136
|
+
for weight, value in zip(weights, values, strict=False):
|
|
137
|
+
if weight > capacity:
|
|
138
|
+
continue
|
|
139
|
+
for cap in range(capacity, weight - 1, -1):
|
|
140
|
+
candidate = row[cap - weight] + value
|
|
141
|
+
if candidate > row[cap]:
|
|
142
|
+
row[cap] = candidate
|
|
143
|
+
return row[capacity]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def edit_distance_advanced(a: str, b: str) -> int:
|
|
147
|
+
"""Levenshtein distance with O(min(m, n)) space via two rolling rows."""
|
|
148
|
+
if len(a) < len(b):
|
|
149
|
+
a, b = b, a
|
|
150
|
+
previous = list(range(len(b) + 1))
|
|
151
|
+
for i in range(1, len(a) + 1):
|
|
152
|
+
current = [0] * (len(b) + 1)
|
|
153
|
+
current[0] = i
|
|
154
|
+
for j in range(1, len(b) + 1):
|
|
155
|
+
insert_cost = current[j - 1] + 1
|
|
156
|
+
delete_cost = previous[j] + 1
|
|
157
|
+
substitute = previous[j - 1] + (0 if a[i - 1] == b[j - 1] else 1)
|
|
158
|
+
current[j] = min(insert_cost, delete_cost, substitute)
|
|
159
|
+
previous = current
|
|
160
|
+
return previous[len(b)]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def graph_bfs_advanced(adj: list[list[int]], start: int, target: int) -> int:
|
|
164
|
+
"""BFS shortest path using a preallocated distance array and deque."""
|
|
165
|
+
if start == target:
|
|
166
|
+
return 0
|
|
167
|
+
if not adj or not (0 <= start < len(adj)):
|
|
168
|
+
return -1 if target != start else 0
|
|
169
|
+
distances = [-1] * len(adj)
|
|
170
|
+
distances[start] = 0
|
|
171
|
+
pending = deque([start])
|
|
172
|
+
while pending:
|
|
173
|
+
node = pending.popleft()
|
|
174
|
+
steps = distances[node] + 1
|
|
175
|
+
for neighbor in adj[node]:
|
|
176
|
+
if distances[neighbor] != -1:
|
|
177
|
+
continue
|
|
178
|
+
distances[neighbor] = steps
|
|
179
|
+
if neighbor == target:
|
|
180
|
+
return steps
|
|
181
|
+
pending.append(neighbor)
|
|
182
|
+
return -1
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def graph_dfs_advanced(adj: list[list[int]], start: int, target: int) -> bool:
|
|
186
|
+
"""Iterative DFS with an explicit stack (avoids recursion limits)."""
|
|
187
|
+
if not adj:
|
|
188
|
+
return start == target
|
|
189
|
+
if not (0 <= start < len(adj)):
|
|
190
|
+
return False
|
|
191
|
+
visited = [False] * len(adj)
|
|
192
|
+
pending = [start]
|
|
193
|
+
while pending:
|
|
194
|
+
node = pending.pop()
|
|
195
|
+
if node == target:
|
|
196
|
+
return True
|
|
197
|
+
if visited[node]:
|
|
198
|
+
continue
|
|
199
|
+
visited[node] = True
|
|
200
|
+
for neighbor in adj[node]:
|
|
201
|
+
if not visited[neighbor]:
|
|
202
|
+
pending.append(neighbor)
|
|
203
|
+
return False
|