CCINP Informatique PC 2026

Thème de l'épreuve Autour de l'astro-informatique
Principaux outils utilisés algorithmes de classification, théorie des graphes, SQL
Mots clefs astro-informatique

Corrigé

 :
👈 gratuite pour tous les corrigés si tu crées un compte
👈 l'accès aux indications de tous les corrigés ne coûte que 1 € ⬅ clique ici
👈 gratuite pour tous les corrigés si tu crées un compte
- - - - - - - - - - - - - - - -
👈 gratuite pour ce corrigé si tu crées un compte
- - - - - - - - - - - - - - -

Énoncé complet

(télécharger le PDF)
                                                                                   

Énoncé obtenu par reconnaissance optique des caractères


SESSION 2026

PC5IN

ÉPREUVE MUTUALISÉE AVEC E3A-POLYTECH
ÉPREUVE SPÉCIFIQUE - FILIÈRE PC
____________________

INFORMATIQUE
Durée : 3 heures
____________________
N.B. : le candidat attachera la plus grande importance à la clarté, à la 
précision et à la concision de la rédaction.
Si un candidat est amené à repérer ce qui peut lui sembler être une erreur 
d'énoncé, il le signalera sur sa copie
et devra poursuivre sa composition en expliquant les raisons des initiatives 
qu'il a été amené à prendre.
RAPPEL DES CONSIGNES
·

·
·

Utiliser uniquement un stylo noir ou bleu foncé non effaçable pour la rédaction 
de votre composition ; d'autres
couleurs, excepté le vert, bleu clair ou turquoise, peuvent être utilisées, 
mais exclusivement pour les schémas
et la mise en évidence des résultats.
Ne pas utiliser de correcteur.
Écrire le mot FIN à la fin de votre composition.

________________________________________________________________________________

Les calculatrices sont interdites.

Le sujet est composé de quatre parties indépendantes.
L'épreuve est à traiter en langage Python sauf pour les bases de données.
Les différents algorithmes doivent être rendus dans leur forme définitive sur 
le Document
Réponse dans l'espace réservé à cet effet en respectant les éléments de syntaxe 
du langage (les brouillons ne sont pas acceptés).
La réponse ne doit pas se limiter à la rédaction de l'algorithme sans 
explication, les programmes doivent être expliqués et commentés de manière 
raisonnable.
Énoncé et Annexe : 16 pages
Document Réponse : 12 pages
Seul le Document Réponse (DR) doit être rendu dans son intégralité (le QR Code 
doit
être collé sur la première page du DR).
1/16

A

Autour de l'astro-informatique
Introduction
L'objectif du travail proposé est de découvrir plusieurs algorithmes différents 
utilisés dans
diverses branches de l'astrophysique pour aider à l'analyse et à la 
compréhension des données récoltées.
Le sujet abordera les points suivants dans quatre parties totalement 
indépendantes :
- extraction des informations contenues dans une base de données astronomiques 
issue
du SDSS (Sloan Digital Sky Survey) 
- classification des galaxies en différentes classes spectrales à l'aide de 
l'algorithme des
k-moyennes 
- classification des étoiles en différentes classes spectrales à l'aide de 
l'algorithme des
k plus proches voisins 
- reconnaissance de la structure à grande échelle de l'Univers à l'aide de la 
théorie des
graphes.

ANNEXE
Rappels des syntaxes en Python
Hormis les fonctions ou méthodes présentées ou utilisées ci-après (len, range, 
.copy(), .items(),
etc), il n'est pas autorisé (sauf exceptions explicitement signalées) 
d'utiliser de fonctions ou méthodes
préimplémentées en Python pouvant trivialiser certaines questions (min, max, 
.sort(), ...).
Fonctionnalités
définir une liste
définir un dictionnaire
accéder à un élément
extraire une sous-liste
vérifier si une clé est dans un dictionnaire (supposé
de complexité constante dans le pire des cas)
ajouter un élément à une liste
supprimer le dernier élément d'une liste et le renvoyer
copier une liste
Vérifier la non égalité de deux listes (complexité
linéaire en la taille des listes dans le pire des cas)
ajouter un élément à un dictionnaire
parcours en position d'une liste
parcours en valeur d'une liste
parcours en valeur d'un dictionnaire
parcours des clés d'un dictionnaire
parcours des clés et des valeurs d'un dictionnaire

2/16

Commandes Python
L = [1,2,3]
dic = {'a':0,'b':1,'c':2}
L[0] renvoie 1
dic['a'] renvoie 0
L[1:2] renvoie [2]
'a' in dic renvoie True
L.append(5)
a = L.pop()
L2 = L.copy()
L1 != L2
dic['d'] = 4
for i in range(len(L)): print(L[i])
for v in L: print(v)
for v in dic.values(): print(v)
for c in dic : print(c)
for c, v in dic.items() : print(c, v)

