Pourquoi ce code utilisant des chaînes aléatoires affiche-t-il «bonjour le monde»?


1769

La déclaration d'impression suivante imprimerait "bonjour le monde". Quelqu'un pourrait-il expliquer cela?

System.out.println(randomString(-229985452) + " " + randomString(-147909649));

Et randomString()ressemble à ceci:

public static String randomString(int i)
{
    Random ran = new Random(i);
    StringBuilder sb = new StringBuilder();
    while (true)
    {
        int k = ran.nextInt(27);
        if (k == 0)
            break;

        sb.append((char)('`' + k));
    }

    return sb.toString();
}

158
Eh bien, ces graines particulières fonctionnent parfaitement. L'aléatoire n'est pas vraiment aléatoire, il est pseudo-aléatoire.
tckmn

341
Cela fonctionne, comme d'autres l'ont dit, parce que le hasard ne l'est pas. Pour moi, une question plus intéressante serait celle de la personne qui a écrit cela, la force brute, ou existe-t-il un moyen facile de prédire quel aléatoire générerait pour les N valeurs suivantes pour une graine donnée. Le forçage brutal est facile et avec du matériel moderne ne devrait pas prendre trop de temps, c'était donc une façon viable de le faire. Étant donné qu'il est statique, vous pouvez même facilement distribuer la recherche sur un réseau.
jmoreno

78
Je me demande le but de nin for (int n = 0; ; n++). Ils pourraient utiliser for(;;)ou à la while(true)place!
Eng.Fouad

13
Dans une séquence vraiment aléatoire, chaque chaîne possible finira par apparaître. Dans une séquence pseudo aléatoire de haute qualité, on peut raisonnablement s’attendre à toutes les chaînes de longueur possibles (log_s (N) - n) bits (où N est le nombre de bits dans l’état interne des PRNG et n est un petit nombre, choisissons 8 pour plus de commodité) ) pour apparaître dans le cycle. Ce code obtient de l'aide de l'utilisation d'un point de départ codé en dur librement choisi (la valeur du caractère backtick) qui récupère la quasi-totalité des 8 bits.
dmckee --- chaton ex-modérateur

13
Ceci provient d'une publication que j'ai écrite il y a quelques années. vanillajava.blogspot.co.uk/2011/10/randomly-no-so-random.html
Peter Lawrey

Réponses:


917

Lorsqu'une instance de java.util.Randomest construite avec un paramètre de départ spécifique (dans ce cas -229985452ou -147909649), elle suit l'algorithme de génération de nombres aléatoires commençant par cette valeur de départ.

Chaque Randomconstruit avec la même graine générera à chaque fois le même schéma de nombres.


8
@Vulcan - le javadoc dit que la graine est de 48 bits. docs.oracle.com/javase/7/docs/api/java/util/Random.html . Et en plus, les graines réelles sont des valeurs de 32 bits.
Stephen C

80
Chaque élément de la séquence de nombres aléatoires est pris modulo 27, et il y a 6 éléments dans chacun de "hello\0"et "world\0". Si vous supposiez un générateur vraiment aléatoire, les chances seraient de 1 sur 27 ^ 6 (387 420 489) d'obtenir la séquence que vous cherchiez - c'est donc assez impressionnant mais pas tout à fait époustouflant!
Russell Borogove

17
@RussellBorogove: Mais avec ces cotes et 2 ^ 64 graines possibles, il y a environ 47,6 milliards de valeurs de graines qui donnent cette séquence. Il s'agit juste d'en trouver un.
dan04

8
@ dan04 - Je n'étais pas tout à fait disposé à faire cette estimation; en fonction de l'implémentation du PRNG, la taille du mot de départ peut ne pas être égale à la taille de l'état et les chemins de séquence peuvent ne pas être répartis également. Mais quand même, les chances sont définitivement bonnes, et si vous ne pouviez pas trouver une paire, vous pourriez réessayer avec un boîtier différent ( "Hello" "World"), ou utiliser à la 122-kplace de 96+k, ou ...
Russell Borogove

7
@ ThorbjørnRavnAndersen Le Javadoc spécifie que "des algorithmes particuliers sont spécifiés pour la classe Random. Les implémentations Java doivent utiliser tous les algorithmes présentés ici pour la classe Random, dans un souci de portabilité absolue du code Java."
FThompson

