Tableaux
========
.. include:: common.rst.inc
Comparaisons et tests de tableaux
+++++++++++++++++++++++++++++++++
#. .. _tableaux_egaux:
|exo| Déterminer si deux tableaux sont identiques ::
def tableaux_egaux(a: [int], b: [int]) -> bool:
"""
:entrée a: tableau d'int
:entrée b: tableau d'int
:sortie te: bool
:post-cond: te est True si et seulement si len(a) == len(b) et
∀ i ∈ [0;len(a)[, a[i] == b[i]
>>> tableaux_egaux([1, 2, 4, 3], [1, 2, 4, 3])
True
>>> tableaux_egaux([1, 2, 4, 3], [9, 2, 4, 3])
False
>>> tableaux_egaux([1, 2, 4, 3], [1, 2, 4])
False
>>> tableaux_egaux([1, 2, 4], [1, 2, 4, 3])
False
"""
.. raw:: html
#. .. _tableau_prefixe:
|exo| Déterminer si le tableau b est un préfixe du tableau a ::
def tableau_prefixe(a: [int], b: [int]) -> bool:
"""
:entrée a: tableau d'int
:entrée b: tableau d'int
:sortie te: bool
:post-cond: te est True si et seulement si len(a) >= len(b) et
∀ i ∈ [0;len(b)[, a[i] == b[i]
>>> tableaux_prefixe([1, 2, 4, 3], [1, 2, 4, 3])
True
>>> tableaux_prefixe([1, 2, 4, 3], [9, 2, 4, 3])
False
>>> tableaux_prefixe([1, 2, 4, 3], [1, 2, 4])
True
>>> tableaux_prefixe([1, 2, 4], [1, 2, 4, 3])
False
"""
.. raw:: html
#. .. _tableau_palindrome:
|exo| Retourner True si et seulement si le tableau a est un Palindrome ::
def tableau_palindrome(a: [int]) -> bool:
"""
:entrée a: tableau d'int
:sortie p: bool
:post-cond: p est True si et seulement si
∀ i ∈ [0;len(a)[, a[i] == a[len(a)-1-i]
>>> tableau_palindrome([1, 2, 4, 3])
False
>>> tableau_palindrome([1, 2, 4, 2, 1])
True
>>> tableau_palindrome([1, 2, 2, 1])
True
>>> tableau_palindrome([4])
True
"""
.. raw:: html
Recherche de valeurs
++++++++++++++++++++
#. .. _indice_min:
|exo| Trouver l'indice du plus petit élément d'un tableau ::
def indice_min(a: [float]) -> int:
"""
:entrée a: tableau de float
:pré-cond: len(a) > 0
:sortie imin: int
:post-cond: ∀ i ∈ [0;len(a)[, a[imin] ≤ a[i]
>>> indice_min([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0])
2
>>> indice_min([2.0, 1.0, -3.0, 7.0, -3.0, 5.0, 1.0])
2
>>> # mais 4 est aussi une réponse correcte
"""
NB : si le tableau contient plusieurs minima ex-æquo,
cette spécification n'impose pas lequel doit être retourné.
#. .. _indice_min_gauche:
|exo| Trouver l'indice le plus à gauche du plus petit élément d'un tableau ::
def indice_min_gauche(a: [float]) -> int:
"""
:entrée a: tableau de float
:pré-cond: len(a) > 0
:sortie imin: int
:post-cond: ∀ i ∈ [0;imin[, a[imin] < a[i],
∀ i ∈ ]imin;len(a)[, a[imin] ≤ a[i],
>>> indice_min_gauche([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0])
2
>>> indice_min_gauche([2.0, 1.0, -3.0, 7.0, -3.0, 5.0, 1.0])
2
"""
NB : La différence avec indice_min_ ci-dessus est que,
si le tableau contient plusieurs minima ex-aequo,
cette spécification impose de retourner l'indice du plus à gauche.
#. .. _indice_min_droite:
|exo| Trouver l'indice le plus à droite du plus petit élément d'un tableau ::
def indice_min_droite(a: [float]) -> int:
"""
:entrée a: tableau de float
:pré-cond: len(a) > 0
:sortie imin: int
:post-cond: ∀ i ∈ [0;imin[, a[imin] ≤ a[i],
∀ i ∈ ]imin;len(a)[, a[imin] < a[i],
>>> indice_min_droite([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0])
2
>>> indice_min_droite([2.0, 1.0, -3.0, 7.0, -3.0, 5.0, 1.0])
4
"""
#. .. _indice_max:
|exo| Trouver l'indice du plus grand élément d'un tableau ::
def indice_max(a: [float]) -> int:
"""
:entrée a: tableau de float
:sortie imax: int
:pré-cond: len(a) > 0
:post-cond: ∀ i ∈ [0;len(a)[, a[imax] ≤ a[i]
>>> indice_max([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0])
3
>>> indice_max([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
3
>>> # mais 5 est aussi une réponse correct
"""
NB : si le tableau contient plusieurs maxima ex-aequo,
cette spécification n'impose pas lequel doit être retourné.
#. .. _indice_gauche:
|exo| Trouver l'indice le plus à gauche de la valeur v dans un tableau ::
def indice_gauche(a: [float], v: float) -> int:
"""
:entrée a: tableau de float
:entrée v: float
:sortie ival: int
:post-cond: si v ∈ a, a[ival] = v et ∀ i ∈ [0;ival[, a[i] ≠ v
sinon ival = -1
>>> indice_gauche([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0], 2.0)
0
>>> indice_gauche([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0], 7.0)
3
>>> indice_gauche([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0)
3
>>> indice_gauche([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0], 9.0)
-1
"""
#. .. _indice_droite:
|exo| Trouver l'indice le plus à droite de la valeur v dans un tableau ::
def indice_droite(a: [float], v: float) -> int:
"""
:entrée a: tableau de float
:entrée v: float
:sortie ival: int
:post-cond: si v ∈ a, a[ival] = v et ∀ i ∈ ]ival, len(a)[, a[i] ≠ v
sinon ival = -1
>>> indice_droite([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0], 2.0)
0
>>> indice_droite([2.0, 1.0, -3.0, 7.0, 3.0, 5.0, 1.0], 7.0)
3
>>> indice_droite([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0)
5
>>> indice_droite([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 9.0)
-1
"""
#. .. _indice_n:
|exo| Trouver l'indice de la n-ème occurrence de v dans un tableau ::
def indice_n(a: [float], v: float, n: int) -> int:
"""
:entrée a: tableau de float
:entrée v: float
:entrée n: int
:sortie ival: int
:pré-cond: n > 0
:post-cond: si v apparaît au moins n fois dans a,
alors a[ival] = v,
et a[0:ival] contient v exactement n-1 fois,
sinon ival = -1
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 2.0, 1)
0
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 2.0, 2)
-1
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0, 1)
3
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0, 2)
5
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0, 3)
-1
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 9.0, 1)
-1
>>> indice_n([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 9.0, 2)
-1
"""
#. .. _nb_occurences:
|exo| Compter le nombre d'occurrences de v dans un tableau::
def nb_occurences(a: [float], v: float) -> int:
"""
:entrée a: tableau de float
:entrée v: float
:sortie n: int
:post-conf: n = |{ i | i ∈ [0;len(a)[ et a[i] = n }|
>>> nb_occurences([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 2.0)
1
>>> nb_occurences([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 7.0)
2
>>> nb_occurences([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0], 9.0)
0
"""
Calculs sur les valeurs
+++++++++++++++++++++++
#. .. _somme:
|exo| Calculer la somme des éléments d'un tableau ::
def somme(a: [float]) -> float:
"""
:entrée a: tableau de float
:pré-cond: len(a) > 0
:sortie s: float
:post-cond: s = Σ a[i] pour i ∈ [0;len(a)[
>>> somme([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
18.0
"""
#. .. _moyenne:
|exo| Calculer la moyenne des éléments d'un tableau ::
def moyenne(a: [float]) -> float:
"""
:entrée a: tableau de float
:pré-cond: len(a) > 0
:sortie m: float
:post-cond: s est la moyenne des éléments de a
>>> moyenne([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
2.5714285714285716
"""
#. .. _moyenne_ponderee:
|exo| ⭑ Calculer la moyenne pondérée des éléments d'un tableau ::
def moyenne_ponderee(a: [float], poids: [float]) -> float:
"""
:entrée a: tableau de float
:entrée poids: tableau de float
:pré-cond: len(a) > 0, len(poids) == len(a), somme(poids) != 0
:sortie mp: float
:post-cond: s est la moyenne pondérée des éléments de a telle que
∀ i ∈ [0;len(a)[, a[i] est pondérée par poids[i]
>>> moyenne_ponderee([10.0, 12.0, 7.0], [1.0, 2.0, 1.0])
10.25
"""
Génération et modification de tableaux
++++++++++++++++++++++++++++++++++++++
#. .. _premiers:
|exo| ⭑ Retourner un tableau contenant les `n` premiers nombres premier ::
def premiers(n: int) -> [int]:
"""
:entrée n: int
:pré-cond: n > 0
:sortie a: tableau d'int
:post-cond: len(a) == n
∀ i ∈ [0;n[, a[i] est le (i+1)-ème nombre premier
>>> premiers(3)
array([2, 3, 5])
>>> premiers(10)
array([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])
"""
On rappelle qu'un nombre premier admet exactement deux diviseurs :
1 et lui même.
Par exemple, premiers(10) retournera le tableau
``[2,3,5,7,11,13,17,19,23,29]``\ [#tip_premiers]_.
#. .. _fibonacci:
|exo| ⭑ Retourner un tableau contenant les `n` premiers termes de la suite de
Fibonacci ::
def fibonacci(n: int) -> [int]:
"""
:entrée n: int
:pré-cond: n > 0
:sortie a: tableau d'int
:post-cond: len(a) == n
∀ i ∈ [0;n[, a[i] = Fi
>>> fibonacci(4)
array([1, 1, 2, 3])
>>> fibonacci(10)
array([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
"""
On rappelle que la suite de Fibonnaci\ [#tip_fibonacci]_ est définie par :
.. math::
& F_0 = 1 \\
& F_1 = 1 \\
& \forall n > 1, ~~ F_n = F_{n-1} + F_{n-2}
#. .. _eratosthene:
|exo| ⭑ Retourner un tableau de `n` booléen indiquant,
pour chaque nombre entre 0 et n-1, s'il est premier ou non ::
def eratosthene(n: int) -> [bool]:
"""
:entrée n: int
:pré-cond: n > 0
:sortie a: tableau de bool
:post-cond: len(a) == n
∀ i ∈ [0;n[, a[i] est True si et seulement si i est premier
>>> eratosthene(4)
array([False, False, True, True])
>>> eratosthene(10)
array([False, False, True, True, False, True, False, True, False, False])
"""
La méthode proposée (le crible d'Ératosthène) consiste à :
* initialiser tous les éléments du tableau à True (à part 0 et 1),
* pour chaque élément à partir de 2, s'il vaut True,
mettre tous ses multiples à False.
À la fin de ce processus, les nombres premiers et uniquement eux seront à True.
#. .. _tableau_inverse:
|exo| Retournerr le tableau inverse de a ::
def tableau_inverse(a: [float]) -> [float]:
"""
:entrée a: tableau de float
:sortie b: tableau de float
:post-cond: ∀ i ∈ [0;len(a)[, b[i] == a[len(a)-1-i]
>>> tableau_inverse([])
array([])
>>> tableau_inverse([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
array([1.0, 7.0, 3.0, 7.0, -3.0, 1.0, 2.0])
"""
#. .. _moyennes_par_groupe:
|exo| ⭑ Calculer les moyennes par groupe pour un tableau contenant
toutes les notes d'une promotion d'étudiants ::
def moyennes_par_groupe(notes: [float], nbEtu: [int]) -> [float]:
"""
:entrée notes: tableau de float
:entrée nbEtu: tableau d'int
:pré-cond:
- la somme des éléments de nbEtu est égale à len(notes)
- notes[0 : nbEtu[0]] contient les notes du premier groupe ;
notes[nbEtu[0] : nbEtu[0] + nbEtu[1]] contient les notes du
deuxième groupe, et ainsi de suite...
:sortie moy: tableau de float
:post-cond:
- len(moy) = len(nbEtu)
- ∀ i ∈ [0;len(moy)[, moy[i] contient la moyenne des étudiants
du (i+1)ème groupe
>>> moyennes_par_groupes([11.0, 9.0, 10.0, 15.0, 16.0, 2.0, 3.0, 4.0],
[3, 2, 3])
array([10.0, 15.5, 3.0])
"""
#. .. _moyennes_par_groupe2:
|exo| ⭑⭑ Calculer les moyennes par groupe pour un tableau contenant
toutes les notes d'une promotion d'étudiants ::
def moyennes_par_groupe2(notes: [float], groupe: [int], nbGroupe, int) -> [float]:
"""
:entrée notes: tableau de float
:entrée groupe: tableau d'int
:entrée nbGroupes: int
:pré-cond:
- len(notes) = len(groupe)
- ∀ i ∈ [0;len(notes)[,
notes[i] est la note du (i+1)ème étudiant, et
groupe[i] est le numéro de groupe (entre 0 et nbGroupes-1)
de cet étudiant
:sortie moy: tableau de float
:post-cond:
- len(moy) = nbGroupes
- ∀ i ∈ [0;len(moy)[, moy[i] contient la moyenne des étudiants
du groupe n° i
>>> moyennes_par_groupes2([11.0, 15.0, 9.0, 3.0, 10.0, 16.0, 2.0, 4.0],
[ 0, 1, 0, 2, 0, 1, 2, 2])
array([10.0, 15.5, 3.0])
"""
#. .. _mots_ab:
|exo| ⭑⭑ Générer toutes les chaînes de caractères de longueur `n`
composées des caractères ``'a'`` et ``'b'``\ ::
def mots_ab(n: int) -> [str]:
"""
:entrée n: int
:pré-cond: n ≥ 0
:sortie mots: tableau de str
:post-cond: mots contient toutes les chaînes de caractères de
longueur n composées des caractères 'a' et 'b'.
>>> # l'ordre peut être différent
>>> mots_ab(0)
array([""])
>>> mots_ab(1)
array(["a", "b"])
>>> mots_ab(2)
array(["aa", "ab", "ba", "bb"])
>>> mots_ab(3)
array(["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"])
>>> mots_ab(4)
array(["aaaa", "aaab", "aaba", "aabb", "abaa", "abab", "abba", "abbb",
"baaa", "baab", "baba", "babb", "bbaa", "bbab", "bbba", "bbbb",
])
"""
NB: pour n = 0, la réponse est constituée de l'unique chaîne ``""``
(qui a bien pour longueur 0, et qui ne contient aucun autre caractère
que ``'a'`` ou ``'b'``).
Bien que ce ne soit pas une obligation,
cet algorithme est plus simple à écrire sous forme récursive qu'itérative.
#. .. _mots:
|exo| ⭑⭑ Générer toutes les chaînes de caractères de longueur `n`
composées d'un ensemble de caractères donnés ::
def mots(chars: str, n: int) -> [str]:
"""
:entrée chars: str
:entrée n: int
:sortie mots: tableau de str
:pré-cond: n ≥ 0, len(chars) > 0,
tous les caractères de chars sont différents
:post-cond: mots contient toutes les chaînes de caractères de
longueur n composées des caractères de chars.
>>> mots("abc", 0)
array([""])
>>> mots("abc", 1)
array(["a", "b", "c"])
>>> mots("abc", 2)
array(["aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"])
>>> mots("XyZ", 2)
array(["XX", "Xy", "XZ", "yX", "yy", "yZ", "ZX", "Zy", "ZZ"])
"""
On pourra s'inspirer de la solution de mots_ab_,
dont cet algorithme est une généralisation.
Modification de tableau
+++++++++++++++++++++++
#. .. _inverse_tableau:
|exo| Modifier le tableau a de sorte à inverser l'ordre de ses éléments ::
def inverse_tableau(a: [float]):
"""
:e/s a: tableau de float
:post-cond: ∀ i ∈ [0;len(a)[, aₑ[i] == aₛ[len(a)-1-i]
>>> a = array([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
>>> inverse_tableau(a)
>>> a
array([1.0, 7.0, 3.0, 7.0, -3.0, 1.0, 2.0])
"""
.. raw:: html
#. .. _insere_val:
|exo| Insèrer une valeur dans un tableau,
en décalant les autres valeurs vers la droite ::
def insere_val(a: [float], v: float, i: int):
"""
:e/s a: tableau de float
:entrée v: float
:entrée i: int
:pré-cond: 0 ≤ i < len(a)
:post-cond: ∀ j ∈ [0;len(a)[,
aₛ[j] == aₑ[j] si j < i
aₛ[j] == v si j = i
aₛ[j] == aₑ[j-1] si j > i
>>> a = array([2.0, 1.0, -3.0, 7.0, 3.0, 7.0, 1.0])
>>> insere_val(a, 9.0, 2)
>>> a
array([1.0, 7.0, 9.0, 3.0, 7.0, -3.0, 1.0])
"""
NB: le tableau ne change pas de taille, donc
cette procédure perd la valeur initialement la plus à droite du tableau.
Tableaux triés
++++++++++++++
#. .. _indice_val:
|exo| ⭑ Trouver l'indice de la valeur v dans un tableau trié ::
def indice_gauche(a: [float], v: float) -> int:
"""
:entrée a: tableau de float
:entrée v: float
:pré-cond: a est trié: ∀ i ∈ [1;len(a)[, a[i] ≥ a[i-1]
:sortie ival: int
:post-cond: si v ∈ a, a[ival] = v
sinon ival = -1
"""
Si `v` a plusieurs occurences dans le tableau,
on ne spécifie pas l'indice de laquelle doit être retourné.
En revanche, il faut tirer partie du fait que le tableau est trié
pour trouver *rapidement* le résultat\ [#tip_indice_val]_.
#. .. _insere_val_trie:
|exo| ⭑ Insèrer une valeur à sa place dans un tableau trié,
en décalant les autres valeurs vers la droite ::
def insere_val(a: [float], v: float) -> int:
"""
:e/s a: tableau de float
:entrée v: float
:pré-cond: a est trié: ∀ i ∈ [1;len(a)[, a[i] ≥ a[i-1]
:sortie ival: int
:post-cond: a est toujours trié ;
∀ i ∈ [0;len(a)[,
aₛ[i] == aₑ[i] si i < ival
aₛ[i] == v si i = ival
aₛ[i] == aₑ[i-1] si i > ival
"""
NB: le tableau ne change pas de taille, donc
cette procédure perd la valeur initialement la plus à droite du tableau.
#. .. _suppression_doublons:
|exo| Supprimer tous les doublons d'un tableau trié ::
def suppression_doublons(a: [float]) -> int:
"""
:e/s a: tableau de float
:sortie newlen: int
:pré-cond: a est trié: ∀ i ∈ [1;len(a)[, a[i] ≥ a[i-1]
:post-cond:
- 0 ≤ newlen ≤ len(a)
- ∀ i ∈ [0;len(a)[, ∃ j ∈ [0;newlen[, aₛ[j] = aₑ[i]
- ∀ i ∈ [1;newlen[, aₛ[i-1] < aₛ[i]
"""
NB: le tableau ne change pas de taille,
mais `newlen` est la nouvelle longueur "utile" du tableau,
c'est à dire l'indice à partir duquel les valeurs dans `aₛ`
ne sont plus pertinentes.
#. .. _interclassement:
|exo| Copier les valeurs de deux tableaux triés dans un troisième tableau
de sorte à ce que ce dernier soit trié lui aussi ::
def interclassement(a1: [float], a2: [float]) -> [float]:
"""
:entrée a1: tableau de float
:entrée a2: tableau de float
:pré-cond: a1 est trié: ∀ i ∈ [1;len(a1)[, a1[i] ≥ a1[i-1]
a2 est trié: ∀ i ∈ [1;len(a2)[, a2[i] ≥ a2[i-1]
:sortie a3: tableau de float
:post-cond:
- len(a3) = len(a1) + len(a2)
- a3 est trié: ∀ i ∈ [1;len(a3)[, a3[i] ≥ a3[i-1]
- a3 contient toutes les valeurs de a1 et a2
"""
.. only:: html
.. rubric:: Indices
.. [#tip_premiers] Bien que cela soit possible,
il n'est pas conseillé de réutiliser la fonction `premier `:ref:,
car il existe une solution plus efficace qui ne l'utilise pas.
.. [#tip_fibonacci] Bien que cela soit possible,
il n'est pas conseillé de réutiliser la fonction `fibo `:ref:,
car il existe une solution plus efficace qui ne l'utilise pas.
.. [#tip_indice_val] Pour cela on effectuera une recherche dichotomique :
à chaque étape, on regarde au milieu de la plage d'indice considérée,
et selon que la valeur trouvée est plus grande ou plus petite que `v`,
on n'en considère plus que la moitié gauche ou la moitié droite.