Partie I - Interrogation d'une base de données astronomiques
Le SDSS (Sloan Digital Sky Survey) est un programme de surveillance 
systématique du ciel.
Un télescope balaie chaque nuit une portion du ciel à la recherche de galaxies 
lointaines
et des systèmes automatisés traitent les photographies obtenues pour repérer et 
classifier
les objets astronomiques qui s'y trouvent en notant un grand nombre 
d'informations utiles
aux astronomes. Nous nous contenterons ici d'en utiliser un sous-ensemble 
pertinent pour
donner l'idée générale des manipulations.
I.1 - Observation du ciel et poids des données
Entre autres choses, le SDSS collecte deux types de données primaires :
- les photographies du ciel dans une couleur donnée 
- les spectres pris pour certains objets lumineux du ciel détectés sur les 
photographies
précédentes.
Une photographie est une matrice de 2 048 pixels de largeur pour 1 489 de 
hauteur, chaque
pixel pouvant contenir 256 valeurs possibles (entiers de 0 à 255).
Q1. Donner le nombre minimal de bits nécessaires pour encoder la valeur d'un 
pixel.
En déduire la taille approximative d'une photographie (arrondie au mégaoctet 
près).
Un spectre consiste à noter l'intensité lumineuse provenant de l'objet observé 
en fonction
de la longueur d'onde. Les spectres considérés notent cette valeur sur un 
flottant en 64 bits
pour des longueurs d'onde allant de 380 à 920 nm par pas de 0,1 nm.
Q2. Déterminer l'espace disque occupé par un spectre (arrondi au kilooctet 
près) si on se
contente de stocker les valeurs d'intensité lumineuse pour chaque longueur 
d'onde
sous forme d'une succession de flottants.
I.2 - Interrogation de la base en SQL
Une fois le ciel complètement cartographié, la base de données résultante est 
mise à disposition de la communauté scientifique via une interface web 1 que 
l'on peut interroger à volonté.
La base de données est constituée de plusieurs tables avec notamment une pour 
chaque
type d'observations (PhotoObj pour les objets repérés sur les photographies et 
SpecObj pour
les spectres de certains de ces objets).
Décrivons dans un premier temps la table PhotoObj qui contient les informations 
concernant
les objets célestes présents sur les photographies prises par le télescope. 
Chaque photographie vient avec son lot d'objets lumineux potentiellement 
intéressants et pour lesquels
on mesure des données issues de la lumière reçue sur la photographie. La table 
a pour
attributs :
- objID (int, clef primaire) : identifiant unique dans la base d'un objet au 
sens « zone lumineuse intéressante sur une photographie donnée ». Si un même 
objet astrophysique
est observé sur deux photographies différentes, il aura deux objID uniques 
différents,
un pour chaque observation 
- ra (float) et dec (float) : respectivement l'ascension droite et la 
déclinaison qui sont un
couple d'angles (similaires à  et  en coordonnées sphériques) permettant de 
définir
la direction d'observation de l'objet dans le ciel 
- z (float) : magnitude absolue Mz (soit la luminosité) dans la bande z 
(infrarouge).
1. Voir http://skyserver.sdss.org

3/16

