Cidr4 merge algorithm #5

Merged
PavelPatsey merged 91 commits from CIDR4_merge_algorithm into main 2025-01-27 22:05:39 +03:00
Showing only changes of commit 722bd5a751 - Show all commits
+51 -43
View File
@@ -1,6 +1,6 @@
from copy import deepcopy from copy import deepcopy
Fedor-Lyanguzov commented 2025-01-07 17:54:03 +03:00 (Migrated from github.com)
Review

Здесь за один проход объединяются все возможные объединения, таким образом мы можем промахнуться мимо цели в M элементов списка.

Здесь за один проход объединяются все возможные объединения, таким образом мы можем промахнуться мимо цели в `M` элементов списка.
Fedor-Lyanguzov commented 2025-01-07 17:55:32 +03:00 (Migrated from github.com)
Review

Здесь не хватает возвращения количества адресов, "попавших под раздачу": не принадлежащих начальному списку, но попавших в результат из-за объединения. Это количество позволит найти оптимальное решение.

Здесь не хватает возвращения количества адресов, "попавших под раздачу": не принадлежащих начальному списку, но попавших в результат из-за объединения. Это количество позволит найти оптимальное решение.
Fedor-Lyanguzov commented 2025-01-07 17:57:45 +03:00 (Migrated from github.com)
Review

Я думаю, использование внешней библиотеки (и вообще любой библиотеки) размывает смысл алгоритма: хотя из следующих строк понятно, что будет сделано; однако не очевидно, как это будет сделано, и будет ли оптимальный ответ, и будет ли оптимальное решение (что не обязательно).

Я думаю, использование внешней библиотеки (и вообще любой библиотеки) размывает смысл алгоритма: хотя из следующих строк понятно, что будет сделано; однако не очевидно, как это будет сделано, и будет ли оптимальный ответ, и будет ли оптимальное решение (что не обязательно).
Fedor-Lyanguzov commented 2025-01-12 17:46:01 +03:00 (Migrated from github.com)
Review

Мне кажется, что функция merge_nodes содержит не все свои обязанности, некоторые из них похоже вложись в функцию reduce_nodes. Стоит их переместить.

Мне кажется, что функция `merge_nodes` содержит не все свои обязанности, некоторые из них похоже вложись в функцию `reduce_nodes`. Стоит их переместить.
Fedor-Lyanguzov commented 2025-01-12 19:00:28 +03:00 (Migrated from github.com)
Review

Алгоритм слишком много делает каждый шаг, из-за этого работает медленно. Как мне кажется, для оптимизации стоит разработать алгоритм начиная с рекурсии, возможно их будет штук 5 связанных, зато это позволит определить характеристики отдельных кусков и принять решение по оптимизации.

Алгоритм слишком много делает каждый шаг, из-за этого работает медленно. Как мне кажется, для оптимизации стоит разработать алгоритм начиная с рекурсии, возможно их будет штук 5 связанных, зато это позволит определить характеристики отдельных кусков и принять решение по оптимизации.
Fedor-Lyanguzov commented 2025-01-12 19:01:34 +03:00 (Migrated from github.com)
Review

Пора перенести тесты в отдельный файл?

