2024-11-31 : rappel feuille de route 2024

Le plan des séances est changé après que celles-ci ont eu lieu.

Séance 1

Prises aux dames

Séance 2

Tests unitaires et classes toujours avec les dames

Prises aux dames

Séance 3

classes pour représenter un graphe

Fin des classes puis les itérateurs et numpy broadcast.

Séance 4

Nous garderons les dames et l’algortithme minimax pour une autre fois peut être. Cette séance à propos de la programmation dynamique. Le premier exercice consiste à déterminer le nombre minimal de pièces de monnaie pour écrire un montant et de retrouver la séquence minimale de pièces. On considère les pièces [1, 2, 4, 5].

<<<

def ecriture_minimale(n):
    pieces = [1, 2, 4, 5]
    min_pieces = [None for i in range(n + 1)]
    predecessor = [None for i in range(n + 1)]
    min_pieces[0] = 1
    for p in pieces:
        min_pieces[p] = 1
        predecessor[p] = p
    for i in range(n + 1):
        if min_pieces[i] is None:
            # écriture impossible
            continue
        for p in pieces:
            if i + p > n:
                break
            m = min_pieces[i] + 1
            if min_pieces[i + p] is None or m < min_pieces[i + p]:
                min_pieces[i + p] = m
                predecessor[i + p] = p
    composition = []
    while n > 0:
        composition.append(predecessor[n])
        n -= predecessor[n]
    return min_pieces[n], composition


print(ecriture_minimale(99))

>>>

    (1, [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4])

On bascule ensuite vers la Distance d’édition.

A propos de la distance d’édition, voir aussi Distance d’édition ou encore Distance entre deux mots de même longueur et tests unitaires.

Séance 5

Séance 6

Evocation de la Recherche à base de préfixes en terme algorithmique.

Autres variations autour du problème du voyageur de commerce, ou TSP pour Travelling Salesman Problem ou encore circuit hamiltonien: Circuit hamiltonien et Kohonen, Circuit hamiltonien et Kruskal. Quelques bouts de code écrit durant la séance. Tout d’abord les expressions régulières.

<<<

import re

reg = re.compile("(-?[1-9][ 0-9]{0,16}([.,][0-9]{0,4})? *(€|(euros))?)")
text = "Le montant de 3000 euros auquel on a ôté 5,4 euros."
print(reg.findall(text))

reg = re.compile("([0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{2,4})")
text = "9/10/2024 09-10-24"
print(reg.findall(text))

>>>

    [('3000 euros', '', 'euros', 'euros'), ('5,4 euros', ',4', 'euros', 'euros')]
    ['9/10/2024', '09-10-24']

Ensuite la pyramide.

<<<

import math
import os
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1)
x, y, colors = [], [], []
cs = ["r", "b", "g"]
for i in range(0, 5):
    for j in range(0, 5):
        x.append(i - j * 0.5)
        y.append(j * math.sqrt(3) / 2)
        colors.append(cs[(2 * i - j) % 3])
size = [2000 for c in x]
ax.scatter(x, y, s=size, c=colors, alpha=0.5)
fig.savefig(os.path.join(__WD__, "pyramide.png"))

text = ".. image:: pyramide.png"
print(text)

>>>

../../_images/pyramide.png

Ensuite le voyageur de commerce.

<<<

import itertools
import numpy as np
import matplotlib.pyplot as plt


def show(villes):
    fig, ax = plt.subplots(1, 1)
    x = villes[:, 0].tolist() + [villes[0, 0]]
    y = villes[:, 1].tolist() + [villes[0, 1]]
    ax.plot(x, y, "o-")
    ax.set_title(f"longueur {distance(villes)}")
    return fig, ax


def distance(villes):
    # distance sans boucle
    dall = villes[1:, :] - villes[:-1, :]
    d = (dall[:, 0] ** 2 + dall[:, 1] ** 2) ** 0.5
    dlast = villes[0, :] - villes[-1, :]
    d1 = (dlast[0] ** 2 + dlast[1] ** 2) ** 0.5
    return d.sum() + d1


