La récursivité
Stephen Hawking
To understand recursion, one must first understand recursion
La récursivité est une des techniques de programmation les plus employés et populaires. Elle apparait quand on met en relation une entité avec elle-même, par exemple la suite de Fibonacci est définis en fonction d’ elle-même :
On retrouve la récursivité dans des domaines très différents comme les sciences du vivant, les mathématiques, l'informatique et la linguistique. Certains acronymes de produits connus sont définis récursivement, par exemple la signification de GNU : "GNU's not Unix".
En informatique et plus spécialement en programmation, la récursivité est une technique qui permet de résoudre et d'exprimer certains problèmes plus simplement que de manière impérative. Par exemple, la factorielle d'un nombre est mathématiquement définie comme suit :
Exercice
Écrivez une fonction récursive qui calcule la factorielle d'un nombre et comparez-la avec l'implémentation impérative que vous avez réalisé avant.
Solution
fun fac(n : Int) : Int =
if(n == 0) 1
else n * fac(n-1)
Le cas de base
Lorsque l'on écrit une fonction (ou un algorithme) récursive, il faut tout d'abord réfléchir au cas trivial. Autrement dit, à quel moment la fonction doit s'arrêter.
Dans le cas de la factorielle d'un nombre, le cas de base est n == 0
. À ce moment-là,
on n'effectue plus l'appel de fonction et on retourne la valeur 1
.
Exercice
Identifiez précisément les cas de bases pour les fonctions récursives suivantes :
- Factorielle
- Suite de Fibonacci
- Suite de Syracuse
- Tester si un nombre est pair
- Tester si un nombre est impair
Solution
fun fac(n: Int): Int {
if (n == 0) // trivial case
return 1
else
TODO()
}
fun fib(n: Int): Int {
if (n == 0) // trivial case 1
return 0
else if (n == 1) // trivial case 2
return 1
else TODO()
}
fun syracuse(n: Int): Sequence<Int> {
if (n == 1) // trivial case
return sequenceOf(2, 4, 1)
else
TODO()
}
fun odd(n: Int): Boolean {
if (n == 0) // trivial case
return true
else
TODO()
}
fun even(n: Int): Boolean {
if (n == 1) // trivial case
return true
else
TODO()
}
Identifier le ou les cas de bases est un point clé dans la conception d'une fonction ou d'un algorithme récursif.
Décomposer le problème
Une fois que le ou les cas de bases sont identifiés, il faut essayer d'identifier les sous-problèmes
liés à la fonction. Par exemple, pour la factorielle, on décompose le calcul n * fac(n-1)
en deux parties :
- Ce qui est calculé maintenant :
n * ...
- Ce qui est délégué à un autre appel récursif :
... fac(n - 1)
L'idée est de résoudre une partie du problème et de déléguer le reste du calcul à quelqu'un d'autre.
Exercice
Exercice
Proposer des signatures pour les fonctions récursives suivantes :
- Factorielle
- Suite de Fibonacci
- Suite de Syracuse
- Tester si un nombre est pair
- Tester si un nombre est impair
Solution
fun fac(n : Int) : Int = TODO()
fun fib(n : Int) : Int = TODO()
fun syracuse(n : Int) : Sequence<Int> = TODO()
fun odd(n : Int) : Boolean = TODO()
fun even(n : Int) : Boolean = TODO()
Astuce : TODO()
Lorsque l'on écrit un programme, il arrive que l'on souhaite concevoir une fonction qui effectue certaines opérations, mais que l'on veuille l'implémenter plus tard. On écrit donc la signature de la fonction, mais sans l'implémenter, on écrit donc du code "bidon" pour que le programme compile :
fun fac(n : Int) : Int {
// TODO implement this function
return 1
}
Avec Kotlin il est possible d'utiliser la fonction TODO()
ce qui évite d'écrire
du code inutile :
fun fac(n : Int) : Int {
TODO("Implement factorial")
}
Exercice
La suite de Syracuse est énoncée comme suit :
«On part d'un nombre entier plus grand que zéro; s'il est pair, on le divise par
2; s'il est impair on le multiplie par 3 et on ajoute 1.» Dès que la suite rencontre
le nombre 1, elle va tourner sur elle-même avec la séquence 4,2,1
.
Écrivez une fonction qui afficher la suite de Syracuse d'un nombre indéfiniment.
Solution
fun syracuse(n: Int): List<Int> {
fun inner(x: Int, acc: List<Int>): List<Int> {
if (x == 1)
return acc.plus(listOf(4, 2, 1))
else {
val newN =
if (x % 2 == 0)
x / 2
else
x * 3 + 1
return inner(newN, acc.plus(newN))
}
}
return inner(n, emptyList())
}
Exercice
Implémenter les fonctions suivantes en utilisant la technique de la récursivité :
- Factorielle
- Suite de Fibonacci
Solution
fun fac(n: Int): Int {
if (n == 0)
return 1
else
return n * fac(n - 1)
}
fun fib(n: Int): Int {
if (n == 0)
return 0
else if (n == 1)
return 1
else
return fib(n - 1) + fib(n - 2)
}
- Tester si un nombre est pair
- Tester si un nombre est impair
Solution
// Even and odd using simple recursion
object SimpleRecursion {
fun even(n: Int): Boolean =
if (n == 0) true
else if (n == 1) false
else odd(n - 2)
fun odd(n: Int): Boolean =
if (n == 0) false
else if (n == 1) true
else even(n - 2)
}
//Even and odd using mutual recursion
object MutualRecursion {
fun even(n: Int): Boolean =
if (n == 0) true
else if (n == 1) false
else even(n - 1)
fun odd(n: Int): Boolean =
if (n == 0) false
else if (n == 1) true
else odd(n - 1)
}