1137

Les autres réponses expliquent pourquoi, mais voici comment.

Étant donné une instance de Random:

Random r = new Random(-229985452)

Les 6 premiers nombres r.nextInt(27)générés sont:

8
5
12
12
15
0

et les 6 premiers nombres qui r.nextInt(27)génèrent donnés Random r = new Random(-147909649)sont:

23
15
18
12
4
0

Ajoutez ensuite ces nombres à la représentation entière du caractère `(qui est 96):

8  + 96 = 104 --> h
5  + 96 = 101 --> e
12 + 96 = 108 --> l
12 + 96 = 108 --> l
15 + 96 = 111 --> o

23 + 96 = 119 --> w
15 + 96 = 111 --> o
18 + 96 = 114 --> r
12 + 96 = 108 --> l
4  + 96 = 100 --> d

48
Pédantiquement, new Random(-229985452).nextInt(27)renvoie toujours 8.
user253751

1
@immibis pourquoi? je veux dire que Random () devrait retourner un nombre aléatoire à chaque fois, pas un ensemble de numéros ordonné fixe?
roottraveller

5
@rootTraveller Pour commencer, new Random()ne renvoie aucun numéro du tout.
user253751

2
Existe-t-il un moyen de calculer ces graines? Il doit y avoir une logique ... ou est-ce juste de la force brute.
Sohit Gore du

2
@SohitGore Étant donné que la valeur par défaut de Java Randomn'est pas sécurisée sur le plan cryptographique (je suis presque sûr qu'il s'agit d'un Twister de Mersenne, mais ne me citez pas à ce sujet), il est probablement possible de revenir en arrière de "Je veux ces chiffres" à "c'est la graines que j'utiliserais ". J'ai fait quelque chose de similaire avec le générateur congruentiel linéaire standard C.
Fund Monica's Lawsuit

280

Je vais juste le laisser ici. Quiconque a beaucoup de temps (CPU) à perdre, n'hésitez pas à expérimenter :) De plus, si vous avez maîtrisé un fork-join-fu pour que cette chose brûle tous les cœurs du processeur (seuls les threads sont ennuyeux, non?), Veuillez partager votre code. Je l'apprécierais grandement.

public static void main(String[] args) {
    long time = System.currentTimeMillis();
    generate("stack");
    generate("over");
    generate("flow");
    generate("rulez");

    System.out.println("Took " + (System.currentTimeMillis() - time) + " ms");
}

private static void generate(String goal) {
    long[] seed = generateSeed(goal, Long.MIN_VALUE, Long.MAX_VALUE);
    System.out.println(seed[0]);
    System.out.println(randomString(seed[0], (char) seed[1]));
}

public static long[] generateSeed(String goal, long start, long finish) {
    char[] input = goal.toCharArray();
    char[] pool = new char[input.length];
    label:
    for (long seed = start; seed < finish; seed++) {
        Random random = new Random(seed);

        for (int i = 0; i < input.length; i++)
            pool[i] = (char) random.nextInt(27);

        if (random.nextInt(27) == 0) {
            int base = input[0] - pool[0];
            for (int i = 1; i < input.length; i++) {
                if (input[i] - pool[i] != base)
                    continue label;
            }
            return new long[]{seed, base};
        }

    }

    throw new NoSuchElementException("Sorry :/");
}

public static String randomString(long i, char base) {
    System.out.println("Using base: '" + base + "'");
    Random ran = new Random(i);
    StringBuilder sb = new StringBuilder();
    for (int n = 0; ; n++) {
        int k = ran.nextInt(27);
        if (k == 0)
            break;

        sb.append((char) (base + k));
    }

    return sb.toString();
}

Production:

-9223372036808280701
Using base: 'Z'
stack
-9223372036853943469
Using base: 'b'
over
-9223372036852834412
Using base: 'e'
flow
-9223372036838149518
Using base: 'd'
rulez
Took 7087 ms

24
@OneTwoThree nextInt(27)signifie dans la plage [0, 26].
Eng.Fouad

30
@Vulcan La plupart des graines sont très proches de la valeur maximale, tout comme si vous sélectionnez des nombres aléatoires entre 1 et 1000, la plupart des nombres que vous finirez par sélectionner auront trois chiffres. Ce n'est pas surprenant, quand on y pense :)
Thomas

18
@Vulcan En fait, si vous faites le calcul, vous verrez qu'ils sont à peu près aussi proches de la valeur maximale que de zéro (je suppose que la graine est interprétée comme non signée dans le code de génération). Mais parce que le nombre de chiffres ne croît que de façon logarithmique avec la valeur réelle, le nombre semble vraiment proche quand il ne l'est vraiment pas.
Thomas

10
Très bonne réponse. Et pour les points bonus, pouvez-vous trouver une graine qui initialisera un aléatoire qui produira la séquence de 4 graines nécessaires à l'initialisation des aléas finaux?
Marek

13
@Marek: Je ne pense pas que les dieux pseudo aléatoires approuveraient un tel comportement.
Denis Tulskiy

254

Tout le monde ici a fait un excellent travail pour expliquer comment le code fonctionne et montrer comment vous pouvez construire vos propres exemples, mais voici une réponse théorique d'information montrant pourquoi nous pouvons raisonnablement nous attendre à une solution que la recherche par force brute trouvera finalement.

Les 26 lettres minuscules différentes forment notre alphabet Σ. Pour permettre de générer des mots de différentes longueurs, nous ajoutons en outre un symbole de terminaison pour produire un alphabet étendu Σ' := Σ ∪ {⊥}.

Soit αun symbole et X une variable aléatoire uniformément répartie Σ'. La probabilité d'obtenir ce symbole P(X = α)et son contenu d'information I(α)sont données par:

P (X = α) = 1 / | Σ '| = 1/27

I (α) = -log₂ [P (X = α)] = -log₂ (1/27) = log₂ (27)

Pour un mot ω ∈ Σ*et son ⊥-homologue terminé ω' := ω · ⊥ ∈ (Σ')*, nous avons

I (ω): = I (ω ') = | ω' | * log₂ (27) = (| ω | + 1) * log₂ (27)

Étant donné que le générateur de nombres pseudo-aléatoires (PRNG) est initialisé avec une graine 32 bits, nous pouvons nous attendre à la plupart des mots de longueur allant jusqu'à

λ = étage [32 / log₂ (27)] - 1 = 5

à générer par au moins une graine. Même si nous devions rechercher un mot à 6 caractères, nous réussirions quand même environ 41,06% du temps. Pas trop mal.

Pour 7 lettres, nous nous rapprochons de 1,52%, mais je ne l'avais pas réalisé avant de l'essayer:

#include <iostream>
#include <random>

int main()
{
    std::mt19937 rng(631647094);
    std::uniform_int_distribution<char> dist('a', 'z' + 1);

    char alpha;
    while ((alpha = dist(rng)) != 'z' + 1)
    {
        std::cout << alpha;
    }
}

Voir la sortie: http://ideone.com/JRGb3l


ma théorie de l'information est un peu faible mais j'aime cette preuve. quelqu'un peut-il m'expliquer la ligne lambda, clairement nous divisons le contenu informatif de l'un avec l'autre, mais pourquoi cela nous donne-t-il notre longueur de mot? comme je l'ai dit, je suis un peu rouillé, donc je m'excuse de demander l'évidence (NB est-ce que cela a quelque chose à voir avec la limite de Shannon -de la sortie du code)
Mike HR

1
@ MikeH-R La ligne lambda est l' I(⍵)équation réarrangée. I(⍵)est 32 (bits) et |⍵|se révèle être 5 (symboles).
iceman

67

J'ai écrit un programme rapide pour trouver ces graines:

import java.lang.*;
import java.util.*;
import java.io.*;

public class RandomWords {
    public static void main (String[] args) {
        Set<String> wordSet = new HashSet<String>();
        String fileName = (args.length > 0 ? args[0] : "/usr/share/dict/words");
        readWordMap(wordSet, fileName);
        System.err.println(wordSet.size() + " words read.");
        findRandomWords(wordSet);
    }

    private static void readWordMap (Set<String> wordSet, String fileName) {
        try {
            BufferedReader reader = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim().toLowerCase();
                if (isLowerAlpha(line)) wordSet.add(line);
            }
        }
        catch (IOException e) {
            System.err.println("Error reading from " + fileName + ": " + e);
        }
    }

    private static boolean isLowerAlpha (String word) {
        char[] c = word.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (c[i] < 'a' || c[i] > 'z') return false;
        }
        return true;
    }

    private static void findRandomWords (Set<String> wordSet) {
        char[] c = new char[256];
        Random r = new Random();
        for (long seed0 = 0; seed0 >= 0; seed0++) {
            for (int sign = -1; sign <= 1; sign += 2) {
                long seed = seed0 * sign;
                r.setSeed(seed);
                int i;
                for (i = 0; i < c.length; i++) {
                    int n = r.nextInt(27);
                    if (n == 0) break;
                    c[i] = (char)((int)'a' + n - 1);
                }
                String s = new String(c, 0, i);
                if (wordSet.contains(s)) {
                    System.out.println(s + ": " + seed);
                    wordSet.remove(s);
                }
            }
        }
    }
}

Je l'ai maintenant en arrière-plan, mais il a déjà trouvé suffisamment de mots pour un pangram classique:

import java.lang.*;
import java.util.*;

public class RandomWordsTest {
    public static void main (String[] args) {
        long[] a = {-73, -157512326, -112386651, 71425, -104434815,
                    -128911, -88019, -7691161, 1115727};
        for (int i = 0; i < a.length; i++) {
            Random r = new Random(a[i]);
            StringBuilder sb = new StringBuilder();
            int n;
            while ((n = r.nextInt(27)) > 0) sb.append((char)('`' + n));
            System.out.println(sb);
        }
    }
}

