#!/usr/bin/env python # coding: utf-8 #
#
TP n°14 (représentation des nombres) : Éléments de correction
# $\require{color}$ # $\require{xcolor}$ # $\require{cancel}$ # $\newcommand{\myfbox}[1]{\fcolorbox{red}{white}{$\textrm{#1}$}}$ # $\require{stmaryrd}$ # $\newcommand{\ient}{[\![}$ # $\newcommand{\rrbracket}{]\!]}$ # $\newcommand{\llbracket}{[\![}$ # $\newcommand{\fient}{]\!]}$ # $\newcommand{\R}{\mathbb R}$ # $\newcommand{\K}{\mathbb K}$ # $\newcommand{\N}{\mathbb N}$ # $\newcommand{\C}{\mathbb C}$ # $\newcommand{\P}{\mathbb P}$ # $\newcommand{\E}{\mathbb E}$ # $\newcommand{\V}{\mathbb V}$ # $\newcommand{\Z}{\mathbb Z}$ # $\newcommand{\dt}{\mathrm d}$ # $\newcommand{\sh}{\operatorname{sh}}$ # $\newcommand{\ch}{\operatorname{ch}}$ # $\newcommand{\id}{\operatorname{Id}}$ # $\newcommand{\mat}{\operatorname{mat}}$ # $\newcommand{\sp}{\operatorname{Sp}}$ # $\newcommand{\In}{\operatorname{I}}$ # $\newcommand{\vect}{\operatorname{Vect}\ }$ # $\newcommand{\rg}{\operatorname{rg}}$ # $\newcommand{\tr}{\operatorname{Tr}}$ # $\newcommand{\dis}{\displaystyle}$ # $\renewcommand{\Im}{\operatorname{Im}}$ # $\newcommand{\im}{\operatorname{Im}}$ # $\newcommand{\bordermatrix}[3]{ \begin{matrix} \begin{matrix}#1\end{matrix} & \\ #3 & \hspace{-1em} \begin{matrix}#2\end{matrix} \end{matrix}}$ # $\newcommand{\fonction}[5]{#1\ \colon \left\{\begin{array}{ccl}#2 & \longrightarrow & #3\\#4 & \longmapsto & #5\end{array}\right.}$ # $\newcommand{\fonctionsansnom}[4]{\left\{\begin{array}{ccl}#1 & \longrightarrow & #2\\#3 & \longmapsto & #4\end{array}\right.}$ # $\newcommand{\revdots}{\mathinner{\mkern1mu\raise1pt{\kern7pt\hbox{.}}\mkern2mu\raise4pt\hbox{.}\mkern2mu\raise7pt\hbox{.}\mkern1mu}}$ # $\newcommand{\tendvers}[2]{\xrightarrow[#1\to#2]{}}$ # In[65]: import matplotlib.pyplot as plt import numpy as np # ## I. Représentation binaire, addition et complément à deux #
# Exercice 1 (conversion en binaire) #
# In[66]: def conv_binaire(n): """ Entrée : un entier naturel n Sortie : la chaîne de caractères correspondant à sa décomposition binaire """ if n == 0: return "0" else: binaire = "" while n > 0: binaire = str(n % 2) + binaire n = n // 2 return binaire # In[42]: conv_binaire(6) # Pour la conversion "réciproque", on peut utiliser la méthode de Hörner, qui minimise le nombre de multiplications à faire : # In[47]: def conv_decimal(L): """ Entrée : une chaîne de caractères de 0 et de 1 Sortie : l’entier en base 10 correspondant. """ s = len(L) n = int(L[0]) for i in range(1,s): n = n * 2 + int(L[i]) return n # In[4]: def conv_decimal(L): """ Entrée : une chaîne de caractères de 0 et de 1 Sortie : l’entier en base 10 correspondant. """ s = int(L[0]) for l in L[1:]: s = s * 2 + int(l) return s # In[5]: conv_decimal("100") #
# Exercice 2 (représentation des entiers relatifs) #
# In[6]: def inverse(S): R = "" for s in S: if s == "1": R = R + "0" else: R = R + "1" return R # In[7]: inverse("10011") # Pour obtenir la représentation de $-n$ (avec complément à $2$) : # - on convertit $n$ en binaire sur $b$ bits # - on inverse tous les bits # - on ajoute $1$ au résultat (avec propagation de la retenue) # - on tronque à $b$ bits # In[8]: def oppose(n, b): assert n > 0 and n <= 2**(b - 1) S = conv_binaire(n) # on convertit n en binaire sur b bits S = "0" * (b - len(S)) + S # on utilise exactement b bits S = inverse(S) # on inverse les bits R = "" # le résultat à renvoyer k = 1 while S[-k] == "1": R = "0" + R k +=1 R = "1" + R R = S[0:b-k] + R return R # In[9]: oppose(120,8) # In[10]: np.binary_repr(-120, width=8) # In[11]: for k in range(1,128): if oppose(k,8) != np.binary_repr(-k, width=8): print(k) # In[12]: def conv_binaire_r(n, b): assert n >= -2**(b - 1) and n <= 2**(b - 1) - 1 if n >= 0: S = conv_binaire(n) return "0" * (b - len(S)) + S else: return oppose(-n,b) # In[13]: conv_binaire_r(-1, 8) # In[14]: conv_binaire_r(1, 8) # In[15]: for k in range(-128,127): if conv_binaire_r(k, 8) != np.binary_repr(k, width=8): print(k) # In[16]: def conv_decimal_r(S, b): if S[0] == "0": return conv_decimal(S) else: return conv_decimal(S[1:]) - 2**(b - 1) # In[17]: conv_decimal_r('00000101', 8) # In[18]: conv_decimal_r('11111111', 8) # ## II. Stéganographie #
# Exercice 3 #
# In[48]: def stegano(S1, S2): return S1[:4] + S2[:4] #
# Exercice 4 #
# In[49]: M = plt.imread("eleves.png") M = (M * 255).astype(np.uint8) N = plt.imread("paysage.png") N = (N * 255).astype(np.uint8) # In[50]: M[12][24] # In[22]: plt.figure(figsize=(4, 4)) plt.imshow(M) plt.axis("off") plt.show() # On va «cacher» les élèves dans l'image du paysage : # In[23]: taille = len(M) P = [[[0, 0, 0] for i in range(taille)] for j in range(taille)] for i in range(taille): for j in range(taille): for k in range(3): S1 = np.binary_repr(N[i][j][k], width=8) S2 = np.binary_repr(M[i][j][k], width=8) P[i][j][k] = conv_decimal(stegano(S1, S2)) plt.figure(figsize=(4, 4)) plt.imshow(P) plt.axis("off") plt.title("Les élèves sont cachés dans cette image") plt.show() # On peut chercher à la comparer à l'image originale : # In[24]: plt.figure(figsize=(6, 6)) plt.subplot(1, 2, 1) # sous-figure n°1 (1 ligne, 2 colonnes, position 1) plt.imshow(N) plt.axis('off') plt.title("Image originale") plt.subplot(1, 2, 2) # sous-figure n°2 (1 ligne, 2 colonnes, position 2) plt.imshow(P) plt.axis('off') plt.title("Image perturbée") plt.show() #
# Exercice 5 #
# On peut maintenant chercher à retrouver l'image cachée dans l'image précédente : # In[25]: L = [[[0, 0, 0] for i in range(taille)] for j in range(taille)] for i in range(taille): for j in range(taille): for k in range(3): S = np.binary_repr(P[i][j][k], width=8) S = S[4:] + 4 * "0" L[i][j][k] = conv_decimal(S) # taille et résolution des figures plt.rcParams['figure.figsize'] = [4, 4] plt.imshow(L) plt.axis("off") plt.show() # On peut la comparer avec l'image originale : # In[26]: plt.figure(figsize=(6, 6)) plt.subplot(1, 2, 1) # sous-figure n°1 (1 ligne, 2 colonnes, position 1) plt.imshow(M) plt.axis('off') plt.title("Image originale") plt.subplot(1, 2, 2) # sous-figure n°2 (1 ligne, 2 colonnes, position 2) plt.imshow(L) plt.axis('off') plt.title("Image retrouvée") plt.show() # ## III. Fibonacci et stéganographie #
# Exercice 6 #
# In[27]: u, v = 0, 1 L = [] for k in range(12): u, v = v, u + v L.append(v) print(L) # In[28]: def zeckendorf(n): m = n S = "" i = len(L) - 1 while m > 0: while L[i] > m: i -= 1 S = S + "0" S = S + "1" m = m - L[i] i -= 1 return S + "0" * (12 - len(S)) # In[29]: zeckendorf(240) #
# Exercice 7 #
# In[58]: M = plt.imread("eleves.png") M = (M * 255).astype(np.uint8) N = plt.imread("paysage.png") N = (N * 255).astype(np.uint8) # In[59]: def stegano_fib(S1, S2): return S1[:6] + S2[:6] # In[60]: def conv_decimal_fib(S): return sum(L[len(L) - i - 1] * int(S[i]) for i in range(len(L))) # In[61]: taille = len(M) P = [[[0, 0, 0] for i in range(taille)] for j in range(taille)] for i in range(taille): for j in range(taille): for k in range(3): S1 = zeckendorf(N[i][j][k]) S2 = zeckendorf(M[i][j][k]) P[i][j][k] = conv_decimal_fib(stegano_fib(S1, S2)) plt.figure(figsize=(4, 4)) plt.imshow(P) plt.axis("off") plt.show() # Effectivement, on peut, avec cette méthode, dépasser la valeur $255$. Python tronque alors la valeur à $255$ avant d'afficher l'image... Cela peut provenir de deux problèmes : # - en concaténant, on peut créer des représentations contenant le motif ```"11"```, qui ne sont pas des représentations de Zeckendorf valides. # - On peut également créer des nombres qui ont une représentation valide, mais qui dépassent $255$ en écriture décimale. # # Le second point se règle au moment de l'affichage, en prenant le minimum entre la valeur de chaque couleur de chaque pixel et $255$, à l'aide de la fonction ```np.minimum()``` qui s'applique composante par composante. # # Le premier point est plus problématique : la conversion inverse ne redonne pas les bits initiaux. On peut régler ce problème de la manière suivante : # In[62]: def stegano_fib(S1, S2): if S1[5] == "1" and S2[0] == "1": return S1[:5] + "0" + S2[:6] else: return S1[:6] + S2[:6] # In[63]: taille = len(M) P = [[[0, 0, 0] for i in range(taille)] for j in range(taille)] for i in range(taille): for j in range(taille): for k in range(3): S1 = zeckendorf(N[i][j][k]) S2 = zeckendorf(M[i][j][k]) P[i][j][k] = conv_decimal_fib(stegano_fib(S1, S2)) plt.figure(figsize=(4, 4)) plt.imshow(np.minimum(P, 255)) plt.axis("off") plt.show() # On peut désormais essayer de retrouver l'image cachée : # In[64]: C = [[[0, 0, 0] for i in range(taille)] for j in range(taille)] for i in range(taille): for j in range(taille): for k in range(3): S = zeckendorf(P[i][j][k]) S = S[6:] + 6 * "0" C[i][j][k] = conv_decimal_fib(S) plt.figure(figsize=(4, 4)) plt.imshow(C) plt.axis("off") plt.show()