Пора перенести тесты в отдельный файл?
from ipaddress import IPv4Address from ipaddress import IPv4Address
from typing import List, Tuple from typing import List, Tuple, Set
from itertools import groupby from itertools import groupby
from collections import defaultdict from collections import defaultdict
import cProfile import cProfile
@@ -42,6 +42,19 @@ def get_mask(binary: str) -> str:
return binary[: i + 1] return binary[: i + 1]
def remove_ips_with_subnets(binaries: Set[str]) -> Set[str]:
"""Убрать ip, у которых есть подсети"""
sorted_binaries = sorted(binaries)
i = 0
while i < len(sorted_binaries) - 1:
mask = get_mask(sorted_binaries[i])
if sorted_binaries[i + 1].startswith(mask):
del sorted_binaries[i]
else:
i += 1
return set(sorted_binaries)
def rough_merge_binaries(binaries: List[str], req_len: int) -> List[str]: def rough_merge_binaries(binaries: List[str], req_len: int) -> List[str]:
ips = set(deepcopy(binaries)) ips = set(deepcopy(binaries))
reduction_limit_reached = False reduction_limit_reached = False
@@ -50,23 +63,14 @@ def rough_merge_binaries(binaries: List[str], req_len: int) -> List[str]:
ip_with_max_vlsm = max(ips, key=lambda x: x.rfind("1")) ip_with_max_vlsm = max(ips, key=lambda x: x.rfind("1"))
max_vlsm = ip_with_max_vlsm.rfind("1") max_vlsm = ip_with_max_vlsm.rfind("1")
reduced_ips = set() reduced_ips = set()
merged_ips = set() other_ips = set()
for ip in ips: for ip in ips:
if ip.rfind("1") == max_vlsm: if ip.rfind("1") == max_vlsm:
reduced_ips.add(reduce_binary(ip)) reduced_ips.add(reduce_binary(ip))
else: else:
merged_ips.add(ip) other_ips.add(ip)
filtered_set = set()
for r_ip in reduced_ips:
mask = get_mask(r_ip)
for ip in merged_ips:
if ip.startswith(mask):
filtered_set.add(r_ip)
reduced_ips -= filtered_set
merged_ips.update(reduced_ips)
merged_ips = remove_ips_with_subnets(reduced_ips | other_ips)
if len(merged_ips) > req_len: if len(merged_ips) > req_len:
ips = merged_ips ips = merged_ips
else: else:
@@ -76,45 +80,29 @@ def rough_merge_binaries(binaries: List[str], req_len: int) -> List[str]:
def smooth_merge_binaries(binaries: List[str], req_len: int) -> List[str]: def smooth_merge_binaries(binaries: List[str], req_len: int) -> List[str]:
non_reducible_ips = set() ips = set(deepcopy(binaries))
ips = set()
for ip in binaries:
if "1" not in reduce_binary(ip):
non_reducible_ips.add(ip)
else:
ips.add(ip)
while len(ips) > 0 and len(ips) + len(non_reducible_ips) > req_len:
# print(f"{req_len=} {len(ips) + len(non_reducible_ips)=}")
while len(ips) > req_len:
group_dict = defaultdict(list) group_dict = defaultdict(list)
for ip in ips: for ip in ips:
group_dict[ip.rfind("1")].append(ip) group_dict[ip.rfind("1")].append(ip)
max_len = max(group_dict) max_len = max(group_dict)
lst = group_dict[max_len] sorted_group = sorted(group_dict[max_len])
for x, y in zip(lst, lst[1:]): for x, y in zip(sorted_group, sorted_group[1:]):
i = x.rfind("1") mask = get_mask(x)
if y.startswith(x[:i]): if y.startswith(mask):
ips.remove(x) ips.remove(x)
ips.remove(y) ips.remove(y)
ips.add(reduce_binary(x)) ips.add(reduce_binary(x))
break break
else: else:
x = lst[0] x = sorted_group[0]
ips.remove(x) ips.remove(x)
ips.add(reduce_binary(x)) ips.add(reduce_binary(x))
_ips = ips | non_reducible_ips ips = remove_ips_with_subnets(ips)
non_reducible_ips = set()
ips = set()
for ip in _ips:
if "1" not in reduce_binary(ip):
non_reducible_ips.add(ip)
else:
ips.add(ip)
ips.update(non_reducible_ips)
return sorted(ips) return sorted(ips)
@@ -133,12 +121,12 @@ def main():
for ip in rough_merged_bin_ips: for ip in rough_merged_bin_ips:
print(ip) print(ip)
# print("#" * 128) print("\n" + "#" * 128 + "\n")
#
# smooth_merged_bin_ips = smooth_merge_binaries(rough_merged_bin_ips, required_len) smooth_merged_bin_ips = smooth_merge_binaries(rough_merged_bin_ips, required_len)
# print(f"{len(smooth_merged_bin_ips)=}") print(f"{len(smooth_merged_bin_ips)=}")
# for ip in smooth_merged_bin_ips: for ip in smooth_merged_bin_ips:
# print(ip) print(ip)
# smooth_merged_bin_ips = smooth_merge_binaries(bin_ips, required_len) # smooth_merged_bin_ips = smooth_merge_binaries(bin_ips, required_len)
# print(f"{len(smooth_merged_bin_ips)=}") # print(f"{len(smooth_merged_bin_ips)=}")
@@ -170,5 +158,25 @@ if __name__ == "__main__":
assert get_mask("00000000000000000000000000000000") == "" assert get_mask("00000000000000000000000000000000") == ""
assert get_mask("00000000000010000000000000000000") == "0000000000001" assert get_mask("00000000000010000000000000000000") == "0000000000001"
assert remove_ips_with_subnets(
{
"0001000",
"0001100",
"0001110",
"0001111",
"0101000",
}
) == {
"0001111",
"0101000",
}
assert remove_ips_with_subnets({"0001000"}) == {"0001000"}
assert remove_ips_with_subnets({"1111000", "0101000"}) == {
"1111000",
"0101000",
}
assert remove_ips_with_subnets(set()) == set()
main() main()
# cProfile.run("main()") # cProfile.run("main()")