From cb6d0e77f5f7e91d69a98392f918087b8c1e63b4 Mon Sep 17 00:00:00 2001 From: Florian Mathieu Date: Thu, 15 Jan 2026 18:32:27 +0100 Subject: [PATCH] =?UTF-8?q?modifications=20des=20tp,=20exercices,=20ajouts?= =?UTF-8?q?=20d'indications=20en=20JS,=20d'exemples=20en=20gloutons,=203?= =?UTF-8?q?=20semaines=20de=20boulot=20enfin=20voil=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gloutons/EXERCICES.md | 259 +++++++++++ gloutons/README.md | 159 +++++-- knn/DISTANCE.md | 4 +- knn/EXERCICES.md | 15 +- knn/IMPLEMENTATION.md | 126 +++++- knn/README.md | 10 +- knn/TP_KNN.md | 4 +- knn/pokemons_knn.py | 130 +++++- knn/pokemons_knn_original.py | 38 ++ web/README.md | 24 +- web/css/CORRECTION.md | 541 +++++++++++++++++++++++ web/css/README.md | 802 +++++++++++++++++++++++++++++++++++ web/html/CORRECTION.md | 333 +++++++++++++++ web/html/README.md | 579 +++++++++++++++++++++++++ web/js/CORRECTION.md | 161 +++++++ web/js/README.md | 682 ++++++++++++++++++++++++----- web/ressources/README.md | 49 +++ web/tp/README.md | 77 ++++ 18 files changed, 3821 insertions(+), 172 deletions(-) create mode 100644 gloutons/EXERCICES.md create mode 100644 knn/pokemons_knn_original.py create mode 100644 web/css/CORRECTION.md create mode 100644 web/css/README.md create mode 100644 web/html/CORRECTION.md create mode 100644 web/html/README.md create mode 100644 web/js/CORRECTION.md create mode 100644 web/ressources/README.md create mode 100644 web/tp/README.md diff --git a/gloutons/EXERCICES.md b/gloutons/EXERCICES.md new file mode 100644 index 0000000..251dd05 --- /dev/null +++ b/gloutons/EXERCICES.md @@ -0,0 +1,259 @@ +### Exercices - Algorithmes Gloutons + +--- + +## Exercice 1 : Achat de V-Bucks (Fortnite) + +Dans Fortnite, on peut acheter des V-Bucks avec les packs suivants : `{500, 1000, 2800, 5000, 13500}` V-Bucks. + +Tu veux acheter exactement **8000 V-Bucks** en utilisant le moins de packs possible. + +1. Appliquer l'algorithme glouton **à la main** pour obtenir 8000 V-Bucks. Combien de packs sont nécessaires ? +2. Existe-t-il une meilleure solution ? +3. Ce système est-il canonique (le glouton donne-t-il toujours l'optimal) ? + +
+Voir la correction + +**Question 1 : Algorithme glouton** +- On prend 5000 (le plus grand ≤ 8000) : reste 3000 +- On prend 2800 : reste 200 +- 200 < 500, on ne peut plus rien prendre ! + +L'algorithme glouton **échoue** à donner exactement 8000. + +**Question 2 : Meilleure solution** +- 8000 = 5000 + 1000 + 1000 + 1000 = **4 packs** +- Ou : 8000 = 2800 + 2800 + 1000 + 1000 + 500 - 100... non ça ne marche pas non plus facilement. + +En fait, 8000 = 5000 + 1000 × 3 = **4 packs** est la solution. + +**Question 3 :** +Non, ce système n'est pas canonique. L'algorithme glouton ne fonctionne pas toujours car les valeurs ne sont pas des multiples les unes des autres. + +
+ +--- + +## Exercice 2 : Playlist Spotify optimale + +Tu prépares une playlist pour un trajet de **60 minutes**. Tu veux y mettre le maximum de sons parmi tes favoris : + +| Titre | Artiste | Durée (min) | +|-------|---------|-------------| +| Alibi | Sevdaliza | 4 | +| La Kiffance | Naps | 3 | +| Bande organisée | Jul & SCH | 6 | +| Mood | 24kGoldn | 2 | +| Fever | Dua Lipa | 3 | +| Blinding Lights | The Weeknd | 3 | +| Peaches | Justin Bieber | 3 | +| Heat Waves | Glass Animals | 4 | +| Iko Iko | Justin Wellington | 3 | +| Astronaut in the Ocean | Masked Wolf | 2 | + +**Objectif** : Maximiser le nombre de titres dans la playlist sans dépasser 60 minutes. + +1. Quelle stratégie gloutonne proposez-vous ? +2. Appliquer cette stratégie. Combien de titres obtenez-vous ? +3. Quelle est la durée totale de la playlist ? + +
+Voir la correction + +**Question 1 : Stratégie gloutonne** +Pour maximiser le **nombre** de titres, on prend les titres **les plus courts en premier**. + +**Question 2 : Application** +Tri par durée croissante : +- Mood (2 min) → total = 2 +- Astronaut in the Ocean (2 min) → total = 4 +- La Kiffance (3 min) → total = 7 +- Fever (3 min) → total = 10 +- Blinding Lights (3 min) → total = 13 +- Peaches (3 min) → total = 16 +- Iko Iko (3 min) → total = 19 +- Alibi (4 min) → total = 23 +- Heat Waves (4 min) → total = 27 +- Bande organisée (6 min) → total = 33 + +**Tous les 10 titres** rentrent dans 60 minutes ! + +**Question 3 :** +Durée totale : **33 minutes** + +*Note : Dans ce cas, la contrainte de 60 min n'était pas limitante. On aurait pu ajouter d'autres titres.* + +
+ +--- + +## Exercice 3 : Inventaire Minecraft + +Tu joues à Minecraft et ton inventaire peut contenir **64 objets** maximum. Tu veux maximiser la valeur totale des objets pour le commerce avec les villageois. + +| Objet | Quantité dispo | Valeur (émeraudes) | Poids (slots) | +|-------|----------------|-------------------|---------------| +| Diamant | 10 | 8 | 1 | +| Lingot d'or | 20 | 3 | 1 | +| Fer | 50 | 1 | 1 | +| Livre enchanté | 5 | 15 | 1 | +| Émeraude | 30 | 1 | 1 | + +*Chaque objet prend 1 slot, mais tu ne peux pas dépasser 64 slots au total.* + +1. Calculer le ratio valeur/slot pour chaque objet. +2. Appliquer l'algorithme glouton (meilleur ratio d'abord). +3. Quelle est la valeur totale obtenue ? + +
+Voir la correction + +**Question 1 : Ratios valeur/slot** + +| Objet | Valeur | Ratio | +|-------|--------|-------| +| Livre enchanté | 15 | 15.0 | +| Diamant | 8 | 8.0 | +| Lingot d'or | 3 | 3.0 | +| Émeraude | 1 | 1.0 | +| Fer | 1 | 1.0 | + +**Question 2 : Algorithme glouton** +Tri par ratio décroissant, on prend dans l'ordre : +- Livre enchanté : 5 objets → slots = 5, valeur = 75 +- Diamant : 10 objets → slots = 15, valeur = 75 + 80 = 155 +- Lingot d'or : 20 objets → slots = 35, valeur = 155 + 60 = 215 +- Émeraude : 29 objets (64-35=29) → slots = 64, valeur = 215 + 29 = 244 + +**Question 3 : Valeur totale** +**244 émeraudes** avec 64 slots utilisés. + +
+ +--- + +## Exercice 4 : Planning de stream Twitch + +Tu es streamer et tu dois planifier tes lives pour demain. Plusieurs marques te proposent des partenariats avec des créneaux imposés : + +| Sponsor | Début | Fin | Rémunération | +|---------|-------|-----|--------------| +| Red Bull | 14h | 17h | 200€ | +| Logitech | 15h | 18h | 180€ | +| Samsung | 17h | 19h | 150€ | +| Razer | 18h | 20h | 120€ | +| Discord | 19h | 21h | 100€ | +| NordVPN | 20h | 22h | 90€ | + +**Objectif** : Maximiser le nombre de partenariats (pas de chevauchement possible). + +1. Quelle stratégie gloutonne permet de maximiser le **nombre** de streams ? +2. Appliquer cette stratégie. Quels sponsors sélectionnez-vous ? +3. Implémenter l'algorithme en Python. + +
+Voir la correction + +**Question 1 : Stratégie** +Pour maximiser le nombre d'activités, on trie par **heure de fin croissante** et on prend chaque activité compatible. + +**Question 2 : Application** +Tri par heure de fin : Red Bull (17h), Logitech (18h), Samsung (19h), Razer (20h), Discord (21h), NordVPN (22h) + +- Red Bull (14h-17h) : OK +- Logitech (15h-18h) : chevauche Red Bull, refusé +- Samsung (17h-19h) : commence à 17h, Red Bull finit à 17h, OK +- Razer (18h-20h) : chevauche Samsung, refusé +- Discord (19h-21h) : commence à 19h, Samsung finit à 19h, OK +- NordVPN (20h-22h) : chevauche Discord, refusé + +**Sponsors sélectionnés : Red Bull, Samsung, Discord** = 3 streams + +Rémunération totale : 200 + 150 + 100 = **450€** + +**Question 3 : Code Python** + +```python +def planning_stream(sponsors): + """ + sponsors : liste de tuples (nom, debut, fin, remuneration) + Retourne la liste des sponsors sélectionnés + """ + # Trier par heure de fin croissante + sponsors_tries = sorted(sponsors, key=lambda x: x[2]) + + selection = [] + derniere_fin = 0 + + for nom, debut, fin, remuneration in sponsors_tries: + if debut >= derniere_fin: + selection.append((nom, debut, fin, remuneration)) + derniere_fin = fin + + return selection + +# Exemple d'utilisation +sponsors = [ + ('Red Bull', 14, 17, 200), + ('Logitech', 15, 18, 180), + ('Samsung', 17, 19, 150), + ('Razer', 18, 20, 120), + ('Discord', 19, 21, 100), + ('NordVPN', 20, 22, 90) +] + +planning = planning_stream(sponsors) +total = sum(s[3] for s in planning) +print("Sponsors sélectionnés :") +for nom, debut, fin, remuneration in planning: + print(f" {nom} : {debut}h - {fin}h ({remuneration}€)") +print(f"Rémunération totale : {total}€") +``` + +
+ +--- + +## Exercice 5 : Réflexion + +1. Dans l'exercice 4, on a maximisé le **nombre** de streams. Si on voulait maximiser la **rémunération totale**, l'algorithme glouton donnerait-il le même résultat ? + +2. Parmi ces situations, lesquelles peuvent être résolues de manière optimale par un algorithme glouton ? + - Choisir les meilleurs joueurs pour une équipe FIFA (budget limité) + - Organiser l'ordre de visionnage de films sur Netflix (minimiser le temps total) + - Rendre la monnaie avec des pièces européennes + - Trouver le chemin le plus court dans Google Maps + +
+Voir la correction + +**Question 1 :** +Non ! Pour maximiser la rémunération, l'algorithme glouton pourrait donner un résultat différent. + +Avec notre solution (Red Bull + Samsung + Discord) : 450€ + +Si on avait pris Red Bull (200€) + Razer (120€) + NordVPN (90€) : 410€ (moins bien) + +Mais que se passe-t-il si on prend Logitech (180€) + Discord (100€) = 280€ ? C'est pire. + +En fait, pour ce problème précis, la solution gloutonne par heure de fin est bonne, mais **maximiser la rémunération** est un problème différent (problème du sac à dos déguisé) qui ne se résout pas toujours de manière optimale avec un glouton. + +**Question 2 :** + +| Situation | Optimal avec glouton ? | +|-----------|------------------------| +| Équipe FIFA (budget) | Non (c'est un sac à dos) | +| Ordre films Netflix | Ça dépend du critère... | +| Monnaie européenne | Oui (système canonique) | +| Google Maps | Non (Dijkstra n'est pas vraiment glouton au sens strict, mais utilise une approche similaire) | + +
+ +---------- + +Auteur : Florian Mathieu + +Licence CC BY NC + +Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International diff --git a/gloutons/README.md b/gloutons/README.md index d9bd120..238c400 100644 --- a/gloutons/README.md +++ b/gloutons/README.md @@ -22,16 +22,16 @@ Selon le problème on utilise une méthode pour résoudre ce dernier. Il peut ex À un problème d'optimisation, on associe une fonction objective. -Prenons un exemple simple : la liste des 23 joueurs selectionnés pour la coupe du monde : +Prenons un exemple simple : la liste des 23 joueurs sélectionnés pour la coupe du monde : ``` Didier Desvilles est bien embêté : il doit choisir 23 joueurs pour aller jouer une compétition. Dans cette optique, il décide d'utiliser une méthode simple: choisir le meilleur joueur possible pour chaque poste, puis le deuxième meilleur comme remplaçant. -Peu importe si les joueurs selectionnés ne s'entendent pas forcément très bien, comme Olivier Gibrun et Karim Benzemb, l'important c'est de faire le meilleur choix à chaque étape. +Peu importe si les joueurs sélectionnés ne s'entendent pas forcément très bien, comme Olivier Gibrun et Karim Benzemb, l'important c'est de faire le meilleur choix à chaque étape. ``` -Un algorithme glouton ne renvoie pas obligatoirement un résultat optimal, dans ce cas la, on parle ***d'heuristique.*** Il renverra un résultat optimal pour chacun des sous-problèmes. +Un algorithme glouton ne renvoie pas obligatoirement un résultat optimal, dans ce cas-là, on parle ***d'heuristique.*** Il renverra un résultat optimal pour chacun des sous-problèmes. Un algorithme glouton ne revient pas sur un sous-problème déjà traité. @@ -41,23 +41,19 @@ Un algorithme glouton ne revient pas sur un sous-problème déjà traité. > Le problème du rendu de monnaie s'énonce de la façon suivante : étant donné un système de monnaie (pièces et billets), comment rendre une somme donnée de façon optimale, c'est-à-dire avec le nombre minimal de pièces et billets ? -On veut programmer une caisse automatique qui rend de façon optimal la monnaie (le moins de pièces/billets) : +On veut programmer une caisse automatique qui rend de façon optimale la monnaie (le moins de pièces/billets) : #### **Des exemples** -``` -Système de monnaie : ```{1, 2, 5, 10}``` +**Exemple 1 :** +- Système de monnaie : `{1, 2, 5, 10}` +- Recherché : `14` +- Résultat optimal : `10 + 2 + 2` (3 pièces) -Recherché : ```14``` - -Résultat optimal : 10 + 2 + 2 -``` - -Système de monnaie : ```{1, 2, 5, 7, 10}``` - -Recherché : ```14``` - -Résultat optimal : 7 + 7 +**Exemple 2 :** +- Système de monnaie : `{1, 2, 5, 7, 10}` +- Recherché : `14` +- Résultat optimal : `7 + 7` (2 pièces) #### **Algorithme** @@ -86,9 +82,9 @@ print(rendu_monnaie(systeme_2, 14)) # Devrait afficher [10, 2, 2] qui est non o ``` -Force brute avec le ```system_2``` pour ```14```: +Force brute avec le ```systeme_2``` pour ```14```: -On cherche **toute** les solutions possibles : +On cherche **toutes** les solutions possibles : - \[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\] - \[2, 2, 2, 2, 2, 2, 2\] - \[5, 5, 2, 2\] @@ -97,7 +93,7 @@ On cherche **toute** les solutions possibles : - \[10, 2, 2] - ... -On parcours la liste des solutions pour garder la solution la plus optimal (la plus courte) : **\[7, 7\]**. +On parcourt la liste des solutions pour garder la solution la plus optimale (la plus courte) : **\[7, 7\]**. ### Exemples de problèmes que l'on peut résoudre par algorithme glouton @@ -108,20 +104,131 @@ On parcours la liste des solutions pour garder la solution la plus optimal (la p ### **Sac à dos** -2 systemes : \[(valeur, poids)\] +> Un voleur entre dans une maison avec un sac à dos pouvant contenir un poids maximum. Il trouve plusieurs objets, chacun ayant une valeur et un poids. Quels objets doit-il prendre pour maximiser la valeur totale sans dépasser la capacité du sac ? -- ```[(22, 11), (5, 5), (1, 1)]``` ; -- ```[(12, 11), (10, 5), (10, 4), (1, 1)]```. +#### Exemple -Pour un poids max de ```12```. +Objets disponibles (valeur, poids) : `[(60, 10), (100, 20), (120, 30)]` -Plusieurs stratégies : **valeur/poids**, **le + de valeur** ou **le poids le plus gros**. +Capacité du sac : `50 kg` -La fonction (et donc le résultat optimal) dépendra de la stratégie choisie. +#### Stratégies gloutonnes possibles + +1. **Prendre l'objet de plus grande valeur** en premier +2. **Prendre l'objet le plus léger** en premier +3. **Prendre l'objet avec le meilleur ratio valeur/poids** en premier (souvent la meilleure stratégie) + +#### Algorithme (stratégie ratio valeur/poids) + +```python +def sac_a_dos_glouton(objets, capacite): + """ + objets : liste de tuples (valeur, poids) + capacite : poids maximum du sac + """ + # Calculer le ratio valeur/poids et trier par ratio décroissant + objets_avec_ratio = [(v, p, v/p) for v, p in objets] + objets_tries = sorted(objets_avec_ratio, key=lambda x: x[2], reverse=True) + + poids_total = 0 + valeur_totale = 0 + sac = [] + + for valeur, poids, ratio in objets_tries: + if poids_total + poids <= capacite: + sac.append((valeur, poids)) + poids_total += poids + valeur_totale += valeur + + return sac, valeur_totale, poids_total + +# Exemple d'utilisation +objets = [(60, 10), (100, 20), (120, 30)] +capacite = 50 + +sac, valeur, poids = sac_a_dos_glouton(objets, capacite) +print(f"Objets pris : {sac}") +print(f"Valeur totale : {valeur}, Poids total : {poids}") +# Résultat : [(60, 10), (100, 20)] avec valeur=160 et poids=30 +# Note : ce n'est pas optimal ! L'optimal serait [(100, 20), (120, 30)] = 220 +``` + +> **Attention** : L'algorithme glouton ne donne pas toujours la solution optimale pour le problème du sac à dos ! + +--------- ### **Voyageur de commerce** -Le problème du **voyageur de commerce**, est un problème d'optimisation qui, étant donné une liste de villes, et des distances entre toutes les paires de villes, détermine un plus court circuit qui visite chaque ville une et une seule fois. +> Un voyageur de commerce doit visiter plusieurs villes exactement une fois, puis revenir à son point de départ. Quel est le chemin le plus court ? + +#### Exemple + +Imaginons 4 villes : A, B, C, D avec les distances suivantes : + +| De/Vers | A | B | C | D | +|---------|---|---|---|---| +| A | - | 10 | 15 | 20 | +| B | 10 | - | 35 | 25 | +| C | 15 | 35 | - | 30 | +| D | 20 | 25 | 30 | - | + +#### Stratégie gloutonne : "Plus proche voisin" + +À chaque étape, on se déplace vers la ville non visitée la plus proche. + +```python +def voyageur_commerce_glouton(distances, depart=0): + """ + distances : matrice des distances entre villes + depart : indice de la ville de départ + """ + n = len(distances) + visitees = [False] * n + chemin = [depart] + visitees[depart] = True + distance_totale = 0 + + ville_actuelle = depart + + for _ in range(n - 1): + # Trouver la ville non visitée la plus proche + plus_proche = None + dist_min = float('inf') + + for ville in range(n): + if not visitees[ville] and distances[ville_actuelle][ville] < dist_min: + dist_min = distances[ville_actuelle][ville] + plus_proche = ville + + # Se déplacer vers cette ville + chemin.append(plus_proche) + visitees[plus_proche] = True + distance_totale += dist_min + ville_actuelle = plus_proche + + # Retour au point de départ + distance_totale += distances[ville_actuelle][depart] + chemin.append(depart) + + return chemin, distance_totale + +# Exemple d'utilisation +distances = [ + [0, 10, 15, 20], # Distances depuis A + [10, 0, 35, 25], # Distances depuis B + [15, 35, 0, 30], # Distances depuis C + [20, 25, 30, 0] # Distances depuis D +] + +chemin, distance = voyageur_commerce_glouton(distances, depart=0) +villes = ['A', 'B', 'C', 'D'] +chemin_noms = [villes[i] for i in chemin] +print(f"Chemin : {' -> '.join(chemin_noms)}") +print(f"Distance totale : {distance}") +# Résultat : A -> B -> D -> C -> A avec distance = 80 +``` + +> **Note** : Le problème du voyageur de commerce est un problème **NP-difficile**. Pour un grand nombre de villes, trouver la solution optimale prend un temps exponentiel. L'approche gloutonne donne une solution acceptable rapidement, mais rarement optimale. -------- diff --git a/knn/DISTANCE.md b/knn/DISTANCE.md index 125a675..5a3682f 100644 --- a/knn/DISTANCE.md +++ b/knn/DISTANCE.md @@ -4,7 +4,7 @@ D'ordre général, on parle de distance ***euclidienne*** que l'on peut noter co Distance AB de deux points A (xa, ya) et B (xb,yb) = $$ -AB = \sqrt{(xa - xb)^2 + (ya - yb)^2} +AB = \sqrt{(x_a - x_b)^2 + (y_a - y_b)^2} $$ @@ -18,7 +18,7 @@ il s'agit également d'une autre mesure de distance populaire, qui mesure la val ​ $$ -Distance (AB) = |xa - xb| + |ya - yb| +Distance(A, B) = |x_a - x_b| + |y_a - y_b| $$ diff --git a/knn/EXERCICES.md b/knn/EXERCICES.md index 22065b8..2e72a09 100644 --- a/knn/EXERCICES.md +++ b/knn/EXERCICES.md @@ -1,9 +1,9 @@ ### Exercices -1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux. +1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux. 2. **Tester la fonction** avec des exemples simples pour vérifier son bon fonctionnement -Conseil : n'oubliez pas d'importer la bibliothèque `pandas`qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table)) +Conseil : n'oubliez pas d'importer la bibliothèque `pandas` qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table)) Le fichier [pokemons.csv](pokemons.csv) qui va avec. @@ -13,7 +13,7 @@ Ne pas oublier d'installer la bibliothèque pandas pip install --upgrade --proxy=172.16.0.253:3128 pandas ``` - +Pour charger les données : ```python import pandas as pd @@ -21,6 +21,11 @@ import pandas as pd pokemons = pd.read_csv('chemin/vers/pokemons.csv') ``` +--- + +
+Voir la correction + ```python import numpy as np @@ -35,6 +40,8 @@ pokemon2 = [85, 80] # Exemple de stats pour le Pokémon 2 distance = calculer_distance(pokemon1, pokemon2) print(f"La distance entre les deux Pokémon est : {distance}") - ``` +**Résultat attendu :** `La distance entre les deux Pokémon est : 31.400636936215164` + +
diff --git a/knn/IMPLEMENTATION.md b/knn/IMPLEMENTATION.md index 86ce3fa..088a7f2 100644 --- a/knn/IMPLEMENTATION.md +++ b/knn/IMPLEMENTATION.md @@ -48,7 +48,7 @@ import matplotlib.pyplot as plt # Supposons que ces listes contiennent les statistiques d'attaque et de défense de quelques Pokémon attaques = [55, 75, 150, 45] defenses = [45, 60, 50, 65] -noms = ['Pikachu', 'Bulbasaur', 'Charizard', 'Squirtle'] +noms = ['Pikachu', 'Bulbizarre', 'Salameche', 'Carapuce'] plt.figure(figsize=(10, 5)) # Définit la taille de la figure plt.scatter(attaques, defenses, color='red') # Crée un nuage de points avec les statistiques d'attaque et de défense @@ -70,6 +70,130 @@ Ce code génère un graphique en nuage de points où chaque point représente un -------- +### Implémentation de l'algorithme KNN + +Maintenant que nous savons visualiser nos données, passons à l'implémentation de l'algorithme. + +#### Étape 1 : Calculer la distance entre deux points + +```python +import math + +def calculer_distance(point1, point2): + """ + Calcule la distance euclidienne entre deux points. + point1 et point2 sont des listes de coordonnées [x, y] + """ + somme = 0 + for i in range(len(point1)): + somme += (point1[i] - point2[i]) ** 2 + return math.sqrt(somme) + +# Test +print(calculer_distance([0, 0], [3, 4])) # Affiche 5.0 +``` + +#### Étape 2 : Trouver les k plus proches voisins + +```python +def trouver_voisins(echantillons, mystere, k): + """ + Trouve les k plus proches voisins de mystere dans echantillons. + + Paramètres: + echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'} + mystere : liste [x, y] du point à classifier + k : nombre de voisins à retourner + + Retourne: + Liste des k plus proches voisins + """ + # Calculer la distance pour chaque échantillon + distances = [] + for echantillon in echantillons: + dist = calculer_distance(echantillon['coords'], mystere) + distances.append({'echantillon': echantillon, 'distance': dist}) + + # Trier par distance croissante + distances_triees = sorted(distances, key=lambda x: x['distance']) + + # Retourner les k premiers + return [d['echantillon'] for d in distances_triees[:k]] +``` + +#### Étape 3 : Déterminer la classe majoritaire + +```python +def classe_majoritaire(voisins): + """ + Détermine la classe la plus représentée parmi les voisins. + + Paramètres: + voisins : liste de dictionnaires avec une clé 'classe' + + Retourne: + La classe majoritaire (str) + """ + compteur = {} + for voisin in voisins: + classe = voisin['classe'] + if classe in compteur: + compteur[classe] += 1 + else: + compteur[classe] = 1 + + # Trouver la classe avec le plus grand nombre + classe_max = None + max_count = 0 + for classe, count in compteur.items(): + if count > max_count: + max_count = count + classe_max = classe + + return classe_max +``` + +#### Étape 4 : L'algorithme KNN complet + +```python +def knn(echantillons, mystere, k): + """ + Algorithme des k plus proches voisins. + + Paramètres: + echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'} + mystere : liste [x, y] du point à classifier + k : nombre de voisins à considérer + + Retourne: + La classe prédite pour mystere + """ + voisins = trouver_voisins(echantillons, mystere, k) + return classe_majoritaire(voisins) +``` + +#### Exemple complet + +```python +# Échantillon de Pokémon +pokemons = [ + {'coords': [49, 49], 'classe': 'Eau'}, # Ecayon + {'coords': [50, 95], 'classe': 'Psy'}, # Deoxys + {'coords': [80, 45], 'classe': 'Psy'}, # Eoko + {'coords': [90, 75], 'classe': 'Psy'}, # Groret + {'coords': [90, 75], 'classe': 'Eau'}, # Tarpaud +] + +# Pokémon mystère à classifier +pokemon_mystere = [65, 40] + +# Prédiction avec k=3 +prediction = knn(pokemons, pokemon_mystere, k=3) +print(f"Le Pokémon mystère est probablement de type : {prediction}") +``` + +-------- + Auteur : Florian Mathieu Licence CC BY NC diff --git a/knn/README.md b/knn/README.md index 2493fc8..374bbe3 100644 --- a/knn/README.md +++ b/knn/README.md @@ -23,7 +23,7 @@ Parmi les modèles et méthodes de classification, on peut citer : - K-NN Voisins - Arbres de décision -- Réseaux de neuronnes +- Réseaux de neurones ---------- @@ -33,7 +33,7 @@ L'algorithme des *k plus proches voisins* permet de résoudre certains problème Il s'agit d'une méthode de raisonnement à partir de cas connus : ce modèle va aider à prendre des décisions en recherchant un ou des cas similaires déjà résolus. -Attention, ici, il n'y a pas d’étape d ’apprentissage: uniquement une construction d ’un ***modèle*** à partir d’un échantillon d'apprentissage. +Attention, KNN est un algorithme dit ***paresseux*** (*lazy learning*) : contrairement à d'autres algorithmes de classification, il n'y a pas de phase d'entraînement où un modèle est construit. L'échantillon d'apprentissage est simplement mémorisé, et tout le travail de calcul se fait au moment de la prédiction. ### Qu'est ce qu'un modèle ? @@ -58,7 +58,7 @@ Le Professeur Chen, inventeur du Pokédex, utilise cet algorithme afin que son a Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristiques : leurs points de vie et leur valeur d'attaque. On peut prendre deux types pour commencer. -| Nom | Écayon | Deoxys | Éoko | Groret | Taraud | +| Nom | Écayon | Deoxys | Éoko | Groret | Tarpaud | | ------------- | ------ | ------ | ---- | ------ | ------ | | Points de vie | 49 | 50 | 80 | 90 | 90 | | Attaque | 49 | 95 | 45 | 75 | 75 | @@ -78,7 +78,7 @@ Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristi ![echantillon_2](assets/echantillon_pokemon_2.png) -Il devrait donc se trouver dans zone. On peut alors trouver ses cinq ou six plus proches voisins. +Il devrait donc se trouver dans cette zone. On peut alors trouver ses cinq ou six plus proches voisins. - Parmi ces voisins se trouvent deux Pokémons de type Eau, et trois de types Psy. - Le Pokémon mystère sera donc probablement de type Psy ! @@ -95,6 +95,8 @@ D'après vous, que faut-il pour pouvoir prédire la classe d'un Pokemon donné ? - Un pokemon *mystère* dont on souhaite prédire la classification - la valeur de *k* +> **Comment choisir k ?** On choisit généralement k impair (pour éviter les égalités lors du vote majoritaire) et une valeur raisonnable comme √n (racine carrée du nombre d'échantillons) ou entre 5 et 7 pour de petits échantillons. + Une fois ces données modélisées, on peut formuler notre algorithme: - Trouver dans l'échantillon, les *k* plus proches voisins du pokemon *mystère* diff --git a/knn/TP_KNN.md b/knn/TP_KNN.md index 5280ce1..1ffb6e3 100644 --- a/knn/TP_KNN.md +++ b/knn/TP_KNN.md @@ -1,7 +1,5 @@ ## Choixpeau Magique et KNN -## - ChoixpeauMagique @@ -87,7 +85,7 @@ def charger_table(nom_fichier): "courage": int(eleve[1]), "loyauté": int(eleve[2]), "sagesse":int(eleve[3]), - "malicez":int(eleve[4]), + "malice":int(eleve[4]), "maison" : eleve[5]}) return table diff --git a/knn/pokemons_knn.py b/knn/pokemons_knn.py index 373e4d3..73a5dd8 100644 --- a/knn/pokemons_knn.py +++ b/knn/pokemons_knn.py @@ -1,38 +1,128 @@ +# Imports regroupés en haut du fichier (PEP 8) import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + # Chargement des données pokemons = pd.read_csv('pokemons.csv') # Afficher les premières lignes pour vérification +print("=== Aperçu des données ===") print(pokemons.head()) -import matplotlib.pyplot as plt - -# Filtrons les données pour n'avoir que les Pokémon de type Eau -eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau']) - -# Création du graphe -plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau']) -plt.title('Points de vie vs Attribut Eau des Pokémon') -plt.xlabel('Points de vie') -plt.ylabel('Attribut Eau') -plt.show() +# Restructuration des données pour avoir une colonne "type" explicite +def restructurer_donnees(df): + """ + Transforme le DataFrame pour avoir des colonnes : nom, points_de_vie, attaque, type + """ + donnees = [] + for _, row in df.iterrows(): + if pd.notna(row['Pokemons de type Eau']): + donnees.append({ + 'nom': row['nom'], + 'points_de_vie': row['points de vie'], + 'attaque': row['Pokemons de type Eau'], + 'type': 'Eau' + }) + elif pd.notna(row['Pokemons de type Psy']): + donnees.append({ + 'nom': row['nom'], + 'points_de_vie': row['points de vie'], + 'attaque': row['Pokemons de type Psy'], + 'type': 'Psy' + }) + return pd.DataFrame(donnees) -import numpy as np +pokemons_clean = restructurer_donnees(pokemons) +print("\n=== Données restructurées ===") +print(pokemons_clean.head(10)) -# Ajoutons un Pokémon "mystère" -pokemon_mystere = [70, 65] +# Visualisation des données +def afficher_graphique(df, pokemon_mystere=None): + """ + Affiche un nuage de points des Pokémon par type + """ + plt.figure(figsize=(10, 6)) + + # Pokémon de type Eau + eau = df[df['type'] == 'Eau'] + plt.scatter(eau['points_de_vie'], eau['attaque'], color='blue', label='Type Eau', s=100) + + # Pokémon de type Psy + psy = df[df['type'] == 'Psy'] + plt.scatter(psy['points_de_vie'], psy['attaque'], color='purple', label='Type Psy', s=100) + + # Pokémon mystère si fourni + if pokemon_mystere: + plt.scatter(pokemon_mystere[0], pokemon_mystere[1], color='red', marker='X', s=200, label='Mystère') + + plt.title('Classification des Pokémon par type') + plt.xlabel('Points de vie') + plt.ylabel('Attaque') + plt.legend() + plt.grid(True) + plt.show() + + +# Fonction de calcul de distance euclidienne def calculer_distance(pokemon1, pokemon2): + """ + Calcule la distance euclidienne entre deux Pokémon + pokemon1 et pokemon2 sont des listes [points_de_vie, attaque] + """ return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2)))) -# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère" -distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1) -# Ajoutons ces distances au DataFrame -eau_pokemons['Distance au mystère'] = distances +# Algorithme KNN complet +def knn(df, pokemon_mystere, k=5): + """ + Algorithme des k plus proches voisins -# Affichons les Pokémon triés par leur distance au mystère -print(eau_pokemons.sort_values(by='Distance au mystère').head()) + Paramètres: + df : DataFrame contenant les Pokémon avec colonnes points_de_vie, attaque, type + pokemon_mystere : liste [points_de_vie, attaque] du Pokémon à classifier + k : nombre de voisins à considérer + + Retourne: + Le type prédit pour le Pokémon mystère + """ + # Calcul des distances pour chaque Pokémon + df_copy = df.copy() + df_copy['distance'] = df_copy.apply( + lambda row: calculer_distance([row['points_de_vie'], row['attaque']], pokemon_mystere), + axis=1 + ) + + # Tri par distance croissante et sélection des k premiers + k_voisins = df_copy.sort_values(by='distance').head(k) + + print(f"\n=== Les {k} plus proches voisins ===") + print(k_voisins[['nom', 'type', 'distance']]) + + # Vote majoritaire + type_majoritaire = k_voisins['type'].value_counts().idxmax() + votes = k_voisins['type'].value_counts() + + print(f"\n=== Votes ===") + print(votes) + + return type_majoritaire + + +# Exemple d'utilisation +if __name__ == "__main__": + # Définir un Pokémon mystère (points_de_vie, attaque) + pokemon_mystere = [65, 40] + + # Afficher le graphique avec le Pokémon mystère + afficher_graphique(pokemons_clean, pokemon_mystere) + + # Appliquer l'algorithme KNN avec k=5 + type_predit = knn(pokemons_clean, pokemon_mystere, k=5) + + print(f"\n=== Résultat ===") + print(f"Le Pokémon mystère ({pokemon_mystere[0]} PV, {pokemon_mystere[1]} attaque) est probablement de type : {type_predit}") diff --git a/knn/pokemons_knn_original.py b/knn/pokemons_knn_original.py new file mode 100644 index 0000000..373e4d3 --- /dev/null +++ b/knn/pokemons_knn_original.py @@ -0,0 +1,38 @@ +import pandas as pd + +# Chargement des données +pokemons = pd.read_csv('pokemons.csv') + +# Afficher les premières lignes pour vérification +print(pokemons.head()) + + +import matplotlib.pyplot as plt + +# Filtrons les données pour n'avoir que les Pokémon de type Eau +eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau']) + +# Création du graphe +plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau']) +plt.title('Points de vie vs Attribut Eau des Pokémon') +plt.xlabel('Points de vie') +plt.ylabel('Attribut Eau') +plt.show() + + +import numpy as np + +# Ajoutons un Pokémon "mystère" +pokemon_mystere = [70, 65] + +def calculer_distance(pokemon1, pokemon2): + return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2)))) + +# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère" +distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1) + +# Ajoutons ces distances au DataFrame +eau_pokemons['Distance au mystère'] = distances + +# Affichons les Pokémon triés par leur distance au mystère +print(eau_pokemons.sort_values(by='Distance au mystère').head()) diff --git a/web/README.md b/web/README.md index 337099b..e78cf3d 100644 --- a/web/README.md +++ b/web/README.md @@ -1,13 +1,23 @@ # Le web -> Le **Web** est le terme communément employé pour parle du World Wide Web, ou WWW, traduit en français par la toile d’araignée mondiale. Il fait référence au système hypertexte fonctionnant sur le réseau informatique mondial internet. +> Le **Web** est le terme communément employé pour parler du World Wide Web, ou WWW, traduit en français par la toile d’araignée mondiale. Il fait référence au système hypertexte fonctionnant sur le réseau informatique mondial Internet. -## +--------- + +## Sommaire + +| Chapitre | Description | +|----------|-------------| +| [HTML](html/) | Structure et contenu des pages web | +| [CSS](css/) | Mise en forme et apparence | +| [JavaScript](js/) | Interactivité et dynamisme | +| [Travaux Pratiques](tp/) | Exercices pratiques | +| [Ressources](ressources/) | Mémentos et aide-mémoires | + +--------- ## Historique - - Le "World Wide Web", plus communément appelé "Web" a été développé au CERN (Conseil Européen pour la Recherche Nucléaire) par le Britannique *Sir Timothy John Berners-Lee* et le Belge *Robert Cailliau* au début des années 90. À cette époque les principaux centres de recherche mondiaux étaient déjà connectés les uns aux autres, mais pour faciliter les échanges d'information Tim Berners-Lee met au point le système hypertexte. @@ -57,7 +67,7 @@ Vous devriez avoir quelque chose comme ça : ![site_genech](assets/site_genech.png) -Nous verrons dans un prochaine exercice ce que tout cela signifie. +Nous verrons dans un prochain exercice ce que tout cela signifie. ------- @@ -67,14 +77,14 @@ En attendant, si vous souhaitez en savoir plus avant d'attaquer les activités e - [MDN Web Docs](https://developer.mozilla.org/fr/docs/Learn) : Une ressource complète, écrite par Mozilla, pour les développeurs web. Les guides d'apprentissage du HTML et du CSS sont excellents pour les débutants. - [Codecademy](https://www.codecademy.com/learn/learn-html) : Des cours interactifs pour apprendre le HTML et le CSS. Leur format pratique est excellent pour se familiariser avec ces langages. -- [W3Schools](https://www.w3schools.com/html/) : Une autre excellente ressource avec des exemples de code et des quiz pour tester la compréhension. Il s'agit d'un site déployé par le World Wide Web Consortium, c'est à dire les personnes chargées de définir les règles d'utilisation du web. +- [W3Schools](https://www.w3schools.com/html/) : Une autre excellente ressource avec des exemples de code et des quiz pour tester la compréhension. Il s'agit d'un site déployé par le World Wide Web Consortium, c'est-à-dire les personnes chargées de définir les règles d'utilisation du web. - [freeCodeCamp](https://www.freecodecamp.org/learn/responsive-web-design/) : Un cours gratuit qui couvre à la fois le HTML et le CSS, ainsi que d'autres aspects de la conception web réactive. - [FlexboxFroggy](https://flexboxfroggy.com/#fr): Probablement mon outil préféré pour apprendre le CSS. En utilisant les règles du CSS, vous verrez une grenouille se déplacer sur votre écran selon ce que vous aurez écrit. - [Grid Garden](https://cssgridgarden.com/#fr) qui est un jeu similaire pour apprendre le CSS Grid Layout. - [Code With Blocks](https://codewithblocks.com/new) : Permet de mieux se représenter l'arborescence d'une page html, à l'aide de blocs. - [CSS Diner](https://flukeout.github.io) : Permet d'apprendre à manier les selecteurs CSS. -- [SmolCSS](https://smolcss.dev) : Une fois que vous maitriserez le CSS...Dans une dizaine d'années donc. +- [SmolCSS](https://smolcss.dev) : Une fois que vous maîtriserez le CSS... dans une dizaine d'années donc. Enfin, vous trouverez dans le répertoire [ressources](ressources/) plusieurs documents qui vous aideront. diff --git a/web/css/CORRECTION.md b/web/css/CORRECTION.md new file mode 100644 index 0000000..115fcc2 --- /dev/null +++ b/web/css/CORRECTION.md @@ -0,0 +1,541 @@ +# Correction des exercices - CSS + +--------- + +## Exercice 1 : Premiers styles + +**Fichier `style.css` :** + +```css +/* Couleur de fond de la page */ +body { + background-color: #f0f0f0; +} + +/* Titres h1 en bleu et centrés */ +h1 { + color: blue; + text-align: center; +} + +/* Paragraphes en Arial, taille 16px */ +p { + font-family: Arial, sans-serif; + font-size: 16px; +} +``` + +**Fichier HTML associé :** + +```html + + + + + Exercice 1 + + + +

Mon titre principal

+

Ceci est un paragraphe de texte pour tester mes premiers styles CSS.

+

Voici un deuxième paragraphe.

+ + +``` + +**Points importants :** +- `#f0f0f0` est un gris très clair en hexadécimal +- `sans-serif` est une police de secours si Arial n'est pas disponible +- Les commentaires en CSS s'écrivent entre `/*` et `*/` + +--------- + +## Exercice 2 : Les classes + +```css +/* Classe pour texte important */ +.important { + color: red; + font-weight: bold; +} + +/* Classe pour encadrer un élément */ +.encadre { + border: 2px solid black; + padding: 10px; +} + +/* Classe pour coins arrondis */ +.arrondi { + border-radius: 15px; +} +``` + +**Exemple d'utilisation en HTML :** + +```html + + + + + Exercice 2 + + + +

Ce texte est important !

+ +

Ce texte est encadré.

+ +

Ce texte est encadré avec des coins arrondis.

+ +
+ On peut combiner plusieurs classes sur un même élément ! +
+ + +``` + +**Points importants :** +- Les classes commencent par un point `.` en CSS +- On peut appliquer plusieurs classes à un élément en les séparant par des espaces +- `font-weight: bold` met le texte en gras +- `padding` ajoute un espace intérieur entre le contenu et la bordure + +--------- + +## Exercice 3 : Navigation + +**Fichier `style.css` :** + +```css +/* Reset des marges par défaut de la liste */ +nav ul { + list-style: none; /* Supprime les puces */ + margin: 0; + padding: 0; + display: flex; /* Affichage horizontal */ + background-color: #333; +} + +/* Style des éléments de liste */ +nav li { + margin: 0; +} + +/* Style des liens */ +nav a { + display: block; + color: white; + text-decoration: none; /* Supprime le soulignement */ + padding: 15px 20px; +} + +/* Effet au survol */ +nav a:hover { + background-color: #555; + color: #ffd700; /* Couleur dorée */ +} +``` + +**Fichier HTML :** + +```html + + + + + Navigation + + + + + + +``` + +**Points importants :** +- `list-style: none` supprime les puces de la liste +- `display: flex` sur le `