def solution_permutations(villes):
    bestp = list(range(villes.shape[0]))
    bestd = distance(villes)
    for p in itertools.permutations(list(range(villes.shape[0]))):
        v2 = villes[list(p), :]
        d2 = distance(v2)
        if d2 < bestd:
            bestd = d2
            bestp = list(p)
    return villes[bestp, :]


def solution_croisement(villes):
    bestd = distance(villes)
    bestv = villes
    for i in range(0, villes.shape[0]):
        for j in range(i + 2, villes.shape[0]):
            p = list(range(villes.shape[0]))
            if i > 0:
                p[i:j] = p[j - 1 : i - 1 : -1]
            else:
                p[i:j] = p[j - 1 :: -1]
            v2 = bestv[p, :]
            d2 = distance(v2)
            if d2 < bestd:
                bestd = d2
                bestv = v2
    return bestv


villes = np.random.rand(8, 2)
print("distance initiale", distance(villes))

# solution naive
print("-- optimisation gourmande...")
optim = solution_permutations(villes)
print("-- optimisation gourmande:", distance(optim))

print()
print("-- optimisation plus rapide mais approchée...")
optim = solution_croisement(villes)
print("-- optimisation plus rapide mais approchée", distance(optim))

# graph
fig, ax = show(optim)
fig.savefig(os.path.join(__WD__, "tsp_simple.png"))

text = ".. image:: tsp_simple.png"
print("\n\n")
print(text)

>>>

distance initiale 3.0600004941537975 – optimisation gourmande… – optimisation gourmande: 2.445930418749022

– optimisation plus rapide mais approchée… – optimisation plus rapide mais approchée 2.7349655558819936

../../_images/tsp_simple.png

Séance 7

Bouts de code, un peu d’optimization.

<<<

import numpy as np


def calcul(h1, h2, v1, v2, x):
    t1 = np.sqrt(x**2 + h1**2) / v1
    t2 = np.sqrt((1 - x) ** 2 + h2**2) / v2
    return t1 + t2


h1, h2, v1, v2 = 1, 0.5, 1, 0.8
p = np.arange(6) / 5
print(p)
print(calcul(1, 1, 1, 1, p))
print(calcul(h1, h2, v1, v2, p))


def calcul_entrepot(v1, v2, A, B, Es):
    # A: [[0, 0]], B: [[1, 1]], Es: [[0.3, 0.4], [...]]
    t1 = np.sqrt(((A - Es) ** 2).sum(axis=1)) / v1
    t2 = np.sqrt(((B - Es) ** 2).sum(axis=1)) / v2
    return t1 + t2


A = np.array([[0, 0]])
B = np.array([[1, 1]])
Es = np.array([[0.5, 0.5], [0.1, 0.1], [0, 0.1]])
print("---------")
print(calcul_entrepot(v1, v2, A, B, Es))

>>>

    [0.  0.2 0.4 0.6 0.8 1. ]
    [2.414 2.3   2.243 2.243 2.3   2.414]
    [2.398 2.199 2.053 1.967 1.954 2.039]
    ---------
    [1.591 1.732 1.782]

Jeu de la vie:

<<<

import os
import numpy as np
import matplotlib.pyplot as plt
import tqdm


def plateau(n, p=0.5):
    return (np.random.rand(n, n) < p).astype(int)


def dessin(plat, next_plat):
    fig, ax = plt.subplots(1, 2)
    ax[0].imshow(plat.astype(float))
    ax[0].get_xaxis().set_visible(False)
    ax[0].get_yaxis().set_visible(False)
    ax[1].imshow(next_plat.astype(float))
    ax[1].get_xaxis().set_visible(False)
    ax[1].get_yaxis().set_visible(False)
    return fig, ax


