Use matplotlib for lab6 visuals and expand report

This commit is contained in:
Artem
2025-11-21 17:29:02 +03:00
parent 9f591dadda
commit 93ab829cff
3 changed files with 79 additions and 113 deletions

View File

@@ -1,10 +1,10 @@
import math
import random
import struct
import zlib
from dataclasses import dataclass
from typing import List, Sequence, Tuple
import matplotlib.pyplot as plt
City = Tuple[float, float]
Tour = List[int]
@@ -23,116 +23,38 @@ def build_distance_matrix(cities: Sequence[City]) -> list[list[float]]:
return matrix
def _write_png(filename: str, pixels: list[list[tuple[int, int, int]]]) -> None:
height = len(pixels)
width = len(pixels[0]) if height else 0
def chunk(chunk_type: bytes, data: bytes) -> bytes:
return (
struct.pack(">I", len(data))
+ chunk_type
+ data
+ struct.pack(">I", zlib.crc32(chunk_type + data) & 0xFFFFFFFF)
)
raw = b"".join(b"\x00" + bytes([c for px in row for c in px]) for row in pixels)
png = b"\x89PNG\r\n\x1a\n"
ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
png += chunk(b"IHDR", ihdr)
png += chunk(b"IDAT", zlib.compress(raw, 9))
png += chunk(b"IEND", b"")
with open(filename, "wb") as f:
f.write(png)
def _scale_points(points: Sequence[tuple[float, float]], size: int = 800, margin: int = 20):
xs = [p[0] for p in points]
ys = [p[1] for p in points]
min_x, max_x = min(xs), max(xs)
min_y, max_y = min(ys), max(ys)
scale_x = (size - 2 * margin) / (max_x - min_x + 1e-9)
scale_y = (size - 2 * margin) / (max_y - min_y + 1e-9)
return [
(
int((x - min_x) * scale_x + margin),
int((y - min_y) * scale_y + margin),
)
for x, y in points
]
def _draw_line(pixels: list[list[tuple[int, int, int]]], p1: tuple[int, int], p2: tuple[int, int], color: tuple[int, int, int]):
x1, y1 = p1
x2, y2 = p2
dx = abs(x2 - x1)
dy = -abs(y2 - y1)
sx = 1 if x1 < x2 else -1
sy = 1 if y1 < y2 else -1
err = dx + dy
while True:
if 0 <= x1 < len(pixels[0]) and 0 <= y1 < len(pixels):
pixels[y1][x1] = color
if x1 == x2 and y1 == y2:
break
e2 = 2 * err
if e2 >= dy:
err += dy
x1 += sx
if e2 <= dx:
err += dx
y1 += sy
def _draw_circle(pixels: list[list[tuple[int, int, int]]], center: tuple[int, int], radius: int, color: tuple[int, int, int]):
cx, cy = center
for y in range(cy - radius, cy + radius + 1):
for x in range(cx - radius, cx + radius + 1):
if 0 <= x < len(pixels[0]) and 0 <= y < len(pixels):
if (x - cx) ** 2 + (y - cy) ** 2 <= radius ** 2:
pixels[y][x] = color
def plot_tour(cities: Sequence[City], tour: Sequence[int], save_path: str) -> None:
ordered = [cities[i] for i in tour] + [cities[tour[0]]]
points = _scale_points(ordered)
width = height = 820
pixels = [[(255, 255, 255) for _ in range(width)] for _ in range(height)]
xs, ys = zip(*ordered)
for i in range(len(points) - 1):
_draw_line(pixels, points[i], points[i + 1], (0, 120, 200))
fig, ax = plt.subplots(figsize=(7, 7))
ax.plot(xs, ys, "-o", color="#1f77b4", markersize=4, linewidth=1.5)
city_xs, city_ys = zip(*cities)
ax.scatter(city_xs, city_ys, s=18, color="#d62728", zorder=5)
# draw cities
city_points = _scale_points(cities)
for p in city_points:
_draw_circle(pixels, p, 4, (200, 50, 50))
_write_png(save_path, pixels)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_title("Маршрут тура")
ax.set_aspect("equal", adjustable="box")
ax.grid(True, linestyle="--", alpha=0.3)
fig.tight_layout()
fig.savefig(save_path, dpi=220)
plt.close(fig)
def plot_history(best_lengths: Sequence[float], save_path: str) -> None:
if not best_lengths:
return
width, height, margin = 820, 400, 20
pixels = [[(255, 255, 255) for _ in range(width)] for _ in range(height)]
n = len(best_lengths)
min_len, max_len = min(best_lengths), max(best_lengths)
span = max_len - min_len if max_len != min_len else 1
def to_point(idx: int, value: float) -> tuple[int, int]:
x = margin + int((width - 2 * margin) * idx / max(1, n - 1))
y = height - margin - int((height - 2 * margin) * (value - min_len) / span)
return x, y
prev = to_point(0, best_lengths[0])
for i, v in enumerate(best_lengths[1:], start=1):
cur = to_point(i, v)
_draw_line(pixels, prev, cur, (30, 30, 30))
prev = cur
_write_png(save_path, pixels)
fig, ax = plt.subplots(figsize=(8, 3.8))
ax.plot(best_lengths, color="#111111", linewidth=1.4)
ax.set_xlabel("Итерация")
ax.set_ylabel("Длина лучшего тура")
ax.set_title("Сходимость ACO")
ax.grid(True, linestyle="--", alpha=0.4)
fig.tight_layout()
fig.savefig(save_path, dpi=220)
plt.close(fig)
@dataclass