Deuxième exercice Python / JavaScript / APL

Résumé en français : On vous donne 3 nombres différents dans un ordre quelconque. En sortie, donnez le rang du nombre qui est entre les 2 autres. Par exemple avec 2, 3, 1 c’est le chiffre 2 qui est entre 1 et 3, son rang dans 2, 3, 1 est 0.

Partons d’un tri

Une première idée est de trier les 3 éléments (par ordre croissant ou décroissant, peu importe), de chercher quelle valeur est au milieu et enfin de récupérer le rang de cette valeur dans le tableau initial. Par exemple avec 2, 3 et 1, le tri donne 1, 2, 3 ce qui permet de récupérer la valeur centrale “2” et donc son rang 0 dans le tableau initial.

PythonJavaScriptAPL
sorted et sortsort
Comment trier en…

En Python, la fonction sorted tri par défaut un tableau numérique par ordre croissant :

>> sorted([21, 3, 1])
[1, 3, 21]

On peut également utiliser la méthode sort :

>> a = [21, 3, 1]
>> a.sort()
>> a
[1, 3, 21]

En JavaScript, la méthode sort fait un tri alphabétique, si bien que :

>> [21, 3, 1].sort()
[1, 21, 3]

En effet, dans un dictionnaire, le “mot” 21 est avant le “mot” 3… C’est pourquoi on utilise très souvent une fonction de comparaison. Voici celle pour obtenir un tri croissant :

>> [21, 3, 1].sort((a, b) => a - b)
[1, 3, 21]

Lorsque la différence a – b est négative, a sera placé avant b, si la différence est positive a sera placé après b sinon les 2 éléments restent inchangés.

En APL, autre vision du tri !

      ⍋ 21 3 1
3 2 1

La réponse 3 2 1 signifie que l’élément qui doit être en premier est à la 3e position (c’est le 1 du vecteur initial) ensuite le second est à la 2e position (le 3 du vecteur initial) et enfin le dernier élément est à la 1ere position (le 21).

Remarquez que l’on a la réponse à l’exercice, le 2e élément de ⍋ est la position recherchée. On visualise le 0 en 2e position :

      ⎕IO ← 0          ⍝ On impose que l'origine des indices soit 0
      ⍋ 2 3 1
2 0 1

Voici des versions Python / JavaScript et APL en utilisant les tris :

Python

def milieu(triplet):
    return triplet.index(sorted(triplet)[1])

>> milieu([2, 3, 1])
0
>> milieu([5, 10, 14])
1

Javascript

milieu = triplet => triplet.indexOf([...triplet].sort((a, b) => a - b)[1])

>> milieu([2, 3, 1])
0
>> milieu([5, 10, 14])
1

[…triplet] permet de faire une copie du tableau initial.

APL

      ⎕IO ← 0
      milieu ← {(⍋⍵)[1]}

      milieu 2 3 1
0
      milieu 5 10 14
1

Autre écriture possible, le symbole ⊃ (pick) utilisé seul (monadique) permet de récupérer le 1er élément d’un vecteur (ou matrice) et sous sa forme dyadique le n-ième terme :

      ⊃ 'bonjour'          ⍝ Ecriture monadique = 1er élément
b
      3 ⊃ 'bonjour'        ⍝ Ecriture dyadique
n
      ⊃ 4 6 8              ⍝ 1er élément
4
      3 ⊃ 4 6 8            ⍝ 3e élément
8

Ce qui permet d’obtenir cette version :

      ⎕IO ← 0
      milieu ← 2 ⊃ ⍋

      milieu 2 3 1
0

Si certain.e.s parmi vous aiment les choses concises, APL ne devrait pas vous déplaire de ce point de vue !

Et si on cherchait un invariant ?

Remarquons que si l’on trouve les positions de la plus grande valeur (max) et de la plus petite valeur (min) du triplet, la position restante sera celle que l’on cherche.

Mais l’ensemble des positions sera toujours { 0, 1, 2 }. Par exemple si le max est en 2e et le min en 1er, alors le milieu est en position 0. L’invariant intéressant ici est la somme des positions qui vaut toujours 0+1+2=3. Le rang que l’on cherche est donc :
rang_cherché = 3 – rang(max) – rang(min)

Python

def milieu(triplet):
    return 3 - triplet.index(max(triplet)) \
             - triplet.index(min(triplet))

javascript

milieu = triplet => 3 - triplet.indexOf(Math.max(...triplet))
                      - triplet.indexOf(Math.min(...triplet))

Observez la différence en JavaScript entre :

>> Math.max(2,5,8,1,10,3)
10
>> Math.max([2,5,8,1,10,3])
NaN
>> Math.max(...[2,5,8,1,10,3])
10

APL

En APL, on trouve les sommes, max, min des éléments d’un vecteur (ou matrice) en utilisant une réduction :

      +/ 2 3 1        ⍝ Somme des termes
6
      ⌈/ 2 3 1        ⍝ maximum
3
      ⌊/ 2 3 1        ⍝ minimum
1

Et pour trouver le rang d’un élément dans un vecteur, on utilise ⍳ (iota) :

      ⎕IO ← 1
      5 7 3 1 ⍳ 7     ⍝ Position de 7 dans le vecteur 5 7 3 1 ?  
2

Toujours avec le principe de l’invariant, somme – max – min donne le nombre qui est au milieu, celui dont on doit chercher le rang. On obtient finalement ce programme que vous pouvez tester directement ici :

⎕IO ← 0
milieu ← ⊢ ⍳ +/ - ⌈/ + ⌊/

      milieu 2 3 1
0
      milieu 5 10 14
1

Le ⊢ permet de récupérer le paramètre qui est à droite (équivalent de {⍵}) et je rappelle qu’APL fait les calculs de la droite vers la gauche, donc le minimum ⌊/ puis max + min ⌈/+⌊/ la soustraction va correspondre à – max – min et on ajoute la somme.