Dans le but de sortir toutes les combinaisons de p éléments parmi n éléments, je me suis référé à The Art of Computer Programming volume IV de Donald Knuth. On y trouve plusieurs algorithmes pour ce faire. J'ai donc essayé d'implémenter en PHP certains de ces algorithmes tels qu'ils sont écrits en pseudo-code pour voir ce que ça donne.
Regardons l'algorithme T:Ce qui, en "traduction littérale" vers PHP donne ceci:T1. [Initialize.] Set cj ⟵ j - 1 for 1 ≤ j ≤ t; then set ct + 1 ⟵ n, ct + 2 ⟵ 0, and j ⟵ t.
T2. [Visit.] (At this point j is the smallest index such that cj + 1 > j.) Visit the combination ct … c2c1. Then, if j > 0, set x ⟵ j and go to step T6.
T3. [Easy case?] If c1 + 1 < c2, set c1 ⟵ c1 + 1 and return to T2. Otherwise set j ⟵ 2.
T4. [Find j.] Set cj - 1 ⟵ j - 2 and x ⟵ cj + 1. If x = cj + 1, set j ⟵ j + 1 and repeat step T4.
T5. [Done?] Terminate the algorithm if j > t.
T6. [Increase cj.] Set cj ⟵ x, j ⟵ j - 1, and return to T2.Seulement voilà, goto c'est le mal, un vestige du passé pour les langages récents, la porte ouverte au code spaghetti, voire au n'importe quoi. (Avec le risque de se faire manger par un vélociraptor).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 function combinations_DK_AOCP_Alg_T($n, $t) { $result = []; T1_Initialize: $c = array_combine(range(1, $t), range(0, $t - 1)); $c[$t + 1] = $n; $c[$t + 2] = 0; $j = $t; T2_Visit: $result[] = array_slice($c, 0, $t); if ($j > 0) { $x = $j; goto T6_Increase_cj; } T3_Easy_case: if ($c[1] + 1 < $c[2]) { $c[1]++; goto T2_Visit; } $j = 2; T4_Find_j: $c[$j - 1] = $j - 2; $x = $c[$j] + 1; if ($x === $c[$j + 1]) { $j++; goto T4_Find_j; } T5_Done: if ($j > $t) return $result; T6_Increase_cj: $c[$j] = $x; $j--; goto T2_Visit; }
Alors je le réécris sans gotos ni labels du mieux possible:Les deux codes fonctionnent et ont les mêmes performances pour ce que je veux en faire. Mais avec ce dernier, j'ai du vilain:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 function combinations($n, $p) { $combinations = []; // T1 Initialize $c = array_combine(range(1, $p), range(0, $p - 1)); $c[$p + 1] = $n; $c[$p + 2] = 0; $j = $p; while (true) { // T2 Visit $combinations[] = array_slice($c, 0, $p); if ($j > 0) { $x = $j; } else { // T3 Easy case? if ($c[1] + 1 < $c[2]) { $c[1]++; continue; } $j = 2; // T4 Find j do { $c[$j - 1] = $j - 2; $x = $c[$j] + 1; } while ($x === $c[$j + 1] && $j++); // T5 Done if ($j > $p) return $combinations; } // T6 Increase cj $c[$j] = $x; $j--; } }
- une boucle infinie while (true)
- trois niveaux d'imbrication
- un continue deux niveaux plus loin que le while auquel il se rapporte
- une condition de do...while dans laquelle j'ai dû fourrer une incrémentation && $j++
Mis à part le prêchi-prêcha à propos de l'Art du bien coder, je n'ai pas vraiment l'impression d'avoir gagner au change sur ce coup là. (À noter qu'avec un des autres algorithmes plus complexe que celui-ci, le revolving door algorithm c'est encore pire).
Qu'en pensez-vous? Voyez-vous une autre manière d'écrire ce même algorithme? (hormis renommer des variables ou remplacer le array_combine par une boucle for).
Partager