Le deuxième outil d'observation est un spectrographe qui, dans un second temps, 
fait des
observations des objets jugés intéressants dans les photographies précédentes. 
Un certain
nombre d'objets (chacun avec un objID donné) sont sélectionnés pour être 
spectrographiés
via un réseau de fibres optiques placées sur cet instrument (on note 
targetObjId l'identifiant
de l'objet photographique de la table PhotoObj qui sert de source de référence 
à l'observation
spectrographique). Néanmoins, un même objet astrophysique peut être à nouveau 
pris en
photo plus tard avec des résultats de meilleure qualité, on note donc aussi 
dans la base le
bestObjId qui correspond à la meilleure photo connue pour cet objet (le 
télescope s'améliore
au cours du temps, donc si on réobserve une étoile ou une galaxie, les 
résultats obtenus
peuvent être de meilleure qualité). La table associée est nommée SpecObj et 
contient les
attributs :
- SpecObjId (int, clef primaire) : identifiant unique du spectre 
- bestObjId (int) : identifiant de l'observation la plus précise de la table 
PhotoObj qui
correspond au spectre 
- targetObjId (int) : identifiant de l'observation de la table PhotoObj qui a 
déclenché la
procédure permettant d'obtenir le spectre 
- class (str) : classification (déduite du spectre) de l'objet astronomique 
associé, cela
peut être une étoile ('STAR') ou une galaxie ('GALAXY') 
- z (float) : redshift (décalage vers le rouge) z de l'objet considéré (à ne 
surtout pas
confondre avec PhotoObj.z qui est une luminosité), cela mesure l'éloignement de 
l'objet le long de la ligne de visée.
Q3. Écrire une requête SQL qui compte parmi les spectres de la table SpecObj 
ceux dont
la meilleure observation photographique correspondante est effectivement celle 
qui a
servi de cible.
Q4. Écrire une requête SQL qui renvoie les identifiants des objets 
photographiques
(tels que stockés dans la table PhotoObj) ainsi que le nombre de spectres qui 
leur
sont associés (via l'attribut bestObjId) en les ordonnant par ordre décroissant 
du
nombre de spectres associés et en ne gardant que les objets associés à au moins
deux spectres.
Dans la partie IV, on a besoin de cartographier la disposition des galaxies 
dans l'Univers.
Le SDSS permet de récupérer les données voulues pour faire apparaître les 
filaments galactiques.
Q5. Écrire une requête SQL qui récupère l'identifiant, l'ascension droite, la 
déclinaison et
le redshift de tous les objets classifiés en tant que galaxies dans la base et 
dont la magnitude absolue dans l'infrarouge est inférieure à 17 et l'ascension 
droite est comprise
entre 100 et 250 . On utilisera pour ce faire la meilleure observation 
photographique
associée à un spectre donné.

4/16

Partie II - Classification de spectres de galaxies
Les observations spectrographiques du SDSS répertorient plus de 5 millions de 
spectres
dont près de 3 millions correspondent à des galaxies. Face à une telle masse de 
données, il est nécessaire de disposer d'un processus de classification 
automatique afin de
pouvoir étudier les populations galactiques qui ont des caractéristiques 
similaires. L'objectif
de cette partie est de construire une telle classification en s'appuyant sur 
l'algorithme des
k-moyennes.
II.1 - Justification de l'approche
Q6. Rappeler en quelques phrases en quoi consiste l'algorithme des k-moyennes 
et en
particulier ce que représente le paramètre k dans cet algorithme.
II.2 - Distance entre deux spectres
On définit la distance entre deux spectres (vus comme des vecteurs à N 
composantes)

 N-1

1 
d(S1 , S2 ) =
S1,i - S2,i 2 .
N i=0
Informatiquement, un spectre est manipulé sous forme d'une liste de N flottants.

Q7. Écrire une fonction distance(S1, S2) qui, étant donné deux listes de 
flottants de
même taille S1 et S2 représentatives de deux spectres, renvoie la distance 
entre les
deux spectres comme définie ci-dessus.
Néanmoins, les observations sont souvent entachées d'erreurs du fait des rayons 
cosmiques
ou de capteurs localement défectueux, ce qui pousse à ignorer certaines 
positions de chaque
spectre. Les listes représentant les spectres contiennent donc par endroit la 
valeur None pour
simuler l'absence de valeur acceptable.
Q8. Écrire une fonction positions_a_rejeter(S1, S2) qui prend en entrée deux 
listes et
renvoie la liste des indices i pour lesquels soit S1[i], soit S2[i] vaut None.
Q9. Implémenter une fonction est_absent(element, L) qui renvoie True si element 
est
absent de la liste L et False sinon. Donner la complexité temporelle dans le 
pire des
cas de votre implémentation en fonction de r = len(L).
On propose deux implémentations (qui ne sont pas forcément optimales en termes 
de complexité) du calcul de distance entre les spectres S1 et S2 en utilisant 
la liste a_rejeter des
positions à rejeter sur l'ensemble des deux spectres.
def d i s t a n c e 2 ( S1 , S2 , a _ r e j e t e r ) :
d = 0
N = len ( S1 )
m = N - len ( a _ r e j e t e r )
f o r i i n range (N) :
i f es t _ a b s e n t ( i , a _ r e j e t e r ) :
d = d + ( S1 [ i ] -S2 [ i ] ) ** 2
r e t u r n ( d / m) ** 0 . 5

def d i s t a n c e 3 ( S1 , S2 , a _ r e j e t e r ) :
d = 0
N = len ( S1 )
m = N - len ( a _ r e j e t e r )
S1c = S1 . copy ( )
S2c = S2 . copy ( )
for i in a _ r e j e t e r :
S1c [ i ] = 0
S2c [ i ] = 0
f o r i i n range (N) :
d = d + ( S1c [ i ] - S2c [ i ] ) ** 2
r e t u r n ( d / m) ** 0 . 5

5/16

Q10. Énoncer une propriété invariante de boucle qui serait utile pour démontrer 
la correction
de la fonction distance2 (on ne demande pas de démontration).
Q11. Estimer les complexités temporelles de chacune des deux implémentations 
distance2
et distance3 en fonction de N = len(S1) et de r = len(a_rejeter).

Pour simplifier, on supposera par la suite que les spectres n'ont aucun défaut 
et que les listes
associées ne contiennent que des flottants.

II.3 - Implémentation des k-moyennes

Notations concernant les variables manipulées ci-après :
- S correspond à un spectre, représenté informatiquement par une liste de N 
flottants.
- spectres est une liste de p éléments contenant l'ensemble des spectres S à 
classifier.
- C correspond au barycentre d'un ensemble de spectres S, lui même liste de N 
flottants.
On appellera un tel barycentre un centroïde.
- centroides est une liste de k éléments contenant l'ensemble des centroïdes.
- partitionnement est une liste de p éléments (autant que de spectres à 
classifier) telle
que si on pose j = partitionnement[i], alors C = centroides[j] est le centroïde
dont S = spectres[i] est le plus proche.

On suppose disposer des fonctions suivantes :
- calcule_centroides(partitionnement, spectres) qui renvoie la liste des 
barycentres de chaque groupe dans la partition représentée par la liste 
partitionnement 
- initialise(k, spectres) qui renvoie une liste de k spectres pris 
aléatoirement parmi
la liste spectres 
- trouve_centroide_le_plus_proche(S, centroides) qui renvoie l'indice du 
centroïde
le plus proche du spectre S dans la liste centroides.

Q12. Écrire une fonction produit_partition(spectres, centroides) qui renvoie 
une liste
de p éléments et qui contient pour chaque spectre de spectres l'indice du 
centroïde
auquel il est associé dans la liste centroides. Il faut utiliser au moins une 
des fonctions
introduites précédemment.
On donne l'exemple suivant dans lequel on suppose disposer de 5 spectres (dans
les variables S1 à S5) à répartir autour de 3 centroïdes (dans les variables C1 
à C3).
Les spectres S1, S3 et S4 ont C2 pour centroïde le plus proche alors que S2 est 
plus
proche de C3 et S5 de C1. On aurait alors :
>>> s p e c t r e s = [ S1 , S2 , S3 , S4 , S5 ]
>>> c e n t r o i d e s = [ C1 , C2 , C3 ]
>>> p r o d u i t _ p a r t i t i o n ( s p e c t r e s , c e n t r o i d e s )
[1 , 2 , 1 , 1 , 0]

Q13. En utilisant des fonctions parmi celles introduites plus haut ainsi que 
celle de la question précédente, écrire une fonction k_moyennes(spectres, k) 
qui, étant donné une
liste spectres de spectres et un entier k renvoie deux listes :
- une liste centroides de k éléments qui contient les centroïdes de chaque 
groupe
trouvé 
- une liste partitionnement de p éléments qui contient pour chaque spectre de
spectres l'indice du centroïde auquel il est associé dans la liste centroides
précédente après convergence de l'algorithme.
On considèrera que l'algorithme a convergé quand la liste partitionnement est 
identique entre deux itérations successives.
6/16

