Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Java Quiz #25

Que pensez-vous de ce code ?

  1. /**
  2.  * Renvoie un nombre aléatoire entre 0 (inclus) et 'maxValue' (inclus).
  3.  * @param maxValue Borne supérieure
  4.  * @param even Indique si le nombre doit être pair (true) ou impair(false)
  5.  * @return Le nombre aléatoire
  6.  */
  7. public static final int getRandomNumber(int maxValue, boolean even)
  8. {
  9. if (maxValue <= 0)
  10. { throw new IllegalArgumentException("Invalid 'maxValue' value");
  11. }
  12.  
  13. int value;
  14. if (even)
  15. { value = new Random().nextInt(maxValue / 2 + 1) * 2;
  16. }
  17. if (!even)
  18. { value = new Random().nextInt((maxValue + 1) / 2) * 2 + 1;
  19. }
  20. return value;
  21. }

Réponse après les fêtes !

Note : on m'a fait remarquer à juste titre que l'algorithme de génération des nombres était faux ; je l'ai donc corrigé (et testé cette fois...). Rassurez-vous, cela ne change en rien le point technique que ce quiz vise à exposer.

Réponse :
Ce code ne compile pas car le compilateur ne peut pas déterminer si la variable locale "value" a bien été initialisée avant utilisation.

Le problème vient ici du double test if. S'il est, de notre point de vue, logiquement équivalent à un bloc if/else, le compilateur le considère au contraire comme deux instructions distinctes et indépendantes. Il est donc incapable de garantir que le code passera dans au moins l'un des deux chemins pour initialiser la variable "value", et provoque une erreur.
Naturellement, remplacer les deux tests par un simple bloc if/else résout le problème et permet la compilation du code.

Conclusion :

  • Toujours initialiser ses variables locales, même avec un simple null.
  • Un simple changement apparemment inconséquent du point de vue logique peut avoir de graves implications du point de vue informatique. Il faut donc prêter une attention particulière à vérifier l'applicabilité des algorithmes logiques aux langage particuliers utilisés pour les implémenter.

Commentaires

1. Le mercredi 24 décembre 2008, 14:33 par Gregory Boissinot

Le code ne compile pas. Le compilateur Java n'est pas capable d'évaluer le paramètre « even » et que l'une des 2 conditions sera vraie à l'exécution. Autre anomalie, le code lancera une exception si maxValue<=1 car la méthode nextInt() ne peut pas prendre un nombre inférieur ou égal à zéro.

2. Le mercredi 24 décembre 2008, 21:32 par Gilles

Je tente le 'trop d'exclusion' : si on veut un nombre pair inférieur strictement à 11, il me semble que 10 ne sera jamais proposé, ce qui est dommage.

Merci au passage pour ce blog.

3. Le jeudi 25 décembre 2008, 09:21 par Pascal

Un "else" au lieu d'un second "if" pour la lisibilité.

Si la fonction doit être appelée intensément, allouer un nouvelle objet Random à chaque appel n'est sûrement pas très appropriée.

A part ça, je ne vois pas :D

4. Le dimanche 28 décembre 2008, 16:56 par José

Je pense que la machine Java va ouinouiner si on ne lui donne pas une valeur initiale pour value, mais ça c'est un détail.

Le vrai problème, c'est que le fait de reconstruire à chaque fois un nouvel objet Random rend la fonction getRandomNumber assez peu aléatoire. Ou en tout cas avec un caractère "non aléatoire" un peu subtil à voir. En fait, l'objet Random construit une suite pseudo aléatoire avec différentes "bonnes" propriétés, à partir d'une "seed", sorte de valeur initiale. Si l'on ne donne pas de "seed", la JVM la choisit elle-même, et ne garantit pas que cette valeur intiale sera différente à chaque construction. Moralité : on peut se retrouver avec des valeurs aléatoires qui sont en fait toutes les mêmes.

C'est un peu subtil à voir, ce qu'il faut regarder, ce sont les probabilité de transition. On peut prendre un exemple simple, qui ne va tirer que des 0 et des 2, par exemple en invoquant getRandomNumber(3, true). Là il faut tirer beaucoup (1 million c'est pas mal) de ces nombres, et calculer
- p00 : nombre de fois que l'on tire 0, puis 0
- p01 : nombre de fois que l'on tire 0, puis 2
- p10 et p11 (idem).

Ensuite, on calcule p00/(p00 + p01) et p01/(p00 + p01). Si on a un "bon" générateur aléatoire, ces deux probabilités doivent valoir 1/2 chacune (idem pour p10 et p11). Si l'on a un générateur qui tire toujours la même valeur sur un petit intervalle de temps (le cas ici, à mon avis), la probabilité de transition sera plus faible.

La simple analyse de la moyenne de ce générateur ne permet pas de voir ce problème.

5. Le dimanche 28 décembre 2008, 17:19 par Olivier Croisier

Le point soulevé par Pascal et José est très intéressant. La génération de nombres alétoires est un sujet complexe qui a fait l'objet de nombreuses thèses, et reste terriblement d'actualité à notre époque où la cryptographie est omniprésente (clés wifi, etc.).
Toutefois, ce n'était pas le point technique que ce quiz visait à démontrer, qui est plus simple et concerne uniquement la phase de compilation.

6. Le mardi 9 octobre 2012, 06:08 par Reiner Saddey

Autre anomalie, le code lancera une exception si maxValue == Integer.MAX_VALUE et even == false car la méthode nextInt() ne peut pas prendre un nombre inférieur ou égal à zéro.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.