( Démo sur ideone. )

Ps. -727295876, -128911, -1611659, -235516779.


35

Cela m'a intrigué, j'ai exécuté ce générateur de mots aléatoires sur une liste de mots du dictionnaire. Plage: Integer.MIN_VALUE à Integer.MAX_VALUE

J'ai eu 15131 hits.

int[] arrInt = {-2146926310, -1885533740, -274140519, 
                -2145247212, -1845077092, -2143584283,
                -2147483454, -2138225126, -2147375969};

for(int seed : arrInt){
    System.out.print(randomString(seed) + " ");
}

Impressions

the quick browny fox jumps over a lazy dog 

7
Vous avez fait mon homme de jour: DI l'a essayé avec Long.Min / Max et a recherché les noms de mes collègues et n'a trouvé que peter: (peter 4611686018451441623 peter 24053719 peter -4611686018403334185 peter -9223372036830722089 peter -4611686017906248127 peter 521139687 peter 521139687 peter 521139687 p1 4611686017645756173 peter 781631731 peter 4611686019209019635 peter -9223372036073144077 peter -4611686017420317288 peter 1007070616 peter -9223372035847705192)
Marcel

25

La plupart des générateurs de nombres aléatoires sont, en fait, «pseudo aléatoires». Ce sont des générateurs congruentiels linéaires, ou LCG ( http://en.wikipedia.org/wiki/Linear_congruential_generator )

Les LCG sont assez prévisibles étant donné une semence fixe. Fondamentalement, utilisez une graine qui vous donne votre première lettre, puis écrivez une application qui continue de générer le prochain int (char) jusqu'à ce que vous frappiez la lettre suivante dans votre chaîne cible et notez combien de fois vous avez dû appeler le LCG. Continuez jusqu'à ce que vous ayez généré chaque lettre.


3
Quel est un exemple de générateur de nombres non pseudo aléatoires
chiliNUT

1
@chiliNUT Ces générateurs sont des gadgets externes. Une lampe électronique. Ou un bit mal écrit qui est lu 0 ou 1. Vous ne pouvez pas faire un générateur numérique pur de nombres aléatoires, les algorithmes numériques ne sont PAS aléatoires, ils sont absolument précis.
Gangnus

@chiliNUT De nombreux systèmes d'exploitation collectent l' entropie . Par exemple, sous Linux, vous pouvez utiliser l' /dev/urandomappareil pour lire des données aléatoires. Il s'agit cependant d'une ressource rare. Ainsi, ces données aléatoires sont normalement utilisées pour amorcer des PRNG.
Adrian W

@AdrianW Wikipedia dit qu'il urandomest toujours pseudo aléatoire en.wikipedia.org/wiki//dev/random
chiliNUT

1
Oui, mais il est cryptographiquement sécurisé, ce qui signifie que l'on ne peut pas faire d'attaques par force brute (comme pour trouver la graine de la séquence "aléatoire" "bonjour le monde") avec des séquences aléatoires créées à partir de /dev/random. L'article que j'ai cité ci-dessus dit que le noyau Linux génère l'entropie à partir des synchronisations du clavier, des mouvements de la souris et des synchronisations IDE et met les données de caractères aléatoires à la disposition des autres processus du système d'exploitation via les fichiers spéciaux / dev / random et / dev / urandom. Cela me laisse croire que c'est vraiment aléatoire. Peut-être que ce n'est pas tout à fait correct. Mais contient/dev/random au moins une certaine entropie.
Adrian W

23

Le multi-threading étant très simple avec Java, voici une variante qui recherche une graine en utilisant tous les cœurs disponibles: http://ideone.com/ROhmTA

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class SeedFinder {

  static class SearchTask implements Callable<Long> {

    private final char[] goal;
    private final long start, step;

    public SearchTask(final String goal, final long offset, final long step) {
      final char[] goalAsArray = goal.toCharArray();
      this.goal = new char[goalAsArray.length + 1];
      System.arraycopy(goalAsArray, 0, this.goal, 0, goalAsArray.length);
      this.start = Long.MIN_VALUE + offset;
      this.step = step;
    }

    @Override
    public Long call() throws Exception {
      final long LIMIT = Long.MAX_VALUE - this.step;
      final Random random = new Random();
      int position, rnd;
      long seed = this.start;

      while ((Thread.interrupted() == false) && (seed < LIMIT)) {
        random.setSeed(seed);
        position = 0;
        rnd = random.nextInt(27);
        while (((rnd == 0) && (this.goal[position] == 0))
                || ((char) ('`' + rnd) == this.goal[position])) {
          ++position;
          if (position == this.goal.length) {
            return seed;
          }
          rnd = random.nextInt(27);
        }
        seed += this.step;
      }

      throw new Exception("No match found");
    }
  }

  public static void main(String[] args) {
    final String GOAL = "hello".toLowerCase();
    final int NUM_CORES = Runtime.getRuntime().availableProcessors();

    final ArrayList<SearchTask> tasks = new ArrayList<>(NUM_CORES);
    for (int i = 0; i < NUM_CORES; ++i) {
      tasks.add(new SearchTask(GOAL, i, NUM_CORES));
    }

    final ExecutorService executor = Executors.newFixedThreadPool(NUM_CORES, new ThreadFactory() {

      @Override
      public Thread newThread(Runnable r) {
        final Thread result = new Thread(r);
        result.setPriority(Thread.MIN_PRIORITY); // make sure we do not block more important tasks
        result.setDaemon(false);
        return result;
      }
    });
    try {
      final Long result = executor.invokeAny(tasks);
      System.out.println("Seed for \"" + GOAL + "\" found: " + result);
    } catch (Exception ex) {
      System.err.println("Calculation failed: " + ex);
    } finally {
      executor.shutdownNow();
    }
  }
}

Pour Java noob comme moi, vous devez suffixer le numéro de sortie avec Let changer le type d'argument en long, c'est- randomString(long i)à- dire afin de jouer. :)
Fruit

