web-dev-qa-db-fra.com

Créer une séquence de nombres aléatoires sans répétition

Dupliquer:

Nombres aléatoires uniques dans O (1)?

Je veux un générateur de nombres pseudo aléatoires capable de générer des nombres sans répétition dans un ordre aléatoire.

Par exemple:

aléatoire (10)

pourrait revenir 5, 9, 1, 4, 2, 8, 3, 7, 6, 10

Existe-t-il un meilleur moyen de le faire autrement que de créer une plage de nombres et de les mélanger ou de vérifier la liste générée à la recherche de répétitions?


Modifier:

De plus, je souhaite qu’il soit efficace pour générer de grands nombres sans toute la gamme.


Modifier:

Je vois tout le monde suggérant des algorithmes de mélange. Mais si je veux générer un grand nombre aléatoire (1024 octets +), alors cette méthode prendrait beaucoup plus de mémoire que si je venais d'utiliser un RNG normal et inséré dans un ensemble jusqu'à ce qu'il soit d'une longueur spécifiée, n'est-ce pas? N'y a-t-il pas de meilleur algorithme mathématique pour cela?.

37
Unknown

Vous pourriez être intéressé par un registre à décalage à rétroaction linéaire ... Nous les construisions à partir de matériel, mais je les ai également réalisés avec un logiciel. Il utilise un registre à décalage avec certains des bits xor'ed et renvoyés à l'entrée, et si vous sélectionnez les "taps" appropriés, vous pouvez obtenir une séquence aussi longue que la taille du registre. C'est-à-dire qu'un lfsr 16 bits peut produire une séquence longue de 65 535 sans répétitions. C'est statistiquement aléatoire mais bien sûr éminemment reproductible. En outre, si cela est mal fait, vous pouvez obtenir des séquences embarrassantes. Si vous recherchez le lfsr, vous trouverez des exemples sur la manière de les construire correctement (c'est-à-dire "longueur maximale").

28
gbarry

