Cidr4 merge algorithm #5
+51
-43
@@ -1,6 +1,6 @@
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
|||||||
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()")
|
||||||
|
|||||||
Reference in New Issue
Block a user
Здесь за один проход объединяются все возможные объединения, таким образом мы можем промахнуться мимо цели в
Mэлементов списка.Здесь не хватает возвращения количества адресов, "попавших под раздачу": не принадлежащих начальному списку, но попавших в результат из-за объединения. Это количество позволит найти оптимальное решение.
Я думаю, использование внешней библиотеки (и вообще любой библиотеки) размывает смысл алгоритма: хотя из следующих строк понятно, что будет сделано; однако не очевидно, как это будет сделано, и будет ли оптимальный ответ, и будет ли оптимальное решение (что не обязательно).
Мне кажется, что функция
merge_nodesсодержит не все свои обязанности, некоторые из них похоже вложись в функциюreduce_nodes. Стоит их переместить.Алгоритм слишком много делает каждый шаг, из-за этого работает медленно. Как мне кажется, для оптимизации стоит разработать алгоритм начиная с рекурсии, возможно их будет штук 5 связанных, зато это позволит определить характеристики отдельных кусков и принять решение по оптимизации.
Пора перенести тесты в отдельный файл?