Algorithms

Voyageur de commerce

[1]:
import numpy as np
import matplotlib.pyplot as plt

villes = np.random.rand(20, 2)
villes[:5]
[1]:
array([[0.90080623, 0.2350022 ],
       [0.56755441, 0.54858335],
       [0.60316886, 0.99559521],
       [0.87287745, 0.22318813],
       [0.21085165, 0.0701609 ]])
[2]:
fig, ax = plt.subplots(1, 1, figsize=(3, 3))
indices = list(range(-1, villes.shape[0]))
ax.plot(villes[indices, 0], villes[indices, 1], "o-")
[2]:
[<matplotlib.lines.Line2D at 0x73893a0a5370>]
../../../_images/practice_years_2025_seance4_algo_2_1.png
[3]:
def distance(v1, v2):  # v1= np.array([0, 1]), v2= ....
    return ((v1 - v2) ** 2).sum() ** 0.5


def plus_proche_non_visitee(villes, chemin):
    depart = chemin[-1]
    dmin, imin = None, None
    for i in range(villes.shape[0]):
        if i not in chemin:
            d = distance(villes[depart], villes[i])
            if dmin is None or d < dmin:
                dmin, imin = d, i
    return imin


def algo_proche_en_proche(villes):
    chemin = [0]
    while len(chemin) < villes.shape[0]:
        # trouver la ville la plus proche non visitée de la dernière
        # ville visitée et l'ajouter au chemin
        inext = plus_proche_non_visitee(villes, chemin)
        chemin.append(inext)
    return chemin


chemin = algo_proche_en_proche(villes)
indices = chemin + chemin[:1]
fig, ax = plt.subplots(1, 1, figsize=(3, 3))
ax.plot(villes[indices, 0], villes[indices, 1], "o-")
[3]:
[<matplotlib.lines.Line2D at 0x738939c39b50>]
../../../_images/practice_years_2025_seance4_algo_3_1.png
[4]:
def elimine_croisements(villes, chemin):
    C = chemin
    while True:
        mieux = 0
        for i in range(-1, villes.shape[0]):
            for j in range(i + 2, villes.shape[0] - 1):
                delta = (
                    -distance(villes[C[i]], villes[C[i + 1]])
                    - distance(villes[C[j]], villes[C[j + 1]])
                    + distance(villes[C[i]], villes[C[j]])
                    + distance(villes[C[i + 1]], villes[C[j + 1]])
                )
                if delta < 0:
                    mieux += 1
                    print("mieux", i, j, delta, chemin[i + 1 : j + 1], chemin[j:i:-1])
                    # c'est mieux... on retourne les villes du chemin
                    # entre i+1 et j inclus
                    if i >= 0:
                        chemin[i + 1 : j + 1] = chemin[j:i:-1]
                    else:
                        chemin[i + 1 : j + 1] = chemin[j::-1]
        if mieux == 0:
            break


elimine_croisements(villes, chemin)
fig, ax = plt.subplots(1, 1, figsize=(3, 3))
indices = chemin + chemin[:1]
ax.plot(villes[indices, 0], villes[indices, 1], "o-")
ax.plot(villes[chemin[:1], 0], villes[chemin[:1], 1], "or")
ax.plot(villes[chemin[-1:], 0], villes[chemin[-1:], 1], "og")
mieux -1 3 -0.15191965099971733 [0, 3, 14, 19] []
mieux -1 11 -0.5172511323315206 [19, 14, 3, 0, 16, 6, 10, 17, 15, 11, 18, 2] []
mieux 11 13 -0.0513745444570991 [1, 12] [12, 1]
mieux 11 17 -0.0044994455150375035 [12, 1, 7, 9, 4, 5] [5, 4, 9, 7, 1, 12]
mieux 15 17 -0.03444152981270798 [1, 12] [12, 1]
mieux 11 13 -0.12384307859548493 [5, 4] [4, 5]
[4]:
[<matplotlib.lines.Line2D at 0x738937b2a870>]
../../../_images/practice_years_2025_seance4_algo_4_2.png

Distance d’édition

[5]:
def distance_edition(m1, m2):
    cout = np.empty((len(m1) + 1, len(m2) + 1))
    predi = np.empty((len(m1) + 1, len(m2) + 1), dtype=np.int64)
    predj = np.empty((len(m1) + 1, len(m2) + 1), dtype=np.int64)
    cout[:, 0] = np.arange(len(m1) + 1)
    cout[0, :] = np.arange(len(m2) + 1)
    predi[:, 0] = np.arange(len(m1) + 1) - 1
    predj[:, 0] = 0
    predi[0, :] = 0
    predj[0, :] = np.arange(len(m2) + 1) - 1
    for i in range(1, len(m1) + 1):
        for j in range(1, len(m2) + 1):
            c_sup = cout[i - 1, j] + 1
            c_ins = cout[i, j - 1] + 1
            c_cmp = cout[i - 1, j - 1] + (1 if m1[i - 1] != m2[j - 1] else 0)
            if c_cmp <= min(c_sup, c_ins):
                cout[i, j], predi[i, j], predj[i, j] = c_cmp, i - 1, j - 1
            elif c_sup <= c_ins:
                cout[i, j], predi[i, j], predj[i, j] = c_sup, i - 1, j
            else:
                cout[i, j], predi[i, j], predj[i, j] = c_ins, i, j - 1
    # alignement
    alignement = [(len(m1), len(m2))]
    while min(alignement[-1]) >= 0:
        i, j = alignement[-1]
        i, j = predi[i, j], predj[i, j]
        alignement.append((i, j))
    alignement = alignement[::-1][2:]
    lettres = [(m1[i - 1], m2[j - 1]) for i, j in alignement]
    return cout, alignement, lettres


cout, alignement, lettres = distance_edition("ENSAE", "ESANE")
print(alignement)
print(lettres)
cout
[(np.int64(1), np.int64(1)), (np.int64(2), np.int64(1)), (np.int64(3), np.int64(2)), (np.int64(4), np.int64(3)), (np.int64(4), np.int64(4)), (5, 5)]
[('E', 'E'), ('N', 'E'), ('S', 'S'), ('A', 'A'), ('A', 'N'), ('E', 'E')]
[5]:
array([[0., 1., 2., 3., 4., 5.],
       [1., 0., 1., 2., 3., 4.],
       [2., 1., 1., 2., 2., 3.],
       [3., 2., 1., 2., 3., 3.],
       [4., 3., 2., 1., 2., 3.],
       [5., 4., 3., 2., 2., 2.]])
[ ]:


Notebook on github