II.4 - Détermination automatique du nombre de groupes
L'algorithme précédent est efficace quand on connait déjà le nombre de groupes 
souhaités,
mais ce n'est en général pas le cas et ici, on n'a pas d'idée a priori 
concernant le nombre de
groupes de galaxies que l'on peut différencier à partir de leurs spectres. 
L'algorithme proposé
est alors le suivant :
Étape 1 : choisir un nombre limité (disons k = 10) de spectres comme points de 
départ de
partitionnement 
Étape 2 : appliquer l'algorithme des k-moyennes à partir de ces points de 
départ 
Étape 3 : récupérer le groupe de spectres le plus peuplé et retirer les 
spectres associés de
la liste des spectres 
Étape 4 : s'il reste plus de 100 spectres à classifier, revenir à l'étape 1, 
sinon l'algorithme
s'arrête.
On suppose disposer d'une fonction principal_et_reste(partitionnement, 
spectres) qui
renvoie la liste des spectres appartenant au groupe majoritaire (défini à 
l'aide de la liste
partitionnement des numéros d'appartenance) ainsi que son complémentaire dans 
la liste
totale spectres des spectres.
Par exemple, si on suppose qu'on avait 5 spectres stockés dans les variables S1 
à S5 et
répartis selon 3 groupes numérotés de 0 à 2, alors on aurait
>>> s p e c t r e s
= [ S1 , S2 , S3 , S4 , S5 ]
>>> p a r t i t i o n n e m e n t = [ 1 , 2 , 1 , 1 , 0 ]
>>> p r i n c i p a l _ e t _ r e s t e ( p a r t i t i o n n e m e n t , s p e 
c t r e s )
[ S1 , S3 , S4 ] , [ S2 , S5 ]

puisque le groupe d'effectif majoritaire est numéroté 1 et contient les trois 
spectres S1, S3 et
S4 alors qu'il reste S2 et S5 en dehors de ce groupe.
Q14. Écrire la fonction groupes_de_spectres(spectres) qui, à partir de la liste 
spectres,
applique l'algorithme décrit plus haut et renvoie une liste des groupes sous 
forme d'une
liste de listes de spectres. À noter que les derniers spectres non classés à 
l'arrêt de
l'algorithme sont « perdus » et n'apparaîssent pas dans l'un des groupes de 
sortie.

Partie III - Classification de spectres stellaires
Les spectres d'étoile ont été parmi les premiers étudiés dès que cet outil a 
été fourni aux
astronomes à la fin du XIXe siècle. Ils ont donc conçu une classification en 
attribuant un
type spectral (O, B, A, F, G, K et M pour la classification usuelle 2 ) aux 
étoiles principales
visibles dans nos cieux. On dispose donc pour chaque type spectral d'un 
ensemble de R
étoiles étiquetées comme faisant partie de ce type spectral (pour R = 10, on 
aura un total de
70 spectres de références, 10 pour chacun des 7 types spectraux). Pour 
classifier automatiquement les étoiles du SDSS dans leur type spectral 
respectif, on va utiliser l'algorithme des
k plus proches voisins (aussi appelé algorithme k-NN pour « k-Nearest Neighbors 
»).
Q15. Expliquer simplement le principe de l'algorithme des k plus proches 
voisins. Donner
en particulier la signification du k dans ce cadre.
Q16. Pourquoi est-il préférable de prendre un nombre R identique de spectres de 
référence
dans chaque classe ? Que se passerait-il par exemple si les spectres de 
référence du
type O étaient 3 fois plus nombreux que ceux du type B, alors que les deux 
types sont
spectralement relativement proches l'un de l'autre ?
2. L'ordre était initialement alphabétique en fonction des raies rencontrées, 
mais les types ont été réordonnés par température de surface décroissante.

7/16

On fournit ci-après le code Python permettant d'appliquer l'algorithme des k 
plus proches
voisins. La liste references est une liste de spectres contenant 7R spectres 
différents :
les R premiers correspondent au type O, les R suivants au type B, etc. Elle est 
obtenue
via une fonction lit_spectres_de_reference() dont le fonctionnement n'est pas 
détaillé
ici. On suppose en outre disposer d'une fonction copie_ordonnee(L) qui, étant 
donnée une
liste L en argument, renvoie une copie de la liste ordonnée dans l'ordre 
croissant. La fonction distance(S1, S2) permet de calculer la distance entre 
deux spectres S1 et S2 comme
défini dans la partie II. La variable spectres_a_classifier est une liste 
contenant tous les
spectres d'étoiles à qui on doit attribuer un type spectral.
1 references = lit_spectres_de_reference ( )
2
3 t y p e s _ s p e c t r a u x = [ "O" , " B " , " A" , " F " , "G" , " K " , 
"M" ]
4
5 R = len ( r e f e r e n c e s ) / / len ( t y p e s _ s p e c t r a u x )
6
7 def mystere ( S , k ) :
8
L = []
9
for Sref in references :
10
d = distance (S, Sref )
11
L . append ( d )
12
copie_L = copie_ordonnee ( L )
13
s e u i l = copie_L [ k ]
14
r e s u l t a t s = [ 0 ] * len ( t y p e _ s p e c t r a u x )
15
f o r i i n range ( len ( r e f e r e n c e s ) ) :
16
if L[ i ] < seuil : 17 j = i // R 18 resultats [ j ] = resultats [ j ] + 1 19 M =max( r e s u l t a t s ) 20 f o r i i n range ( len ( r e s u l t a t s ) ) : 21 i f r e s u l t a t s [ i ] == M : 22 return types_spectraux [ i ] 23 24 c l a s s i f i c a t i o n = [ ] 25 f o r S i n s p e c t r e s _ a _ c l a s s i f i e r : 26 t y p e _ s p e c t r a l = mystere ( S , 8 ) 27 c l a s s i f i c a t i o n . append ( t y p e _ s p e c t r a l ) Q17. Quelle valeur de k a été choisie pour cette implémentation de l'algorithme des k plus proches voisins ? Sur quel numéro de ligne le voit-on ? Q18. Expliquer en une ligne (et sans paraphraser le code) ce que font les lignes 1 à 5, 8 à 11, 12 à 18, 19 à 22 et 24 à 27. Q19. Écrire la signature de la fonction mystere. On attend en particulier de spécifier les données attendues en entrée et ce que renvoit la fonction. Q20. Détailler deux défauts potentiels du script précédent en précisant les lignes de code incriminées. Q21. Proposer une modification de la fonction mystere qui permette de rajouter une notion de « niveau de confiance » dans le résultat renvoyé. Q22. Définir la notion de matrice de confusion. Proposer un algorithme en langage naturel qui permette de la calculer pour les spectres de référence. 8/16 Partie IV - Reconnaissance des filaments galactiques En raffinant un peu les requêtes de la partie I, on peut visualiser la structure tridimentionnelle des filaments galactiques comme indiqué sur la figure 1(a). (a) (b) Figure 1 - Placement original des galaxies en coordonnées ascension droite, déclinaison et redshift (a) et détection finale des filaments (b) Le but de cette partie est d'utiliser la théorie des graphes pour essayer de détecter plus facilement la structure filamentaire par le biais d'un « arbre couvrant de poids minimal » que l'on va construire, puis élaguer petit à petit pour bien faire apparaître les groupes galactiques comme ce que l'on peut voir sur la figure 1(b). On considère dans cette partie des graphes non orientés simples dont les arêtes sont pondérées par des poids dans R+ . On rappelle qu'un graphe est dit connexe lorsqu'il existe un chemin entre deux sommets quelconques du graphe. Un graphe non orienté simple T = (S, A) avec S l'ensemble de ses sommets et A l'ensemble de ses arêtes est appelé un arbre lorsqu'il vérifie les conditions équivalentes suivantes : 1. T est acyclique et connexe 2. T est connexe et |A| = |S| - 1 3. T est acyclique et |A| = |S| - 1. IV.1 - Exemple pour comprendre le concept Considérons un graphe connexe G à N sommets qui soit non orienté et à arêtes pondérées. Un exemple d'un tel graphe jouet est donné en figure 2 pour N = 5. On cherche à construire un graphe avec les mêmes N sommets en ne gardant que N - 1 arêtes parmi celles de G. On demande en plus que ce nouveau graphe reste connexe (c'est-à-dire qu'il existe toujours un chemin entre deux sommets quelconques de ce graphe) et de sorte que la somme des poids des arêtes soit minimale. C'est ce qu'on appelle un arbre couvrant de poids minimal. 9/16 L'algorithme que l'on va tout d'abord utiliser est un algorithme de type glouton : - on choisit un sommet de départ que l'on place dans l'arbre - tant qu'il reste des sommets non encore inclus dans l'arbre : - on sélectionne, parmi les arêtes liant un sommet de l'arbre aux sommets non encore inclus, celle qui est de pondération minimale, - on ajoute à l'arbre le sommet externe associé à cette arête (c'est le sommet qui est le plus proche de l'arbre construit jusqu'à présent en considérant la pondération comme une distance) en gardant en mémoire l'arête utilisée - à la fin, on a obtenu les N - 1 arêtes qui constituent l'arbre cherché. Sur l'exemple de la figure 2, en démarrant sur le sommet E, l'arête la plus légère mène à B, que l'on rajoute ainsi à l'arbre. Mais l'arête suivante est celle de poids 3 allant de E à A, on rajoute donc A à notre construction. L'arête suivante est celle de poids 1 qui va de A à C et finalement parmi celles qui vont à D, la plus légère est celle de poids 5 provenant de A. On a donc un ordre de remplissage de l'arbre sur notre exemple qui est E-B-A-C-D. B B 4 C C 4 6 1 7 1 A A 6 D 5 2 3 D 5 2 3 6 E E (a) (b) Figure 2 - Graphe-jouet (a) permettant d'illustrer l'algorithme utilisé pour déterminer un « arbre couvrant de poids minimal » constitué de N - 1 arêtes de somme des pondérations minimale et gardant un graphe connexe (b) Q23. Sur le graphe-jouet à 7 sommets du DR, donner l'ordre d'ajout des sommets quand on démarre du sommet D, dessiner l'arbre couvrant de poids minimal qui en résulte et donner la somme des pondérations associées. Q24. Appliquer à nouveau la procédure en partant à présent du sommet F. Commenter. En général, l'arbre obtenu n'a pas de raison d'être unique. Par exemple, pour un graphe où toutes les arêtes ont la même pondération, n'importe quel choix de N-1 arêtes qui conservent le caractère connexe peut servir d'arbre couvrant de poids minimal. Néanmoins, pour garantir l'unicité, il suffit que toutes les arêtes soient de poids différents, ce qui sera en pratique le cas dans l'utilisation prévue ici sur les galaxies. La figure 3 présente un modèle-jouet bidimensionnel avec les données à gauche et ce qu'un astronome voudrait obtenir à droite. Les sous-parties suivantes vont nous permettre d'atteindre progressivement cet objectif. 10/16 Figure 3 - Modèle jouet où chaque point représente une galaxie dans un espace bidimensionnel : on y a mis deux amas circulaires connectés par un filament de galaxies ainsi qu'un amas isolé. Le but de cette partie est de comprendre comment passer à l'aide de la théorie des graphes de la représentation brute de gauche à celle à droite où l'on visualise plus facilement le filament et l'amas isolé IV.2 - Application à la distribution de galaxies : initialisation du graphe Supposons que l'on dispose d'une liste coords qui contient les coordonnées spatiales (sous forme d'un triplet (xi , yi , zi )) de nos N galaxies dans un repère cartésien mieux adapté que la description en terme d'angles et de redshift. On définit le graphe pondéré non orienté G représentatif de notre assemblée de galaxies en prenant pour sommets les galaxies et en reliant deux à deux toutes les galaxies par une arête dont le poids correspond au carré de la distance euclidienne entre les deux sommets. Ainsi, pour deux galaxies i et j, le poids associé à l'arête i j sera : pi j = (xi - x j )2 + (yi - y j )2 + (zi - z j )2 . De ce graphe complet (où chaque sommet est connecté aux N - 1 autres), on veut extraire un arbre couvrant de poids minimal comme on l'a étudié dans la sous-partie précédente, c'est-à-dire un sous-graphe à N sommets mais seulement N - 1 arêtes, qui reste connexe. La figure 4 montre l'arbre couvrant de poids minimal associé à une distribution de points bidimensionnelle (on a simplement mis tous les zi à 0) qui fait aussi apparaître une structure filamentaire entre deux amas de points. Q25. Étant donné une liste coords qui contient les coordonnées des différentes galaxies (sous forme de triplets (x, y, z)) ainsi que les identifiants i et j de deux galaxies dans cette liste, écrire une fonction poids(coords, i, j) qui calcule et renvoie le poids pi j de l'arête associée à ces deux galaxies. Exemple d'utilisation : gal0 = ( 0 , 1 , 5) g a l 1 = ( -1 , 2 , -4) gal2 = ( 3 , 2 , 5) coords = [ gal0 , gal1 , g a l 2 ] p o i d s ( coords , 0 , 2 ) # D o i t r e n v o y e r 10 = (0 -3) ** 2 + (1 -2) ** 2 + (5 -5) ** 2 11/16 Figure 4 - Modèle jouet où chaque point représente une galaxie dans un espace bidimensionnel : on y a mis deux amas circulaires connectés par un filament de galaxies. La figure de droite indique l'arbre couvrant de poids minimal qui a été trouvé en partant de la configuration initiale de gauche Q26. Définir une fonction matrice_adjacence(coords) qui, à partir de la donnée de la liste des coordonnées, renvoie, sous forme de liste de listes, la matrice d'adjacence pour le graphe pondéré associé aux données galactiques : chaque galaxie est un sommet numéroté par sa position dans la liste des coordonnées et les coefficients mi j de la matrice d'adjacence sont donnés par la fonction préparée à la question précédente. IV.3 - Algorithme de construction de l'arbre Pour construire l'arbre couvrant de poids minimal de cette assemblée de galaxies, on utilise l'algorithme de Prim, similaire à celui de Dijkstra utilisé pour trouver le chemin le plus court entre deux sommets d'un graphe pondéré. Dans cet algorithme, on tiendra à jour deux dictionnaires utiles : - un dictionnaire dist (pour distance) ayant pour clefs toutes les galaxies qui ne sont pas encore dans l'arbre et pour valeur la distance actuelle de la galaxie à l'arbre (c'est-à-dire le minimum des distances de la galaxie à toutes celles déjà présentes dans l'arbre). C'est le dictionnaire dist qui va permettre de choisir la galaxie à ajouter dans l'arbre (celle qui a la distance minimale). L'algorithme de Prim consiste à réactualiser ce dictionnaire après chaque ajout en regardant parmi les galaxies encore non ajoutées si leur distance à l'arbre a diminué - un dictionnaire pred (pour prédécesseur) ayant pour clefs toutes les galaxies (sauf le point de départ) et pour valeur la galaxie de l'arbre qui est la plus proche de la galaxie servant de clef. Lorsque le dictionnaire dist aura été vidé, le dictionnaire pred servira de description complète à l'arbre couvrant minimal obtenu en donnant pour chaque galaxie sa prédécesseure dans l'arbre. On définit une fonction globale arbre_couvrant_minimal qui prend en argument une matrice d'adjacence G représentative du graphe à traiter et une galaxie depart d'où démarrer. Le but des prochaines questions est d'écrire les fonctions annexes qui apparaîssent dans celle-ci. 12/16 def a r b r e _ c o u v r a n t _ m i n i m a l (G, d e p a r t ) : d i s t = i n i t i a l i s a t i o n _ d i s t a n c e (G, d e p a r t ) pred = { } while len ( d i s t ) != 0 : x = recherche_distance_minimale ( d i s t ) mise_a_jour ( d i s t , pred , x , G) r e t u r n pred Q27. Définir la fonction initialisation_distance(G, depart) qui prend en argument une matrice d'adjacence G représentative du graphe à traiter et une galaxie depart. Elle doit renvoyer un dictionnaire dont les clefs sont les numéros associés à chaque galaxie dans G (compris entre 0 et len(G)-1 inclus) avec pour valeur float('inf') 3 sauf pour la clef depart qui doit avoir une valeur nulle. Q28. Compléter sur le DR la fonction recherche_distance_minimale(dist) qui prend en argument le dictionnaire contenant les distances associées à chaque galaxie. Noter que la fonction modifie le dictionnaire dist sans pour autant le renvoyer, mais, avec un dictionnaire en Python, la modification sera « visible » depuis la fonction principale. La fonction doit renvoyer la galaxie encore présente dans le dictionnaire dont la distance à l'arbre est la plus faible. Q29. Prouver que la fonction arbre_couvrant_minimal introduite par l'énoncé termine en exhibant un variant adéquat (et en justifiant rapidement que ce soit un variant). La fonction mise_a_jour(dist, pred, x, G) prend en argument les dictionnaires dist et pred ainsi qu'une galaxie x et le graphe G sous forme de matrice d'adjacence. Cette fonction modifie les dictionnaires donnés en entrée, mais ne renvoie rien. La modification est la suivante : en parcourant toutes les galaxies présentes dans le graphe, si la galaxie y considérée est encore une clef de dist et que la distance associée est supérieure à la pondération de l'arête xy, alors x devient le prédécesseur de y et la distance de y est mise à la pondération en question. On utilise l'implémentation suivante. def mise_a_jour ( d i s t , pred , x , G) : f o r y i n range ( len (G) ) : i f y in d i s t : i f d i s t [ y ] > G[ x ] [ y ] :
d i s t [ y ] = G[ x ] [ y ]
pred [ y ] = x

Q30. Estimer (en la justifiant) la complexité temporelle globale dans le pire 
des cas de
la fonction arbre_couvrant_minimal introduite par l'énoncé en fonction du nombre
N de galaxies présentes dans le graphe G. On détaillera brièvement chaque
point du raisonnement, notamment les complexités pour chacune des fonctions
initialisation_distance, recherche_distance_minimale et mise_a_jour.
3. float('inf') est une valeur spéciale d'un flottant qui représente l'infini. 
Elle a pour propriété d'être
inférieure ou égale à elle-même mais strictement supérieure à toute autre 
valeur numérique « normale ».

13/16

IV.4 - Élagage de l'arbre couvrant de poids minimal
Par application de la fonction arbre_couvrant_minimal, on a obtenu un graphe 
orienté sans
cycle. On a donc réussi à relier chaque galaxie à celle qui est sa plus proche 
voisine et donc
retrouver le dessin du filament galactique. Néanmoins, de petits radicules 
semblent pousser
le long du filament principal et on aimerait bien pouvoir les élaguer comme 
présenté en
figure 5. Cela revient à supprimer tous les sommets sans successeur dans le 
graphe orienté
généré précédemment jusqu'à obtenir une représentation pertinente pour un 
astronome.

(a)

(b)

Figure 5 - Application de l'algorithme de Prim menant à l'arbre couvrant de 
poids minimal
(figure (a)) du modèle jouet de la figure 4 . La figure (b) indique le même 
arbre après élagage
des branches non nécessaires
On rappelle que l'arbre fourni est un dictionnaire qui, pour chaque galaxie lui 
servant de clef,
contient le prédécesseur de cette galaxie dans l'arbre, c'est-à-dire la galaxie 
à laquelle elle
est rattachée.
Q31. Écrire une fonction dico_galaxies_avec_successeurs(arbre) qui renvoie un 
dictionnaire dont les clefs sont les galaxies x qui ont au moins un successeur 
dans l'arbre
et dont la valeur est le nombre de successeurs, c'est-à-dire le nombre de 
galaxies y
telles que x == arbre[y]. Si une galaxie n'a aucun successeur, elle ne doit pas 
être
entrée comme clef du dictionnaire renvoyé. On privilégiera dans la mesure du 
possible
une fonction en O(n) plutôt qu'en O(n2 ) avec n = len(arbre). Notez que x peut 
être
un prédécesseur de y et ne pas faire partie des clefs du dictionnaire arbre 
(cas de la
racine, la galaxie depart de la question Q27).
On définit à présent la fonction
def elagage ( arbre , nb_etapes ) :
f o r i i n range ( nb_etapes ) :
nb_succ = d i c o _ g a l a x i e s _ a v e c _ s u c c e s s e u r s ( a r b r 
e )
a r br e _ e l a g u e = { }
for gal in arbre :
i f g a l i n nb_succ :
arbre_elague [ gal ] = arbre [ gal ]
arbre = arbre_elague
return arbre

14/16

Q32. Décrire brièvement ce que fait la fonction elagage et l'intérêt de ce que 
cela représente
dans notre cas. On pourra se référer à la figure 5 pour illustrer cette 
description.
IV.5 - Séparation des groupes sans filament
On voit sur la figure 5 que l'élagage n'est pas parfait car il relie l'amas 
isolé en haut à gauche
au reste du filament galactique, alors qu'il n'y a pas de raison physique de le 
faire, l'amas
étant très éloigné du filament. On rappelle que le but de ces manipulations 
(figure 3) est
aussi de séparer visuellement les parties qui n'ont pas de lien physique entre 
elles. Une
technique pour couper l'arbre en plusieurs composantes connexes distinctes 
consiste à se
débarrasser des sommets dont la distance au prédécesseur est supérieure à m où 
m est
la longueur moyenne des liaisons le long de l'arbre complet, le coefficient  
étant choisi
de manière empirique par l'astronome en charge du dossier pour obtenir un 
résultat qui lui
semble satisfaisant (figure 1 où  a été choisi à une valeur de 5).
Q33. Écrire la fonction separation(G, arbre, alpha) qui prend en argument la 
matrice
d'adjacence du graphe G, l'arbre précédemment élagué et le coefficient  
permettant
de comparer à la longueur moyenne m des liaisons de l'arbre. La fonction doit 
renvoyer un dictionnaire de prédecesseur où les sommets pour lesquels la 
distance au
prédécesseur est supérieure à m ont été évincés. Les arguments ne doivent pas
être modifiés.
Pour aller plus loin, on pourra visiter
- le site du SDSS :
https://www.sdss.org/
- les publications basées sur le SDSS :
https://www.sdss.org/science/publications/
- une page pour tester les requêtes SQL :
https://skyserver.sdss.org/dr19/SearchTools/sql
- la galerie d'images des instruments et des résultats dont les dernières 
figures ont été
tirées :
https://www.sdss.org/science/image-gallery/
- l'article ayant servi de base d'inspiration à la partie IV : Bonnaire, Tony, 
et al. "T-ReX : a
graph-based filament detection method." Astronomy & Astrophysics 637 (2020) : 
A18.

Figure 6 - À gauche : télescope du Sloan Digital Sky Survey à l'observatoire 
d'Apache point
au Nouveau Mexique (crédits photo SDSS). À droite : fibres optiques servant à 
récolter les
spectres dans le spectrographe APOGEE du temps où le placement des fibres se 
faisait
encore à la main (crédits photo SDSS)
15/16

Figure 8 - Distribution des galaxies du SDSS dans la tranche équatoriale 
(déclinaison 
comprise entre - 2 et + 2°) où l'on voit une coupe dans la structure 
filamentaire étudiée dans
ce problème. Les zones blanches correspondent aux « angles morts » de 
l'ascension droite
non observables via les télescopes du SDSS. Credits photo M. Blanton et SDSS

FIN
16/16

I M P R I M E R I E N A T I O N A L E ­ 26 1010 ­ D'après documents fournis

Figure 7 - À gauche : image prise par le SDSS de la galaxie M51 dite « galaxie 
du Tourbillon »,
d'un diamètre d'environ 75 000 années lumière, en interaction avec sa voisine 
plus petite sur
la gauche de l'image. Elle occupe environ 3 millionième de la surface totale 
imagée par le
SDSS et fait partie des près de 100 millions de galaxies photographiées par le 
projet souvent
bien plus petites et bien moins brillantes. On en voit quelques unes sous 
formes de taches
diffuses sur la photo. Crédits photo SDSS et Robert Lupton. À droite : un 
empilement de
plus de 46 000 spectres de quasar (un par ligne) répartis selon leur redshift 
(décalage vers
le rouge) issus de la DR3 du SDSS. Les bandes claires correspondent aux raies 
d'émission
spécifiques de l'hydrogène, du carbone, de l'oxygène, du magnésium et du fer. 
Le décalage
progressif vers le rouge (longueurs d'ondes plus grandes) est ce qui permet de 
mesurer le
redshift z. Crédits photo X. Fan et le SDSS

Numéro
d'inscription

Nom : ____________________________________

Numéro
de table

Prénom : _________________________________

Né(e) le

QR Code

Emplacement

Filière :

Session : 2026

PC

Épreuve de :

Consignes

Informatique
· Remplir soigneusement l'en-tête de chaque feuille avant de commencer à 
composer
· Rédiger avec un stylo non effaçable bleu ou noir
· Ne rien écrire dans les marges (gauche et droite)
· Numéroter chaque page (cadre en bas à droite)
· Placer les feuilles A3 ouvertes, dans le même sens et dans l'ordre

PC5IN

DOCUMENT RÉPONSE
Q1 - Nombre de bits pour un pixel. Taille d'une photographie (arrondie au Mo 
près)

Q2 - Espace disque nécessaire pour stocker un spectre.

Q3 - Requête SQL pour compter les spectres.

B

/

1/12

NE RIEN ÉCRIRE DANS CE CADRE

Q4 - Requête SQL pour ordonner les photographies par nombre de spectres 
associés.

Q5 - Requête SQL pour récupérer les bonnes galaxies.

Q6 - En quoi consiste les k-moyennes et que représente k.

Q7 - Fonction distance(S1:[float], S2:[float])  float.

2/12
/

Q8 - Fonction positions_a_rejeter(S1:[float ou None],S2:[float ou None])[int].

Q9 - Fonction est_absent(element:int, L:[int])  bool et sa complexité temporelle
dans le pire des cas.

Q10 - Propriété invariante de boucle pour distance2.

Q11 - Complexités temporelles de distance2 et distance3.

3/12

Q12 - Fonction produit_partition(spectres:[[float]], centroides:[[float]])  
[int].

Q13 - Fonction k_moyennes(spectres:[[float]], k:int)  [[float]], [int].

Q14 - Fonction groupe_de_spectres(spectres:[[float]])  [ [[float]] ].

4/12

Numéro
d'inscription

Nom : ____________________________________

Numéro
de table

Prénom : _________________________________

Né(e) le

QR Code

Emplacement

Filière :

Session : 2026

PC

Épreuve de :

Consignes

Informatique
· Remplir soigneusement l'en-tête de chaque feuille avant de commencer à 
composer
· Rédiger avec un stylo non effaçable bleu ou noir
· Ne rien écrire dans les marges (gauche et droite)
· Numéroter chaque page (cadre en bas à droite)
· Placer les feuilles A3 ouvertes, dans le même sens et dans l'ordre

PC5IN

Q15 - Principe de l'algorithme des k plus proches voisins. Signification de k.

Q16 - Explication du nombre R identique de spectres de référence pour chaque 
type spectral.

Q17 - Valeur de k utilisée dans le script et numéro de ligne associé.

C

/

5/12

NE RIEN ÉCRIRE DANS CE CADRE

Q18 - Explications succinctes du code.
Lignes 1 à 5 :

Lignes 8 à 11 :

Lignes 12 à 18 :

Lignes 19 à 22 :

Lignes 24 à 27 :

Q19 - Signature de la fonction mystere.

6/12
/

Q20 - Deux défauts potentiels du code et lignes associées.

Q21 - Modification de mystere pour rajouter un niveau de confiance.

Q22 - Notion de matrice de confusion et mise en oeuvre pour les spectres de 
référence.

7/12

Q23 - Graphe vierge de droite à remplir en partant du sommet D.
C

C

6

B

1
D

3

B

2

D

1

8

A
7

4

3

A

4
5

E

E

6
5

G

7

F

G
F

Ordre de remplissage des sommets et la valeur du poids total de l'arbre.

Q24 - Graphe vierge de droite à remplir en partant du sommet F.
C

C

6

B

1
D

3

B

2

D

1

8

A
7

4

3

A

4
5

E

E

6
5
F

7

G

G
F

Ordre de remplissage des sommets et commentaire

8/12

Numéro
d'inscription

Nom : ____________________________________

Numéro
de table

Prénom : _________________________________

Né(e) le

QR Code

Emplacement

Filière :

Session : 2026

PC

Épreuve de :

Consignes

Informatique
· Remplir soigneusement l'en-tête de chaque feuille avant de commencer à 
composer
· Rédiger avec un stylo non effaçable bleu ou noir
· Ne rien écrire dans les marges (gauche et droite)
· Numéroter chaque page (cadre en bas à droite)
· Placer les feuilles A3 ouvertes, dans le même sens et dans l'ordre

PC5IN

Q25 - Fonction poids(coords:[(float)], i:int, j:int)  float.

Q26 - Fonction matrice_adjacence(coords:[(float)])  [[float]].

D

/

9/12

NE RIEN ÉCRIRE DANS CE CADRE

Q27 - Fonction initialisation_distance(G:[[float]], depart:int)  dict.

Q28 - Fonction recherche_distance_minimale(dist:dict)  int à compléter.
def r e c h e r c h e _ d i s t a n c e _ m i n i m a l e ( d i s t ) :
mindist = float ( ' i n f ' )
candidat =

None

for gal in d i s t :
if

........................ :
mindist =

............

candidat =

...........

del d i s t [ c a n d i d a t ] # Suppression de l a c l e f c a n d i d a t e 
du d i c t i o n n a i r e
return candidat

# e t r e n v o i du c a n d i d a t

Q29 - Preuve de la terminaison arbre_couvrant_minimal.

10/12
/

Q30 - Complexité temporelle dans le pire des cas de arbre_couvrant_minimal.

Q31 - Fonction dico_galaxies_avec_successeurs(arbre:dict)  dict.

11/12

Q32 - Description et intérêt de la fonction elagage.

Q33 - Fonction separation(G:[[float]], arbre:dict, alpha:float)  dict.

FIN

12/12