Accueil Nos publications Blog Kotlin 1.3: Multiplatform Random

Kotlin 1.3: Multiplatform Random

Le 29 Octobre est sorti Kotlin 1.3. Au programme, beaucoup de nouveautés. Mais bien qu’il était déjà possible de générer des nombres aléatoires en Kotlin (par le biais des apis Java / Js / Native), dans la release note de la version 1.3 du langage, une nouvelle fonctionnalité fait son entrée. Elle se nomme : “Multiplatform Random”. Elle est très importante pour l’évolution du langage. Mais avant de parler des subtilités et de l’implémentation en Kotlin, je pense qu’il est important de comprendre comment fonctionne l’aléatoire en informatique.

aléatoireadjectif
Soumis au hasard, dont le résultat est incertain : Entreprise aléatoire.

Source: Larousse

Un algorithme étant, par définition, un ensemble d’opérations déterministes, nous pouvons nous demander s’il est possible, avec les mêmes conditions initiales, d’obtenir une suite de nombres aléatoires par ordinateur.

“Le résultat est incertain” implique donc qu’il n’existe aucun lien déterministe entre tous les éléments d’une suite de nombres aléatoires. Un algorithme, avec les mêmes conditions initiales, donnera toujours le même résultat, il sera donc impossible de générer une véritable suite aléatoire. C’est pour cela que l’on parle de Pseudo Random Number Generator (PRNG) lorsque l’on parle d’un algorithme et non pas d’un True Random Number Generator (TRNG).

Il existe des solutions matérielles permettant d’obtenir un TRNG en utilisant des capteurs afin de mesurer des phénomènes physiques tels que le bruit électromagnétique ou atmosphérique, par exemple.

Dans beaucoup de cas courants, l’utilisation d’un PRNG est suffisante, voire recommandée. Les PRNG sont beaucoup plus rapides que leurs homologues, même si déterministes. Il est néanmoins important de garder en tête la différence entre les deux familles de générateurs aléatoires. Par exemple, il n’est pas recommandé d’utiliser un PRNG directement pour le développement de son jeu d’argent en ligne.

Il est d’ailleurs possible de vérifier l’indépendance de deux variables, et donc de valider qu’un ou plusieurs tirages sont biens aléatoires, statistiquement, en utilisant le test de χ²(test de khy 2).

Si vous souhaitez approfondir votre connaissance des générateurs de nombres aléatoires, je vous recommande de lire cette page de la documentation du service de TRNG en ligne sur random.org, très complète sur le sujet : https://www.random.org/randomness/

Avant Kotlin 1.3

Kotlin est un langage de programmation pouvant fonctionner suivant plusieurs paradigmes.

Kotlin peut fonctionner sur plusieurs plateformes. (JVM / JS / Native)

Lorsque l’on utilise Kotlin/JVM, Kotlin fonctionne sur la JVM et dispose d’une compatibilité complète avec Java. Un développeur travaillant sur un projet dans cette configuration pourra donc utiliser java.util.Random(), java.security.SecureRandom() ou encore java.util.concurrent.ThreadLocalRandom(), sans problème dans son code Kotlin.

Kotlin/JS quant à lui, est assez similaire à Typescript. Dans cette configuration, le code Kotlin est transpilé en Javascript et dispose donc d’une compatibilité avec les fonctionnalités Javascript. Tout développeur sur ce type de projet aura donc accès à Math.random().

Kotlin/Native utilise LLVM pour proposer une version compilée en code machine du code Kotlin. Dans ce cas, Kotlin aura accès au rand() de C / C++.

Mais depuis la version 1.2, il existe un type de projet hybride : les projets dits “multiplatform”. Il s’agit d’un type de projet permettant de partager du code Kotlin entre les différentes plateformes (avec possibilité de définir des implémentations particulières par plateforme). Ce type de projet ne reposant pas sur une autre plateforme, il n’a pas de PRNG à disposition.

Sans solution, les développeurs rusaient afin de répondre à leur besoin. Plusieurs solutions assez originales ont été remarquées aux quatres coins du web. Certains utilisaient les mots clés expect / actual de Kotlin Multiplatform pour utiliser le random de chaque plateforme ; d’autres utilisaient Iterable.shuffled() pour mélanger un Range et prendre la première valeur et des implémentations à la main de PRNG ont même été aperçues.

Mais aucune des solutions n’était réellement optimale, car les problématiques autour des PRNG sont nombreuses et complexes. C’est à ce moment que la solution proposée dans Kotlin 1.3 entre en jeu.

Depuis Kotlin 1.3

Dans la version 1.3, un nouveau package kotlin.random a fait son apparition au sein de la stdlib, avec comme description “Fournit le générateur de valeur pseudo-aléatoire par défaut et une classe de base pour les autres implémentations de RNG”.

Comment s’en servir ?

La classe abstraite Random a donc fait son apparition au sein du package, à l’instar de java.util.Random en Java. Elle représente la classe de base pour toute implémentation de RNG en Kotlin, mais aussi l’implémentation de PRNG par défaut dans son Companion Object.

L’implémentation par défaut proposée dans la stdlib est un XORWOW. Le choix de cet algorithme a été fait en prenant en compte les contraintes suivantes :

  • L’implémentation doit être simple pour être facilement vérifiable.
  • L’implémentation ne doit pas nécessiter de long 64 bits pour des problèmes de performance en Kotlin/JS (pas de support natif).
  • L’implémentation doit avoir un nombre minimal d’opérations et ne doit pas nécessiter d’opérations complexes.
  • L’implémentation doit avoir un minimum d’état à maintenir.
  • La période du générateur (le nombre de valeurs possibles) doit être au moins de la taille d’un long (soit 2^32).

Pour s’en servir, il suffit donc juste de faire :

import kotlin.random.Random
[...]
val number = Random.nextInt(42) // Dans l'intervalle 0 .. 42

Les PRNG peuvent être initialisés avec une seed. Deux générateurs aléatoires avec la même seed donneront le même résultat, quelle que soit la plateforme. Pratique pour faire des tests unitaires.

import kotlin.random.Random
[...]
val number = Random(24).nextInt(42) // Dans l'intervalle 0 .. 42 avec la seed 24

La stdlib a été mise à jour pour utiliser directement cette classe au lieu des classes des plateformes. Aussi, de nouvelles fonctionnalités sont apparues, notamment sur les collections.

val myList = listOf("a", "b", "c")
println(myList.random())

En Kotlin/JVM, il est possible de transformer son objet Random Kotlin en objet random Java pour pouvoir s’en servir avec du code Legacy, par exemple.

Random.asJavaRandom()

Conclusion

Aussi insignifiante que paraisse la fonctionnalité sur le papier, elle va enlever une épine du pied de nombre de développeurs. Avec cette dernière, nous pouvons remarquer que Jetbrains aimerait réellement voir Kotlin évoluer au delà des frontières de la JVM, et travaille en ce sens. De nombreux projets Kotlin multiplatform fleurissent (Les Coroutines, Multiplatform HTTP Client de KTor, SQLDelight de Square…) et la communauté est là pour suivre leur évolution.

Même s’il est encore trop tôt pour réellement apposer un jugement sur Kotlin Multiplatform, une chose est sûre, Kotlin continue sa progression, et il pourrait devenir un sérieux concurrent aux solutions multiplateformes déjà en place.

© SOAT
Toute reproduction interdite sans autorisation de l’auteur