master > master: tsp

This commit is contained in:
RD 2022-06-02 11:42:42 +02:00
parent 5e92cd1fd4
commit 00e5432b6c
9 changed files with 109 additions and 52 deletions

View File

@ -6,6 +6,7 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import math;
import numpy as np;
import random;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -14,5 +15,6 @@ import random;
__all__ = [
'math',
'np',
'random',
];

View File

@ -17,6 +17,7 @@ from typing import Tuple;
from typing import Type;
from typing import TypeVar;
from typing import Union;
from nptyping import NDArray;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
@ -34,4 +35,5 @@ __all__ = [
'Type',
'TypeVar',
'Union',
'NDArray',
];

View File

@ -12,8 +12,10 @@ os.chdir(os.path.join(os.path.dirname(__file__), '..'));
sys.path.insert(0, os.getcwd());
from src.core.log import *;
from src.local.maths import *;
from src.graphs.graph import *;
from src.graphs.tarjan import *;
from src.travel.naive import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL CONSTANTS/VARIABLES
@ -26,28 +28,17 @@ from src.graphs.tarjan import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def enter():
## Beispiel aus Seminarblatt 1
nodes = [1,2,3,4,5,6,7,8];
edges = [
(1,2),
(1,3),
(2,4),
(2,5),
(3,5),
(3,6),
(3,8),
(4,5),
(4,7),
(5,1),
(5,8),
(6,8),
(7,8),
(8,6),
];
G = Graph(nodes=nodes, edges=edges);
components = tarjan_algorithm(G, debug=True);
for component in components:
log_debug(component);
## Beispiel aus Seminarblatt 8
tsp_naive_algorithm(
dist = np.asarray([
[0, 7, 2, 5],
[7, 0, 5, 6],
[2, 5, 0, 5],
[2, 7, 4, 0],
], dtype=float),
optimise=max,
verbose=True,
);
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from src.local.typing import *;
from src.local.maths import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'tsp_naive_algorithm',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHOD tsp_naive_algorithm
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def tsp_naive_algorithm(
dist: NDArray[(Any, Any), float],
optimise = min,
verbose: bool = False,
) -> tuple[float, list[list[int]]]:
m, n = dist.shape[:2];
assert m == n;
memory: Dict[tuple[int, tuple], tuple[float, list[list[int]]]] = dict();
def g(i: int, S: list[int]) -> tuple[float, list[list[int]]]:
# wenn g bereits für den Input definiert ist, gib diesen Wert zurück:
if (i, tuple(S)) not in memory.keys():
if len(S) == 0:
paths = [[i]] if i == 0 else [[i, 0]];
memory[(i, tuple(S))] = (dist[i,0], paths);
else:
values_and_paths = [ (j, *g(j, (*S[:index], *S[(index+1):]))) for index, j in enumerate(S) ];
# berechne d(i,j) + g(j, S \ {i}) for each j in S:
values_and_paths = [ (j, dist[i,j] + value, paths) for j, value, paths in values_and_paths];
value = optimise([value for _, value, _ in values_and_paths]);
paths = [];
for j, value_, paths_ in values_and_paths:
if value_ == value:
paths += [ [i, *path] for path in paths_ ];
memory[(i, tuple(S))] = (value, paths);
return memory[(i, tuple(S))];
# berechne g(0, {1,2,...,n-1}):
optimal_wert = g(0, [i for i in range(1,n)]);
if verbose:
display_computation(n, memory);
return optimal_wert, [];
def display_computation(n: int, memory: Dict[tuple[int, tuple], tuple[float, list[list[int]]]]):
keys = sorted(memory.keys(), key=lambda key: (len(key[1]), key[0], key[1]));
for k in range(0,n):
print(f'\x1b[4;1m|S| = {k}:\x1b[0m');
for (i, S) in keys:
if len(S) != k:
continue;
value, paths = memory[(i, S)];
print(f'g({i}, {list(S)}) = {value}');
if len(paths) == 1:
print(f'optimal path: {" -> ".join(map(str, paths[0]))}');
else:
print('optimal paths:');
for path in paths:
print(f'* {" -> ".join(map(str, path))}');
print('');
return;

View File

@ -6,17 +6,8 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from unittest import TestCase;
from pytest import mark;
from pytest import fixture;
from tests.core.log import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# FIXTURES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -24,3 +15,11 @@ from tests.core.log import *;
@fixture(scope='module')
def test():
return TestCase();
@fixture(scope='module')
def debug():
def log(*lines: str):
with open('logs/debug.log', 'a') as fp:
for line in lines:
print(line, end='\n', file=fp);
return log;

View File

@ -1,17 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODS - logging
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def log(*lines: str):
with open('logs/debug.log', 'a') as fp:
for line in lines:
print(line, end='\n', file=fp);

View File

@ -9,8 +9,6 @@ from unittest import TestCase;
from pytest import mark;
from pytest import fixture;
from tests.core.log import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,12 +5,12 @@
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from contextlib import nullcontext as does_not_raise
from collections import Counter;
import pytest;
from pytest import mark;
from pytest import fixture;
from pytest import lazy_fixture;
from unittest import TestCase;
from unittest.mock import patch;
from src.local.typing import *;
from src.graphs.graph import *;
@ -73,6 +73,13 @@ def test_tarjan(test, G, expected):
components = tarjan_algorithm(G, False);
assert_components_eq(test, components, expected);
@patch(f'{__name__}.tarjan_algorithm', lambda *_: [])
@mark.parametrize(('G', 'expected'), [ (lazy_fixture('graph1'), [[1], [3], [2,4]])])
@mark.usefixtures('test')
def test_failable_tarjan(test, G, expected):
with pytest.raises(AssertionError):
test_tarjan(test, G, expected);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY METHODS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~