21

Random renvoie toujours la même séquence. Il est utilisé pour mélanger les tableaux et autres opérations comme permutations.

Pour obtenir différentes séquences, il est nécessaire d'initialiser la séquence dans une certaine position, appelée "graine".

RandomSting obtient le nombre aléatoire dans la position i (seed = -229985452) de la séquence "random". Utilise ensuite le code ASCII pour les 27 caractères suivants dans la séquence après la position de départ jusqu'à ce que cette valeur soit égale à 0. Cela renvoie le "bonjour". La même opération est effectuée pour "monde".

Je pense que le code n'a pas fonctionné pour d'autres mots. Le gars qui a programmé qui connaît très bien la séquence aléatoire.

C'est un très bon code geek!


10
Je doute qu'il "connaisse très bien la séquence aléatoire". Plus probablement, il a juste essayé des milliards de graines possibles jusqu'à en trouver une qui fonctionnait.
dan04

24
@ dan04 Les vrais programmeurs n'utilisent pas simplement le PRNG, ils se souviennent par cœur de toute la période et énumèrent les valeurs selon les besoins.
Thomas

1
"Random renvoie toujours la même séquence" - mettez () après Random ou affichez-le sous forme de code. Sinon, la phrase est fausse.
Gangnus

14

Le principe est que la classe aléatoire construite avec la même graine va générer le même modèle de nombres à chaque fois.