Un remaniement est un excellent moyen de le faire (à condition de ne pas introduire de biais en utilisant l'algorithme naïf). Voir Mélange Fisher-Yates .

17
Mitch Wheat

Afin de s'assurer que la liste ne se répète pas, il devrait conserver une liste des numéros renvoyés précédemment. Comme il doit donc générer la liste complète d’ici la fin de l’algorithme, cela équivaut, pour le stockage requis, à générer la liste ordonnée puis à la réorganisation.

Plus d'informations sur le brassage ici: Création d'une liste ordonnée aléatoire à partir d'une liste ordonnée

Cependant, si la plage des nombres aléatoires est très grande mais que la quantité de nombres requise est petite (vous avez laissé entendre qu'il s'agit là de l'exigence réelle dans un commentaire), générez une liste complète et remuez-la, c'est un gaspillage. Un remaniement sur un vaste réseau implique l'accès à des pages de mémoire virtuelle d'une manière qui (par définition) vaincra le système de pagination du système d'exploitation (à une plus petite échelle, le même problème se produirait avec la mémoire cache de la CPU).

Dans ce cas, la recherche dans la liste jusque-là sera beaucoup plus efficace. L'idéal serait donc d'utiliser des heuristiques (déterminées par l'expérience) pour choisir la bonne implémentation pour les arguments donnés. (Excuses pour avoir donné des exemples en C # plutôt qu'en C++ mais ASFAC++ B je m'entraîne à penser en C #).

IEnumerable<int> GenerateRandomNumbers(int range, int quantity)
{
    int[] a = new int[quantity];

    if (range < Threshold)
    {
        for (int n = 0; n < range; n++)
            a[n] = n;

        Shuffle(a);
    }
    else
    {
        HashSet<int> used = new HashSet<int>();

        for (int n = 0; n < quantity; n++)
        {
            int r = Random(range);

             while (!used.Add(r))
                 r = Random(range);

             a[n] = r;
        }
    }

    return a;
}

Le coût de la vérification des nombres répétés, des boucles en cas de collisions, etc. sera coûteux, mais il y aura probablement une valeur Threshold où elle deviendra plus rapide que l'allocation pour toute la plage.

Pour des quantités suffisamment petites, il peut être plus rapide d’utiliser un tableau pour used et d’y effectuer des recherches linéaires, en raison de la plus grande localité, des frais généraux plus faibles, du coût peu élevé de la comparaison ...

De même, pour les grandes quantités ET les plages étendues, il peut être préférable de renvoyer un objet produisant les nombres de la séquence à la demande, au lieu d’allouer le tableau pour les résultats à l’avance. Ceci est très facile à implémenter en C # grâce au mot clé yield return:

IEnumerable<int> ForLargeQuantityAndRange(int quantity, int range)
{
    for (int n = 0; n < quantity; n++)
    {
        int r = Random(range);

        while (!used.Add(r))
            r = Random(range);

        yield return r;
    }
}
16
Daniel Earwicker

Si un nombre aléatoire est garanti pour ne jamais répéter, il n'est plus aléatoire et la quantité de randomness diminue à mesure que les nombres sont générés (après neuf chiffres, random(10) est plutôt prévisible et même après seulement huit, vous avez 50% de chance .

7
Motti

Je comprends que vous ne souhaitiez pas un mélange pour les grandes plages, car vous auriez à stocker toute la liste pour le faire. 

Utilisez plutôt un hachage pseudo-aléatoire réversible. Entrez ensuite les valeurs 0 1 2 3 4 5 6 etc.

Il y a un nombre infini de hachages comme celui-ci. Ils ne sont pas trop difficiles à générer s'ils sont limités à une puissance de 2, mais n'importe quelle base peut être utilisée.

En voici une qui fonctionnerait par exemple si vous vouliez parcourir toutes les valeurs 2 ^ 32 32 bits. Il est plus facile d’écrire car le mod implicite 2 ^ 32 de la mathématique entière joue à votre avantage dans ce cas.

unsigned int reversableHash(unsigned int x)
{
   x*=0xDEADBEEF;
   x=x^(x>>17);
   x*=0x01234567;
   x+=0x88776655;
   x=x^(x>>4);
   x=x^(x>>9);
   x*=0x91827363;
   x=x^(x>>7);
   x=x^(x>>11);
   x=x^(x>>20);
   x*=0x77773333;
   return x;
}
4
SPWorley

Si les propriétés aléatoires médiocres ne vous dérangent pas et si le nombre d'éléments le permet, vous pouvez utiliser un générateur de nombres aléatoires congruentiels linéaires .

3
starblue

Une lecture aléatoire est ce que vous pouvez faire de mieux pour des nombres aléatoires dans une plage spécifique sans répétition. La raison pour laquelle la méthode que vous décrivez (générer de manière aléatoire des nombres et les placer dans un ensemble jusqu'à ce que vous atteigniez une longueur spécifiée) est moins efficace, c'est à cause des doublons. Théoriquement, cet algorithme pourrait ne jamais finir. Au mieux, il se terminera dans un laps de temps indéterminé, par rapport à un brassage, qui se déroulera toujours dans un délai très prévisible.


Réponse aux modifications et commentaires:

Si, comme vous l'indiquez dans les commentaires, la plage de nombres est très large et que vous souhaitez en sélectionner relativement peu au hasard, sans répétition, la probabilité de répétition diminue rapidement. Plus la différence de taille entre la plage et le nombre de sélections est grande, plus il est probable que des sélections répétées soient répétées, et meilleures seront les performances de l'algorithme de sélection-vérification décrit dans la question.

3
Bill the Lizard

Qu'en est-il de l'utilisation du générateur GUID (comme dans celui de .NET). Certes, il n’est pas garanti qu’il n’y aura pas de doublons, mais les chances d’en obtenir un sont très faibles.

2
Rashack

Lorsque vous générez vos numéros, utilisez un filtre Bloom pour détecter les doublons. Cela utiliserait une quantité minimale de mémoire. Il ne serait pas du tout nécessaire de stocker les numéros précédents dans la série.

Le compromis est que votre liste ne peut pas être exhaustive dans votre gamme. Si vos chiffres sont vraiment de l’ordre de 256 ^ 1024, ce n’est guère un compromis.

(Bien sûr, s’ils sont aléatoires à cette échelle, même s’ennuyer de détecter les doublons est une perte de temps. Si chaque ordinateur sur Terre générait un billion de nombres aléatoires qui grossissent chaque seconde pendant des milliards d’années, le risque de collision est toujours négligeable.)

1
Imbue

J'appuie la réponse de gbarry sur l'utilisation d'un LFSR . Ils sont très efficaces et simples à mettre en œuvre même dans un logiciel et sont garantis pour ne pas répéter dans (2 ^ N - 1) utilisations pour un LFSR avec un registre à décalage à N bits.

Il existe cependant quelques inconvénients: en observant un petit nombre de sorties du RNG, il est possible de reconstruire le LFSR et de prédire toutes les valeurs qu'il générera, les rendant inutilisables pour la cryptographie et partout où un bon RNG est nécessaire. Le deuxième problème est que le mot tout à zéro ou le mot tout un (en termes de bits) n'est pas valide en fonction de l'implémentation de la LFSR. Le troisième problème qui concerne votre question est que le nombre maximal généré par la LFSR est toujours une puissance de 2 - 1 (ou une puissance de 2 - 2).

Le premier inconvénient peut ne pas être un problème selon votre application. D'après l'exemple que vous avez donné, il semble que vous ne vous attendiez pas à ce que zéro soit parmi les réponses; La deuxième question ne semble donc pas pertinente pour votre cas… .. Le problème de la valeur maximale (et donc de la plage) peut être résolu en réutilisant le LFSR jusqu'à ce que vous obteniez un nombre compris dans votre plage. Voici un exemple:

Supposons que vous souhaitiez avoir des nombres compris entre 1 et 10 (comme dans votre exemple). Vous utiliseriez un LFSR 4 bits qui a une plage [1, 15] incluse. Voici un pseudo-code indiquant comment obtenir un nombre compris dans l'intervalle [1,10]:

x = LFSR.getRandomNumber();
while (x > 10) {
   x = LFSR.getRandomNumber();
}

Vous devriez intégrer le code précédent dans votre RNG; de sorte que l'appelant ne se soucie pas de la mise en œuvre .. Notez que cela ralentirait votre GNA si vous utilisez un registre à décalage volumineux et que le nombre maximal que vous souhaitez n'est pas une puissance de 2 - 1.

1
gsarkis

Cela a été demandé auparavant - voir ma réponse à la question précédente . En un mot: vous pouvez utiliser un chiffrement par bloc pour générer une permutation sécurisée (aléatoire) sur n'importe quelle plage de votre choix, sans avoir à stocker la permutation complète à aucun moment.

1
Nick Johnson

Si vous souhaitez créer de grands nombres aléatoires (par exemple 64 bits ou plus) sans répétition, créez-les simplement. Si vous utilisez un bon générateur de nombres aléatoires, qui a en fait assez d'entropie, les chances de générer des répétitions sont si minimes qu'elles ne méritent pas de vous inquiéter. 

Par exemple, lors de la génération de clés cryptographiques, personne ne se soucie réellement de savoir s’il a déjà généré la même clé auparavant; puisque vous faites confiance à votre générateur de nombre aléatoire qu'un attaquant dédié ne sera pas en mesure de sortir la même clé, pourquoi vous attendriez-vous à ce que vous veniez avec la même clé accidentellement?

Bien sûr, si vous avez un mauvais générateur de nombres aléatoires (comme la vulnérabilité du générateur de nombres aléatoires Debian SSL ) ou si vous générez des nombres suffisamment petits, le paradoxe anniversaire vous donne une grande chance de collision vous devrez réellement faire quelque chose pour vous assurer que vous ne recevez pas de répétitions. Mais pour les grands nombres aléatoires avec un bon générateur, il suffit de faire confiance à la probabilité de ne pas vous donner de répétition.

1
Brian Campbell

Si vous ne voulez pas dire de mauvaises propriétés statistiques de la séquence générée, il existe une méthode:

Supposons que vous souhaitiez générer N nombres de 1024 bits chacun. Vous pouvez sacrifier certains bits du nombre généré pour en faire un "compteur".

Donc, vous générez chaque nombre aléatoire, mais dans certains bits que vous avez choisis, vous mettez un compteur codé binaire (à partir de variable, vous augmentez chaque fois que le prochain nombre aléatoire est généré).

Vous pouvez fractionner ce nombre en bits simples et le placer dans des bits moins significatifs du nombre généré.

De cette façon, vous êtes sûr d'obtenir un numéro unique à chaque fois.

Je veux dire par exemple que chaque numéro généré ressemble à ça:.

0
ajuc

Veuillez vérifier les réponses à

Génère une séquence d'entiers dans un ordre aléatoire sans construire la liste complète au début

et aussi ma réponse se trouve là comme

 very simple random is 1+((power(r,x)-1) mod p) will be from 1 to p for values of x from 1 to p and will be random where r and p are prime numbers and r <> p.
0
lakshmanaraj

En supposant que vous ayez un générateur de nombres aléatoires ou pseudo-aléatoires, même s'il n'est pas garanti de renvoyer des valeurs uniques, vous pouvez en implémenter un qui renvoie chaque fois des valeurs uniques à l'aide de ce code, en supposant que la limite supérieure reste constante (vous l'appellerez toujours avec random(10), et ne l'appelez pas avec random(10); random(11).

Le code ne vérifie pas les erreurs. Vous pouvez ajouter cela vous-même si vous le souhaitez.
Cela nécessite également beaucoup de mémoire si vous voulez une grande plage de nombres.

/* the function returns a random number between 0 and max -1
 * not necessarily unique
 * I assume it's written
 */
int random(int max);

/* the function returns a unique random number between 0 and max - 1 */
int unique_random(int max)
{

    static int *list = NULL;    /* contains a list of numbers we haven't returned */
    static int in_progress = 0; /* 0 --> we haven't started randomizing numbers
                                 * 1 --> we have started randomizing numbers
                                 */

    static int count;
    static prev_max = 0;

    // initialize the list
    if (!in_progress || (prev_max != max)) {
        if (list != NULL) {
            free(list);
        }
        list = malloc(sizeof(int) * max);
        prev_max = max;
        in_progress = 1;
        count = max - 1;

        int i;
        for (i = max - 1; i >= 0; --i) {
            list[i] = i;
        }
    }

    /* now choose one from the list */
    int index = random(count);
    int retval = list[index];

    /* now we throw away the returned value.
     * we do this by shortening the list by 1
     * and replacing the element we returned with
     * the highest remaining number
     */
    swap(&list[index], &list[count]);

    /* when the count reaches 0 we start over */
    if (count == 0) {
        in_progress = 0;
        free(list);
        list = 0;
    } else { /* reduce the counter by 1 */
        count--;
    }
}

/* swap two numbers */
void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
0
Nathan Fellman

Mélanger N éléments ne prend pas trop de mémoire ... réfléchissez-y. Vous n'échangez qu'un élément à la fois, la mémoire maximale utilisée est donc celle de N + 1 éléments.

0
Jason Punyon
static std::unordered_set<long> s;
long l = 0;
for(; !l && (s.end() != s.find(l)); l = generator());
v.insert(l);

générateur () étant votre générateur de nombre aléatoire. Vous obtenez des numéros tant que l'entrée n'est pas dans votre jeu, puis vous ajoutez ce que vous y trouvez. Vous avez eu l'idée. 

Je l'ai fait avec long pour l'exemple, mais vous devriez en faire un modèle si votre PRNG est modélisé.

La solution de rechange consiste à utiliser un PRNG cryptographiquement sécurisé qui aura une très faible probabilité de générer deux fois le même nombre.

0
Edouard A.

Pour qu'une séquence soit aléatoire, il ne devrait y avoir aucune corrélation automatique. La restriction selon laquelle les numéros ne doivent pas être répétés signifie que le numéro suivant doit dépendre de tous les numéros précédents, ce qui signifie qu'il n'est plus aléatoire ...

0
John Smith

Voici un moyen aléatoire de ne pas répéter les résultats. Cela fonctionne aussi pour les chaînes. Son en C # mais la logig devrait fonctionner dans de nombreux endroits. Mettez les résultats aléatoires dans une liste et vérifiez si le nouvel élément aléatoire est dans cette liste. Sinon, vous avez un nouvel élément aléatoire. Si c'est dans cette liste, répétez l'aléatoire jusqu'à ce que vous obteniez un élément qui ne figure pas dans cette liste.

List<string> Erledigte = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
    label1.Text = "";
    listBox1.Items.Add("a");
    listBox1.Items.Add("b");
    listBox1.Items.Add("c");
    listBox1.Items.Add("d");
    listBox1.Items.Add("e");
}

private void button1_Click(object sender, EventArgs e)
{
    Random Rand = new Random();
    int index=Rand.Next(0, listBox1.Items.Count);
    string rndString = listBox1.Items[index].ToString();

    if (listBox1.Items.Count <= Erledigte.Count)
    {
        return;
    }
    else
    {
        if (Erledigte.Contains(rndString))
        {
            //MessageBox.Show("vorhanden");
            while (Erledigte.Contains(rndString))
            {
                index = Rand.Next(0, listBox1.Items.Count);
                rndString = listBox1.Items[index].ToString();
            }
        }

        Erledigte.Add(rndString);
        label1.Text += rndString;
    }
}
0
Johnny

Supposons que vous vouliez générer une série de 256 nombres aléatoires sans répétition.

  1. Créez un bloc de mémoire de 256 bits (32 octets) initialisé avec des zéros, appelons-le b
  2. Votre variable en boucle sera n, le nombre de nombres restant à générer
  3. Boucle de n = 256 à n = 1
  4. Générer un nombre aléatoire r dans la plage [0, n)
  5. Trouvez le r- ème bit dans votre bloc de mémoire b, appelons-le p
  6. Mettez p dans votre liste de résultats, un tableau appelé q
  7. Basculez le p- ème bit du bloc mémoire b sur 1
  8. Après avoir passé le n = 1, vous avez fini de générer votre liste de numéros.

Voici un court exemple de ce dont je parle, en utilisant n = 4 au départ:

**Setup**
b = 0000
q = []

**First loop pass, where n = 4**
r = 2
p = 2
b = 0010
q = [2]

**Second loop pass, where n = 3**
r = 2
p = 3
b = 0011
q = [2, 3]

**Third loop pass, where n = 2**
r = 0
p = 0
b = 1011
q = [2, 3, 0]

** Fourth and final loop pass, where n = 1**
r = 0
p = 1
b = 1111
q = [2, 3, 0, 1]
0
William Brendel

J'ai déjà posé une question similaire auparavant, mais la mienne concernait toute la gamme d'un int see Recherche d'une fonction de hachage/Ordered Int/to/Shuffled Int /

0
David Allan Finch

Le problème consiste à sélectionner une séquence "aléatoire" de N nombres uniques de la plage 1..M, dans laquelle il n’ya aucune contrainte sur la relation entre N et M (M pourrait être beaucoup plus grand, à peu près le même ou même plus petit que N; ils peuvent ne pas être relativement prime). 

En développant la réponse du registre à décalage à retour linéaire: pour un M donné, construisez une LFSR maximale pour la plus petite puissance de deux supérieure à M. Puis prenez simplement vos nombres de la LFSR en jetant des nombres supérieurs à M. En moyenne, vous rejette au plus la moitié des nombres générés (étant donné que par construction, plus de la moitié de la portée de la LFSR est inférieure à M), le temps d'exécution attendu pour obtenir un nombre est égal à O (1). Vous ne stockez pas les numéros générés précédemment, de sorte que la consommation d'espace est également de O(1). Si vous faites un cycle avant d'obtenir N nombres, alors M inférieur à N (ou le LFSR est construit de manière incorrecte).

Vous pouvez trouver les paramètres de longueur maximale des LFSR jusqu'à 168 bits ici (de wikipedia): http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf

Voici du code Java:

/ ** * Générer une séquence de nombres "aléatoires" uniques dans [0, M) * @author dkoes * * /

classe publique UniqueRandom { long lfsr; masque long; long max;

private static long seed = 1;
//indexed by number of bits
private static int [][] taps = {
        null, // 0
        null, // 1
        null, // 2
        {3,2}, //3
        {4,3},
        {5,3},
        {6,5},
        {7,6},
        {8,6,5,4},
        {9,5},
        {10,7},
        {11,9},
        {12,6,4,1},
        {13,4,3,1},
        {14,5,3,1},
        {15,14},
        {16,15,13,4},
        {17,14},
        {18,11},
        {19,6,2,1},
        {20,17},
        {21,19},
        {22,21},
        {23,18},
        {24,23,22,17},
        {25,22},
        {26,6,2,1},
        {27,5,2,1},
        {28,25},
        {29,27},
        {30,6,4,1},
        {31,28},
        {32,22,2,1},
        {33,20},
        {34,27,2,1},
        {35,33},
        {36,25},
        {37,5,4,3,2,1},
        {38,6,5,1},
        {39,35},
        {40,38,21,19},
        {41,38},
        {42,41,20,19},
        {43,42,38,37},
        {44,43,18,17},
        {45,44,42,41},
        {46,45,26,25},
        {47,42},
        {48,47,21,20},
        {49,40},
        {50,49,24,23},
        {51,50,36,35},
        {52,49},
        {53,52,38,37},
        {54,53,18,17},
        {55,31},
        {56,55,35,34},
        {57,50},
        {58,39},
        {59,58,38,37},
        {60,59},
        {61,60,46,45},
        {62,61,6,5},
        {63,62},
};

//m is upperbound; things break if it isn't positive
UniqueRandom(long m)
{
    max = m;
    lfsr = seed; //could easily pass a starting point instead
    //figure out number of bits
    int bits = 0;
    long b = m;
    while((b >>>= 1) != 0)
    {
        bits++;
    }
    bits++;

    if(bits < 3)
        bits = 3; 

    mask = 0;
    for(int i = 0; i < taps[bits].length; i++)
    {
        mask |= (1L << (taps[bits][i]-1));
    }

}

//return -1 if we've cycled
long next()
{
    long ret = -1;
    if(lfsr == 0)
        return -1;
    do {
        ret = lfsr;
        //update lfsr - from wikipedia
        long lsb = lfsr & 1;
        lfsr >>>= 1;
        if(lsb == 1)
            lfsr ^= mask;

        if(lfsr == seed)            
            lfsr = 0; //cycled, stick

        ret--; //zero is stuck state, never generated so sub 1 to get it
    } while(ret >= max);

    return ret;
}

}

0
dkoes

Twister de mersenne

Description de ce qui peut être trouvé ici sur Wikipedia: Twister Mersenne

Regardez au bas de la page pour les implémentations dans différentes langues.

0
Indy9000

Cette réponse suggère quelques stratégies pour obtenir ce que vous voulez et pour vous assurer qu'elles sont dans un ordre aléatoire en utilisant des algorithmes déjà bien connus. 

Il existe une version inversée de l'algorithme de lecture aléatoire Fisher-Yates, appelée version de Durstenfeld, qui distribue de manière aléatoire des éléments acquis de manière séquentielle dans des tableaux et des collections lors du chargement du tableau ou de la collection. 

Une chose à garder à l'esprit est que le shuffle de Fisher-Yates (AKA Knuth) ou la version de Durstenfeld utilisée au moment du chargement est très efficace avec des tableaux d'objets, car seul le pointeur de référence sur l'objet est déplacé et l'objet lui-même n'est pas obligé de le faire. être examiné ou comparé à tout autre objet faisant partie de l'algorithme. 

Je donnerai les deux algorithmes plus bas. 

Si vous voulez des nombres aléatoires vraiment énormes, de l'ordre de 1024 octets ou plus, un très bon générateur aléatoire capable de générer des octets non signés ou des mots à la fois suffira. Générez aléatoirement autant d'octets que de mots dont vous avez besoin pour construire le nombre, transformez-le en objet avec un pointeur de référence, et hop, vous avez un très grand entier aléatoire. Si vous avez besoin d'une plage spécifique vraiment énorme, vous pouvez ajouter une valeur de base de zéro octet à l'extrémité inférieure de la séquence d'octets pour augmenter la valeur. Cela peut être votre meilleure option. 

Si vous devez éliminer les doublons de nombres aléatoires vraiment énormes, c'est plus compliqué. Même avec des nombres aléatoires vraiment énormes, supprimer les doublons les rend également biaisés et non aléatoires. Si vous avez un très grand nombre de nombres aléatoires énormes non dupliqués et que vous choisissez au hasard parmi ceux qui ne l'ont pas encore été, le biais n'est que le biais de la création de valeurs énormes pour le très grand nombre de nombres à choisir. Une version inversée de la version de Durstenfeld du Yates-Fisher pourrait être utilisée pour choisir au hasard des valeurs parmi un très grand nombre d'entre elles, les supprimer des valeurs restantes et les insérer dans un nouveau tableau qui est un sous-ensemble et pourrait le faire ceci avec juste les tableaux source et cible in situ. Ce serait très efficace. 

Cela peut être une bonne stratégie pour obtenir un petit nombre de nombres aléatoires avec des valeurs énormes d'un très grand nombre d'entre eux dans lequel ils ne sont pas dupliqués. Il suffit de choisir un emplacement aléatoire dans le jeu source, d’obtenir sa valeur, d’échanger sa valeur avec l’élément supérieur du jeu source, de réduire la taille du jeu source de un, puis de répéter avec le jeu source de taille réduite jusqu’à ce que vous ayez choisi suffisamment de valeurs. Il s’agit essentiellement de la version Durstenfeld de Fisher-Yates en sens inverse. Vous pouvez ensuite utiliser la version de Dursenfeld de l'algorithme de Fisher-Yates pour insérer les valeurs acquises dans l'ensemble de destination. Cependant, c'est exagéré, car ils devraient être choisis au hasard et ordonnés au hasard comme indiqué ici. 

Les deux algorithmes supposent que vous avez une méthode d'instance de nombre aléatoire, nextInt (int setSize), qui génère un entier aléatoire compris entre zéro et setSize, ce qui signifie qu'il existe des valeurs possibles pour setSize. Dans ce cas, ce sera la taille du tableau puisque le dernier index du tableau est size-1.

Le premier algorithme est la version de Durstenfeld de l'algorithme de shuffle de Fisher-Yates (aka Knuth) appliqué à un tableau de longueur arbitraire, qui positionne simplement de manière aléatoire des nombres entiers allant de 0 à la longueur du tableau dans le tableau. Le tableau n'a pas besoin d'être un tableau d'entiers, mais peut être un tableau d'objets acquis séquentiellement, ce qui en fait un tableau de pointeurs de référence. C'est simple, court et très efficace

int size = someNumber;
int[] int array = new int[size]; // here is the array to load
int location; // this will get assigned a value before used
// i will also conveniently be the value to load, but any sequentially acquired
// object will work
for (int i = 0; i <= size; i++) { // conveniently, i is also the value to load
      // you can instance or acquire any object at this place in the algorithm to load
      // by reference, into the array and use a pointer to it in place of j
      int j = i; // in this example, j is trivially i
    if (i == 0) { // first integer goes into first location
        array[i] = j; // this may get swapped from here later
    } else { // subsequent integers go into random locations
            // the next random location will be somewhere in the locations
            // already used or a new one at the end
            // here we get the next random location
            // to preserve true randomness without a significant bias
            // it is REALLY IMPORTANT that the newest value could be
            // stored in the newest location, that is, 
            // location has to be able to randomly have the value i
            int location = nextInt(i + 1); // a random value between 0 and i
            // move the random location's value to the new location
            array[i] = array[location];
            array[location] = j; // put the new value into the random location
    } // end if...else
} // end for

Voila, vous avez maintenant un tableau déjà randomisé.

Si vous voulez mélanger au hasard un tableau que vous avez déjà, voici l'algorithme standard de Fisher-Yates.

type[] array = new type[size];

// some code that loads array...

// randomly pick an item anywhere in the current array segment, 
// swap it with the top element in the current array segment,
// then shorten the array segment by 1
// just as with the Durstenfeld version above,
// it is REALLY IMPORTANT that an element could get
// swapped with itself to avoid any bias in the randomization
type temp; // this will get assigned a value before used
int location; // this will get assigned a value before used
for (int i = arrayLength -1 ; i > 0; i--) {
    int location = nextInt(i + 1);
    temp = array[i];
    array[i] = array[location];
    array[location] = temp;
} // end for

Pour les collections et les ensembles séquencés, c’est-à-dire certains types d’objets de la liste, vous pouvez simplement utiliser add/ou des insertions avec une valeur d’index vous permettant d’insérer des éléments n’importe où, mais vous devez autoriser l’ajout ou l’ajout après le dernier élément actuel pour éviter de créer un biais dans la randomisation. 

0
Jim