1+ {
2+ "cells" : [
3+ {
4+ "cell_type" : " markdown" ,
5+ "metadata" : {},
6+ "source" : [
7+ " # 1A - Enoncé 5 novembre 2025 - comptabilité schtroumph\n " ,
8+ " \n " ,
9+ " Correction de l'examen du 5 novembre 2025.\n " ,
10+ " \n " ,
11+ " Toutes les questions valent 2 points."
12+ ]
13+ },
14+ {
15+ "cell_type" : " markdown" ,
16+ "metadata" : {},
17+ "source" : [
18+ " ## Q1"
19+ ]
20+ },
21+ {
22+ "cell_type" : " code" ,
23+ "execution_count" : 3 ,
24+ "metadata" : {},
25+ "outputs" : [
26+ {
27+ "data" : {
28+ "text/plain" : [
29+ " np.int64(45)"
30+ ]
31+ },
32+ "execution_count" : 3 ,
33+ "metadata" : {},
34+ "output_type" : " execute_result"
35+ }
36+ ],
37+ "source" : [
38+ " import numpy as np\n " ,
39+ " \n " ,
40+ " \n " ,
41+ " def distance(table1, table2):\n " ,
42+ " return np.abs(table1 - table2).sum()\n " ,
43+ " \n " ,
44+ " \n " ,
45+ " table1 = np.array([[8, 7], [18, 18], [6, 8]])\n " ,
46+ " table2 = np.array([[18, 18], [7, 9], [8, 6]])\n " ,
47+ " distance(table1, table2)"
48+ ]
49+ },
50+ {
51+ "cell_type" : " markdown" ,
52+ "metadata" : {},
53+ "source" : [
54+ " ## Q2"
55+ ]
56+ },
57+ {
58+ "cell_type" : " code" ,
59+ "execution_count" : 6 ,
60+ "metadata" : {},
61+ "outputs" : [
62+ {
63+ "data" : {
64+ "text/plain" : [
65+ " [(0, 1, 2, 3),\n " ,
66+ " (0, 1, 3, 2),\n " ,
67+ " (0, 2, 1, 3),\n " ,
68+ " (0, 2, 3, 1),\n " ,
69+ " (0, 3, 1, 2),\n " ,
70+ " (0, 3, 2, 1),\n " ,
71+ " (1, 0, 2, 3),\n " ,
72+ " (1, 0, 3, 2),\n " ,
73+ " (1, 2, 0, 3),\n " ,
74+ " (1, 2, 3, 0),\n " ,
75+ " (1, 3, 0, 2),\n " ,
76+ " (1, 3, 2, 0),\n " ,
77+ " (2, 0, 1, 3),\n " ,
78+ " (2, 0, 3, 1),\n " ,
79+ " (2, 1, 0, 3),\n " ,
80+ " (2, 1, 3, 0),\n " ,
81+ " (2, 3, 0, 1),\n " ,
82+ " (2, 3, 1, 0),\n " ,
83+ " (3, 0, 1, 2),\n " ,
84+ " (3, 0, 2, 1),\n " ,
85+ " (3, 1, 0, 2),\n " ,
86+ " (3, 1, 2, 0),\n " ,
87+ " (3, 2, 0, 1),\n " ,
88+ " (3, 2, 1, 0)]"
89+ ]
90+ },
91+ "execution_count" : 6 ,
92+ "metadata" : {},
93+ "output_type" : " execute_result"
94+ }
95+ ],
96+ "source" : [
97+ " import itertools\n " ,
98+ " \n " ,
99+ " \n " ,
100+ " def permutations(n):\n " ,
101+ " return list(itertools.permutations(list(range(n))))\n " ,
102+ " \n " ,
103+ " \n " ,
104+ " permutations(4)"
105+ ]
106+ },
107+ {
108+ "cell_type" : " markdown" ,
109+ "metadata" : {},
110+ "source" : [
111+ " ## Q3, Q4"
112+ ]
113+ },
114+ {
115+ "cell_type" : " code" ,
116+ "execution_count" : 23 ,
117+ "metadata" : {},
118+ "outputs" : [
119+ {
120+ "data" : {
121+ "text/plain" : [
122+ " array([[ 6, 8],\n " ,
123+ " [18, 18],\n " ,
124+ " [ 8, 7]])"
125+ ]
126+ },
127+ "execution_count" : 23 ,
128+ "metadata" : {},
129+ "output_type" : " execute_result"
130+ }
131+ ],
132+ "source" : [
133+ " def permute_ligne_ou_colonne(table, permutation, axis):\n " ,
134+ " if axis == 0:\n " ,
135+ " if len(table.shape) == 2:\n " ,
136+ " return table[permutation, :]\n " ,
137+ " return table[list(permutation)]\n " ,
138+ " return table[:, permutation]\n " ,
139+ " \n " ,
140+ " \n " ,
141+ " permute_ligne_ou_colonne(table1, [2, 1, 0], axis=0)"
142+ ]
143+ },
144+ {
145+ "cell_type" : " markdown" ,
146+ "metadata" : {},
147+ "source" : [
148+ " ## Q5"
149+ ]
150+ },
151+ {
152+ "cell_type" : " code" ,
153+ "execution_count" : 24 ,
154+ "metadata" : {},
155+ "outputs" : [
156+ {
157+ "data" : {
158+ "text/plain" : [
159+ " ((1, 0, 2), (1, 0))"
160+ ]
161+ },
162+ "execution_count" : 24 ,
163+ "metadata" : {},
164+ "output_type" : " execute_result"
165+ }
166+ ],
167+ "source" : [
168+ " def optimise(table1, table2):\n " ,
169+ " best = None\n " ,
170+ " for p1 in permutations(table1.shape[0]):\n " ,
171+ " for p2 in permutations(table1.shape[1]):\n " ,
172+ " t = permute_ligne_ou_colonne(permute_ligne_ou_colonne(table1, p1, 0), p2, 1)\n " ,
173+ " d = distance(t, table2)\n " ,
174+ " if best is None or d < best:\n " ,
175+ " best = d\n " ,
176+ " perms = p1, p2\n " ,
177+ " return perms\n " ,
178+ " \n " ,
179+ " \n " ,
180+ " optimise(table1, table2)"
181+ ]
182+ },
183+ {
184+ "cell_type" : " markdown" ,
185+ "metadata" : {},
186+ "source" : [
187+ " ## Q6\n " ,
188+ " \n " ,
189+ " Si $i$ et $j$ sont les dimensions de deux tables, c'est $O((i!)(j!))$."
190+ ]
191+ },
192+ {
193+ "cell_type" : " markdown" ,
194+ "metadata" : {},
195+ "source" : [
196+ " ## Q7"
197+ ]
198+ },
199+ {
200+ "cell_type" : " code" ,
201+ "execution_count" : 25 ,
202+ "metadata" : {},
203+ "outputs" : [
204+ {
205+ "data" : {
206+ "text/plain" : [
207+ " ((0, 1), (1, 0, 2))"
208+ ]
209+ },
210+ "execution_count" : 25 ,
211+ "metadata" : {},
212+ "output_type" : " execute_result"
213+ }
214+ ],
215+ "source" : [
216+ " def optimise_vecteur(vec1, vec2):\n " ,
217+ " best = None\n " ,
218+ " for p1 in permutations(vec1.shape[0]):\n " ,
219+ " t = permute_ligne_ou_colonne(vec1, p1, 0)\n " ,
220+ " d = distance(t, vec2)\n " ,
221+ " if best is None or d < best:\n " ,
222+ " best = d\n " ,
223+ " perm = p1\n " ,
224+ " return perm\n " ,
225+ " \n " ,
226+ " \n " ,
227+ " def optimise_fast(table1, table2):\n " ,
228+ " return (\n " ,
229+ " optimise_vecteur(table1.sum(axis=0), table2.sum(axis=0)),\n " ,
230+ " optimise_vecteur(table1.sum(axis=1), table2.sum(axis=1)),\n " ,
231+ " )\n " ,
232+ " \n " ,
233+ " \n " ,
234+ " optimise_fast(table1, table2)"
235+ ]
236+ },
237+ {
238+ "cell_type" : " markdown" ,
239+ "metadata" : {},
240+ "source" : [
241+ " Le coût est en $O(i!) + O(j!)$. Pas nécessairement optimal mais beaucoup plus rapide."
242+ ]
243+ },
244+ {
245+ "cell_type" : " markdown" ,
246+ "metadata" : {},
247+ "source" : []
248+ },
249+ {
250+ "cell_type" : " code" ,
251+ "execution_count" : null ,
252+ "metadata" : {},
253+ "outputs" : [],
254+ "source" : [
255+ " import numpy as np\n " ,
256+ " \n " ,
257+ " \n " ,
258+ " coef = np.random.rand(5).reshape((-1, 1))\n " ,
259+ " coef[:, 0] = 1\n " ,
260+ " print(coef)\n " ,
261+ " t1 = np.ones((5, 1)) @ np.array([[5, 1, 2, 3, 4]], dtype=np.float32)\n " ,
262+ " t2 = np.ones((5, 1)) @ np.array([[2, 3, 1, 4, 5]], dtype=np.float32)\n " ,
263+ " M = np.array(\n " ,
264+ " [\n " ,
265+ " [0, 0, 1, 0, 0],\n " ,
266+ " [0, 0, 0, 1, 0],\n " ,
267+ " [0, 1, 0, 0, 0],\n " ,
268+ " [0, 0, 0, 0, 1],\n " ,
269+ " [1, 0, 0, 0, 0],\n " ,
270+ " ]\n " ,
271+ " ).T\n " ,
272+ " \n " ,
273+ " # Il faut diminuer le nombre de solutions pour n'en garder qu'une.\n " ,
274+ " for i in range(5):\n " ,
275+ " t1[i, t1[i, :] == i] = i + 0.01\n " ,
276+ " t2[i, t2[i, :] == i] = i + 0.01\n " ,
277+ " \n " ,
278+ " assert (t1 @ M - t2).max() < 1e-6\n " ,
279+ " \n " ,
280+ " m = np.linalg.lstsq(t1, t2)\n " ,
281+ " print(t1)\n " ,
282+ " print(t2)\n " ,
283+ " print(M)\n " ,
284+ " print((m[0] * 100).astype(int) / 100)"
285+ ]
286+ }
287+ ],
288+ "metadata" : {
289+ "kernelspec" : {
290+ "display_name" : " this312" ,
291+ "language" : " python" ,
292+ "name" : " python3"
293+ },
294+ "language_info" : {
295+ "codemirror_mode" : {
296+ "name" : " ipython" ,
297+ "version" : 3
298+ },
299+ "file_extension" : " .py" ,
300+ "mimetype" : " text/x-python" ,
301+ "name" : " python" ,
302+ "nbconvert_exporter" : " python" ,
303+ "pygments_lexer" : " ipython3" ,
304+ "version" : " 3.12.3"
305+ }
306+ },
307+ "nbformat" : 4 ,
308+ "nbformat_minor" : 2
309+ }
0 commit comments