Des automates cellulaires☘
Dans cet exercice, on travaille sur les listes de listes.
L'un des objectifs sera d'obtenir des images comme celle-ci (au dernier paragraphe):
Fonction cree_matrice☘
Écrire un code python possible pour le corps de la fonction suivante:
def cree_matrice(n):
"""
n: entier >= 1
renvoie une matrice carrée (2n+1)*(2n+1) avec un 1 au centre
et des 0 partout ailleurs.
"""
Solution
Un code possible:
def cree_matrice(n):
"""
n: entier >= 1
renvoie une matrice carrée (2n+1)*(2n+1) avec un 1 au centre
et des 0 partout ailleurs.
"""
mat = [[0 for col in range(2*n+1)] for lig in range(2*n+1)]
mat[n][n] = 1
return mat
Un code☘
Fonction dec_to_bin☘
Écrire un code python possible pour le corps de la fonction suivante:
def dec_to_bin(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1
correspondant à l'écriture binaire de entier.
"""
Solution
Il s'agit d'un code que vous devez connaître par coeur (divisions en cascade):
def dec_to_bin(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1
correspondant à l'écriture binaire de entier.
"""
if entier == 0: return '0'
chaine_bin = ''
while entier != 0:
chaine_bin = str(entier%2) + chaine_bin
entier = entier//2
return chaine_bin
Fonction bin_to_code☘
Écrire un code python possible pour le corps de la fonction suivante:
def bin_to_code(chaine_bin):
"""
chaine_bin: chaîne de 0 et 1 de longueur <= 10
renvoie la chaîne avec d'éventuels 0 à gauche pour que cette chaîne
ait une longueur égale à 10.
>>> bin_to_code('1101')
'0000001101'
"""
Solution
Un code possible:
def bin_to_code(chaine_bin):
"""
chaine_bin: chaîne de 0 et 1 de longueur <= 10
renvoie la chaîne avec d'éventuels 0 à gauche pour que cette chaîne
ait une longueur égale à 10.
>>> bin_to_code('1101')
'0000001101'
"""
lg = len(chaine_bin)
while lg != 10:
chaine_bin = '0' + chaine_bin
lg += 1
return chaine_bin
Fonction dec_to_code☘
Écrire un code python possible pour le corps de la fonction suivante:
def dec_to_code(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1 de longueur 10
correspondant à l'écriture binaire de entier
(avec d'éventuels 0 à gauche pour obtenir la longueur 10)
"""
Solution
Un code possible:
def dec_to_code(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1 de longueur 10
correspondant à l'écriture binaire de entier
(avec d'éventuels 0 à gauche pour obtenir la longueur 10)
"""
return bin_to_code(dec_to_bin(entier))
Des voisins☘
Écrire un code python possible pour le corps de la fonction suivante:
def nb_voisins1(matrice, xcellule, ycellule):
"""
matrice: matrice carrée contenant uniquement des 0 et des 1
xcellule, ycellule: coordonnées d'une cellule de la matrice
renvoie le nombre de voisins de cellule qui contiennent 1
(parmi les 4 voisins ayant un "côté" commun dans une représentation classique de la matrice
sous forme d'un tableau)
"""
On ne traitera pas les cellules du bord (colonne 0, dernière colonne, ligne 0, dernière ligne).
Solution
Un code possible:
def nb_voisins1(matrice, xcellule, ycellule):
"""
matrice: matrice carrée contenant uniquement des 0 et des 1
xcellule, ycellule: coordonnées d'une cellule de la matrice
renvoie le nombre de voisins de cellule qui contiennent 1
(parmi les 4 voisins ayant un "côté" commun dans une représentation classique de la matrice
sous forme d'un tableau)
"""
compteur = 0
if matrice[xcellule-1][ycellule] == 1: compteur += 1
if matrice[xcellule][ycellule-1] == 1: compteur += 1
if matrice[xcellule][ycellule+1] == 1: compteur += 1
if matrice[xcellule+1][ycellule] == 1: compteur += 1
return compteur
Lecture du code☘
On dispose d'une matrice carrée (en python, on utilisera une liste de listes), chaque cellule contenant 0 ou 1 (cf la fonction
cree_matrice
précédente).
On veut transformer chaque cellule en 0 ou 1 suivant ce que contient la cellule et le nombre de cellules égales à 1 parmi les voisines.
On va utiliser les écritures binaires de longueur 10 des questions précédentes comme des règles de transformation des 0 et des 1 de chaque cellule.
La fonction suivante traduit le principe du codage utilisé pour nos règles de transformations:
def transfo_cell(matrice, xcellule, ycellule, regle):
"""
regle: entier entre 0 et 1023
matrice: matrice carrée
xcellule, ycellule: coordonnées d'une cellule de la matrice
renvoie 0 ou 1 suivant la règle regle.
"""
nb1 = nb_voisins1(matrice, xcellule, ycellule)
regle = dec_to_code(regle)
if matrice[xcellule][ycellule] == 0:
return int(regle[2 * nb1])
else:
return int(regle[2 * nb1 + 1])
Remarque: on ne traite pas les cellules du bord (colonne 0, dernière colonne, ligne 0, dernière ligne).
Question 1☘
Si la cellule de coordonnées (xcellule, ycellule) a trois voisins marqués 1, comment sera-t-elle modifiée
-
si elle contient un 0 (par exemple la cellule centrale de cette configuration: )
-
si elle contient un 1 (par exemple la cellule centrale de cette configuration: )
avec la règle regle = 421
.
Solution
Après l'instruction nb1 = nb_voisins1(matrice, xcellule, ycellule)
, nb1 désigne la valeur 3.
Après l'instruction regle = dec_to_code(regle)
, regle désigne la chaîne "0110100101".
Si la cellule contient un 0, la fonction renvoie int(regle[2 * nb1])
, c'est à dire ici int(regle[6])
qui a pour valeur 0.
La cellule reste donc 0.
Si la cellule contient un 1, la fonction renvoie int(regle[2 * nb1 + 1])
, c'est à dire ici int(regle[7])
qui a pour valeur 1.
La cellule reste donc 1.
Question 2☘
Si la cellule de coordonnées (xcellule, ycellule) a 1 voisin marqué 1, comment sera-t-elle modifiée
-
si elle contient un 0 (par exemple la cellule centrale de cette configuration: )
-
si elle contient un 1 (par exemple la cellule centrale de cette configuration: )
avec la règle regle = 918
.
Solution
Après l'instruction nb1 = nb_voisins1(matrice, xcellule, ycellule)
, nb1 désigne la valeur 1.
Après l'instruction regle = dec_to_code(regle)
, regle désigne la chaîne "1110010110".
Si la cellule contient un 0, la fonction renvoie int(regle[2 * nb1])
, c'est à dire ici int(regle[2])
qui a pour valeur 1.
La cellule devient 1.
Si la cellule contient un 1, la fonction renvoie int(regle[2 * nb1 + 1])
, c'est à dire ici int(regle[3])
qui a pour valeur 0.
La cellule devient donc 0.
Transformation de la matrice☘
Une étape☘
Écrire un code python possible pour le corps de la fonction suivante:
def etape(regle, matrice):
"""
regle: entier entre 0 et 1023.
matrice: matrice carrée ne contenant que des 0 et des 1.
renvoie la matrice dans laquelle chaque cellule a été modifiée suivant la
règle regle.
Attention: les valeurs des voisins à prendre en compte pour les transformations
sur les valeurs initiales de la matrice, tout doit se passer comme si toutes les
transformations étaient faites en même temps: en d'autres termes une cellule
ne doit pas tenir compte de la nouvelle valeur d'une de ses voisines déjà transformée
mais de la valeur de départ dans la donnée matrice.
"""
Remarque: on ne transformera pas les cellules du bord (colonne 0, dernière colonne, ligne 0, dernière ligne).
Solution
On a besoin de garder en mémoire les valeurs de départ de la matrice
pour ne pas effectuer de transformation basée sur des cellules voisines déjà transformées.
Pour cela, attention, on a besoin de faire une copie profonde.
def etape(regle, matrice):
"""
regle: entier entre 0 et 1023.
matrice: matrice carrée ne contenant que des 0 et des 1.
renvoie la matrice dans laquelle chaque cellule a été modifiée suivant la
règle regle.
Attention: les valeurs des voisins à prendre en compte pour les transformations
sur les valeurs initiales de la matrice, tout doit se passer comme si toutes les
transformations étaient faites en même temps: en d'autres termes une cellule
ne doit pas tenir compte de la nouvelle valeur d'une de ses voisines déjà transformée
mais de la valeur de départ dans la donnée matrice.
"""
nv_matrice = deepcopy(matrice)
d = len(matrice)
for lig in range(1, d-1):
for col in range(1, d-1):
nv_matrice[lig][col] = transfo_cell(matrice, col, lig, regle)
return nv_matrice
n étapes☘
Quel est alors le rôle de la fonction python suivante ?
def repet(n, regle):
matrice = cree_matrice(n)
for k in range(n):
matrice = etape(regle, matrice)
return matrice
Solution
Calculer la matrice après n étapes de transformation complète.
Utilisation pour créer une image☘
On interprète les 0 et les 1 de la matrice comme des indications de couleur de pixels d'une image carrée, par exemple avec le code suivant:
from PIL import Image
def cree_image(n, regle, couleur):
# création de la matrice et transformations :
matrice = repet(n, regle)
# dimension de la matrice carrée crée :
d = 2*n+1
# création d'une image de largeur d pixels et hauteur d pixels :
imageBut = Image.new('RGB', (d, d))
# on donne une couleur à chaque pixel :
for y in range(d) :
for x in range(d) :
if matrice[y][x] == 0:
imageBut.putpixel((x,y), (255, 255, 255)) # (255,255,255) = blanc
else:
imageBut.putpixel((x,y), couleur) # couleur est un triplet (r,g,b)
# sauvegarde de l'image créée :
imageBut.save(f'repet{n}_regle{regle}.png')
# on lance une visualisation :
#imageBut.show() # activer cette ligne pour ouvrir automatiquement l'image
Avec la règle 421, on obtient par exemple:
-
Avec 22 répétitions:
-
Avec 60 répétitions:
-
Avec 90 répétitions:
Faîtes d'autres tests !
Le code complet
from copy import deepcopy
from PIL import Image
def cree_matrice(n):
"""
n: entier >= 1
renvoie une matrice carrée (2n+1)*(2n+1) avec un 1 au centre
et des 0 partout ailleurs.
"""
mat = [[0 for col in range(2*n+1)] for lig in range(2*n+1)]
mat[n][n] = 1
return mat
def dec_to_bin(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1
correspondant à l'écriture binaire de entier.
"""
if entier == 0: return '0'
chaine_bin = ''
while entier != 0:
chaine_bin = str(entier%2) + chaine_bin
entier = entier//2
return chaine_bin
def bin_to_code(chaine_bin):
"""
chaine_bin: chaîne de 0 et 1 de longueur <= 10
renvoie la chaîne avec d'éventuels 0 à gauche pour que cette chaîne
ait une longueur égale à 10.
>>> bin_to_code('1101')
'0000001101'
"""
lg = len(chaine_bin)
while lg != 10:
chaine_bin = '0' + chaine_bin
lg += 1
return chaine_bin
def dec_to_code(entier):
"""
entier: entier naturel entre 0 et 1023
renvoie une chaîne de 0 et 1 de longueur 10
correspondant à l'écriture binaire de entier
(avec d'éventuels 0 à gauche pour obtenir la longueur 10)
"""
return bin_to_code(dec_to_bin(entier))
def nb_voisins1(matrice, xcellule, ycellule):
"""
matrice: matrice carrée contenant uniquement des 0 et des 1
xcellule, ycellule: coordonnées d'une cellule de la matrice
renvoie le nombre de voisins de cellule qui contiennent 1
(parmi les 4 voisins ayant un "côté" commun dans une représentation classique de la matrice
sous forme d'un tableau)
"""
compteur = 0
if matrice[xcellule-1][ycellule] == 1: compteur += 1
if matrice[xcellule][ycellule-1] == 1: compteur += 1
if matrice[xcellule][ycellule+1] == 1: compteur += 1
if matrice[xcellule+1][ycellule] == 1: compteur += 1
return compteur
def transfo_cell(matrice, xcellule, ycellule, regle):
"""
regle: entier entre 0 et 1023
matrice: matrice carrée
xcellule, ycellule: coordonnées d'une cellule de la matrice
renvoie 0 ou 1 suivant la règle regle.
"""
nb1 = nb_voisins1(matrice, xcellule, ycellule)
regle = dec_to_code(regle)
if matrice[xcellule][ycellule] == 0:
return int(regle[2 * nb1])
else:
return int(regle[2 * nb1 + 1])
def etape(regle, matrice):
"""
regle: entier entre 0 et 1023.
matrice: matrice carrée ne contenant que des 0 et des 1.
renvoie la matrice dans laquelle chaque cellule a été modifiée suivant la
règle regle.
Attention: les valeurs des voisins à prendre en compte pour les transformations
sur les valeurs initiales de la matrice, tout doit se passer comme si toutes les
transformations étaient faites en même temps: en d'autres termes une cellule
ne doit pas tenir compte de la nouvelle valeur d'une de ses voisines déjà transformée
mais de la valeur de départ dans la donnée matrice.
"""
nv_matrice = deepcopy(matrice)
d = len(matrice)
for lig in range(1, d-1):
for col in range(1, d-1):
nv_matrice[lig][col] = transfo_cell(matrice, col, lig, regle)
return nv_matrice
def repet(n, regle):
matrice = cree_matrice(n)
for k in range(n):
matrice = etape(regle, matrice)
return matrice
def cree_image(n, regle, couleur):
matrice = repet(n, regle)
d = 2*n+1
imageBut = Image.new('RGB', (d, d))
for y in range(d) :
for x in range(d) :
if matrice[y][x] == 0:
imageBut.putpixel((x,y), (255, 255, 255))
else:
imageBut.putpixel((x,y), couleur)
# sauvegarde de l'image créée :
imageBut.save(f'repet{n}_regle{regle}.png')
# on lance une visualisation :
#imageBut.show()
411
>>> cree_image(22, 411, (255, 215, 0))
>>> cree_image(60, 411, (255, 0, 0))
>>> cree_image(90, 411, (0, 0, 255))
409
>>> cree_image(22, 409, (255, 215, 0))
>>> cree_image(60, 409, (255, 0, 0))
>>> cree_image(90, 409, (0, 0, 255))
442
>>> cree_image(22, 442, (255, 215, 0))
>>> cree_image(60, 442, (255, 0, 0))
>>> cree_image(90, 442, (0, 0, 255))
426
>>> cree_image(22, 426, (255, 215, 0))
>>> cree_image(60, 426, (255, 0, 0))
>>> cree_image(90, 426, (0, 0, 255))
418
>>> cree_image(22, 418, (255, 215, 0))
>>> cree_image(60, 418, (255, 0, 0))
>>> cree_image(90, 418, (0, 0, 255))
449
>>> cree_image(22, 449, (255, 215, 0))
>>> cree_image(60, 449, (255, 0, 0))
>>> cree_image(90, 449, (0, 0, 255))