12

Dérivée de la réponse de Denis Tulskiy , cette méthode génère la graine.

public static long generateSeed(String goal, long start, long finish) {
    char[] input = goal.toCharArray();
    char[] pool = new char[input.length];
    label:
        for (long seed = start; seed < finish; seed++) {
            Random random = new Random(seed);

            for (int i = 0; i < input.length; i++)
                pool[i] = (char) (random.nextInt(27)+'`');

            if (random.nextInt(27) == 0) {
                for (int i = 0; i < input.length; i++) {
                    if (input[i] != pool[i])
                        continue label;
                }
                return seed;
            }

        }

    throw new NoSuchElementException("Sorry :/");
}

10

Dans les documents Java, il s'agit d'une fonctionnalité intentionnelle lors de la spécification d'une valeur de départ pour la classe Random.

Si deux instances de Random sont créées avec la même graine et que la même séquence d'appels de méthode est effectuée pour chacune, elles généreront et renverront des séquences de nombres identiques. Afin de garantir cette propriété, des algorithmes particuliers sont spécifiés pour la classe Random. Les implémentations Java doivent utiliser tous les algorithmes présentés ici pour la classe Random, dans un souci de portabilité absolue du code Java.

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html

Curieusement cependant, on pourrait penser qu'il y a des problèmes de sécurité implicites à avoir des nombres «aléatoires» prévisibles.