def iteration(plat):
    voisin = np.zeros(plat.shape, dtype=int)
    i, j = plat.shape
    # voisin gauche, droite
    voisin[:-1, :] += plat[1:, :]
    voisin[1:, :] += plat[:-1, :]
    # voisin haut,bas
    voisin[:, :-1] += plat[:, 1:]
    voisin[:, 1:] += plat[:, :-1]
    # voisin diagonal
    voisin[:-1, :-1] += plat[1:, 1:]
    voisin[1:, 1:] += plat[:-1, :-1]
    # voisin autre diagonal
    voisin[:-1, 1:] += plat[1:, :-1]
    voisin[1:, :-1] += plat[-1:, 1:]
    # mise à jour
    nouveau = np.zeros(plat.shape, dtype=int)
    nouveau += ((plat == 1) & (voisin <= 3) & (voisin >= 2)).astype(int)
    nouveau += ((plat == 0) & (voisin == 3)).astype(int)
    return nouveau


def jeu(n, p, n_iter=5, save_intermediate=False):
    plat = plateau(10, 0.2)
    x, y = [], []
    for i in tqdm.tqdm(list(range(n_iter))):
        x.append(i)
        y.append(plat.sum())
        next_plat = iteration(plat)
        if save_intermediate:
            fig, ax = dessin(plat, next_plat)
            fig.savefig(os.path.join(__WD__, "anim_vie{i:03d}.png"))
        plat = next_plat
    fig, ax = plt.subplots(1, 1)
    ax.plot(x, y)
    ax.set_title(f"{n_iter} itération du jeu de la vie")
    fig.savefig(os.path.join(__WD__, "anim_evolution.png"))
    return plat


plat = plateau(20, 0.4)
next_plat = iteration(plat)

print("première itération")
print(next_plat)

fig, ax = dessin(plat, next_plat)
ax[0].set_title("avant")
ax[1].set_title("première itération")
fig.savefig(os.path.join(__WD__, "vie_1.png"))

print("et le jeu")
plat = jeu(16, 0.2)
print(plat)

>>>

    première itération
    [[0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0]
     [1 1 1 1 0 1 0 0 0 1 1 0 1 1 0 0 1 1 0 0]
     [0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0]
     [1 0 0 1 0 1 0 1 1 0 0 0 1 0 0 0 0 1 0 1]
     [0 0 1 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 1 0]
     [0 0 0 1 1 1 0 1 0 1 0 0 1 1 0 1 0 1 1 1]
     [1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 1]
     [0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1]
     [0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]
     [0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]
     [1 0 1 0 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 0]
     [0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0]
     [1 0 0 1 1 1 0 0 1 1 0 0 1 0 1 1 0 0 1 1]
     [0 0 1 1 1 1 0 0 0 0 0 0 0 1 0 1 0 1 0 1]
     [1 1 0 1 0 1 1 0 0 1 1 1 1 0 0 1 0 1 0 1]
     [0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0]
     [0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0]
     [1 0 0 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 0 0]
     [1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 1 1 1 0 0]
     [1 0 1 0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0]]
    et le jeu
    
  0%|          | 0/5 [00:00<?, ?it/s]
100%|##########| 5/5 [00:00<00:00, 12475.62it/s]
    [[0 1 0 1 1 0 0 0 0 0]
     [1 0 0 0 0 1 1 1 0 0]
     [0 0 0 0 0 0 1 1 1 0]
     [1 0 1 1 1 1 0 1 1 0]
     [0 1 1 0 0 1 0 1 1 0]
     [0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 1 0 0 0]
     [0 0 0 0 0 1 1 1 0 0]
     [0 0 0 0 0 1 0 0 0 0]
     [0 0 0 0 0 1 0 1 0 0]]

Et visuellement, la première itération :

../../_images/vie_1.png

Et l’évolution du jeu :

../../_images/anim_evolution.png

Séance 8

TD noté 1h30 en seconde partie. Classes et un algorithme. Enoncés des années précédentes : Séances minutées.

Idées laissées de côté mais autant d’exercices possibles

Convertir une expression mathématique comme ((34 + 6) - 2) / (7 - 4) en notation polonaise inverse. Voir aussi Algorithme Shunting-yard.