3
C'est pourquoi le constructeur par défaut de Random"définit la graine du générateur de nombres aléatoires à une valeur très susceptible d'être distincte de toute autre invocation de ce constructeur" ( javadoc ). Dans l'implémentation actuelle, il s'agit d'une combinaison de l'heure actuelle et d'un compteur.
martin

En effet. Vraisemblablement, il existe des cas d'utilisation pratiques pour spécifier la valeur de départ initiale. Je suppose que c'est le principe de fonctionnement de ces télécommandes pseudo-aléatoires que vous pouvez obtenir (celles RSA?)
deed02392

4
@ deed02392 Bien sûr, il existe des cas d'utilisation pratiques pour spécifier une valeur de départ. Si vous simulez des données pour utiliser une approche de Monte Carlo pour résoudre un problème, c'est une bonne chose de pouvoir reproduire vos résultats. Définir une graine initiale est le moyen le plus simple de le faire.
Dason


3

Voici une amélioration mineure pour la réponse de Denis Tulskiy . Cela réduit le temps de moitié

public static long[] generateSeed(String goal, long start, long finish) {
    char[] input = goal.toCharArray();

    int[] dif = new int[input.length - 1];
    for (int i = 1; i < input.length; i++) {
        dif[i - 1] = input[i] - input[i - 1];
    }

    mainLoop:
    for (long seed = start; seed < finish; seed++) {
        Random random = new Random(seed);
        int lastChar = random.nextInt(27);
        int base = input[0] - lastChar;
        for (int d : dif) {
            int nextChar = random.nextInt(27);
            if (nextChar - lastChar != d) {
                continue mainLoop;
            }
            lastChar = nextChar;
        }
        if(random.nextInt(27) == 0){
            return new long[]{seed, base};
        }
    }

    throw new NoSuchElementException("Sorry :/");
}

1

Il s'agit de la graine d' entrée . La même graine donne les mêmes résultats tout le temps. Même si vous réexécutez votre programme encore et encore, c'est la même sortie.

public static void main(String[] args) {

    randomString(-229985452);
    System.out.println("------------");
    randomString(-229985452);

}

private static void randomString(int i) {
    Random ran = new Random(i);
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());

}

Production

-755142161
-1073255141
-369383326
1592674620
-1524828502
------------
-755142161
-1073255141
-369383326
1592674620
-1524828502
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.