8
votes

Émuler le hasard de Python dans .NET .NET

Module de Python 'Random' a une fonction aléatoire.choice code>

aléatoire.choice (SEQ) code> strong>

Renvoie un élément aléatoire de la séquence non vide SEQ. Si SEQ code> est vide, augmente indexerror code>. P> blockQuote>

Comment puis-je imiter cela dans .net ?net? P>

public T RandomChoice<T> (IEnumerable<T> source)
  • 'La séquence est trop longue pour économiser à la mémoire' li>
  • 'Vous ne pouvez faire que boucle sur la séquence une fois " li>
  • 'La séquence n'a pas de méthode de longueur / dénombrement' (Ã La .NET iEnumerable) li> ul> p>


4 commentaires

Vous dites que vous voulez une fonction qui vous retournerait exactement ce que Python fait ? Ou vous voulez une fonction avec le même contrat ? C'est-à-dire que vous seriez heureux si la fonction .NET renvoyait différents éléments de ce que Python serait?


Juste pour commenter les réponses fournies, @Matthickford, vous devriez peut-être envisager, en plus d'un ienumerable include un iList surcharge (ou un chèque dans le ienumerable si c'est un ilist ) afin que vous puissiez éviter d'énumérer et de créer une collection copiée. Edit: Vous pouvez également ajouter un paramètres surcharger pour extraire une liste à partir de la compilation: RandomChoice ("pomme", "poire", "orange")


Aakashm, je demande un analogue .NET de la fonction Python. Qu'est-ce qu'un contrat?


Oh, notre difficulté à communiquer est que les mots anglais «émulent», «analogique» et «contrat» sont surchargés avec des significations techniques précises. J'utilise les définitions de dictionnaire. Et par dictionnaire, je ne veux pas dire la structure de données!


7 Réponses :


0
votes

Eh bien, obtenez une liste de tous les éléments de la séquence. Demandez à un générateur de nombres aléatoires pour l'index, renvoyer Elemnt par index. Définissez quelle séquence est - iEnumerable serait la plus évidente, mais vous devez matérialiser cela dans une liste alors de connaître le nombre d'éléments pour le générateur de nombres aléatoires. C'est BTW., Pas imiter, il est implémenter.

est-ce une question de cours d'étude débutant de devoirs?


0 commentaires

2
votes
private static Random rng = new Random();

...
return source.Skip(rng.next(source.Count())).Take(1);

1 commentaires

1) N'oubliez pas le verrouillage. 2) Votre code nécessite plusieurs énumérations d'une séquence, qui devrait généralement être évitée. 3) prendre (1) renvoie une séquence d'éléments unique. Vous devez utiliser premier () à la place.



6
votes

Pour éviter d'itération à travers la séquence deux fois (une fois pour le compte et une fois pour l'élément), il est probablement une bonne idée de sauvegarder votre séquence dans un tableau avant d'obtenir son élément aléatoire:

public static class RandomExt {
    private static Random rnd = new Random();
    public static T RandomChoice<T> (this IEnumerable<T> source) {
        var arr = source.ToArray();   
        return arr[rnd.Next(arr.Length)];
    }
    public static T RandomChoice<T> (this ICollection<T> source) {
        return source[rnd.Next(rnd.Count)];
    }
}


6 commentaires

J'aime particulièrement cela comme une méthode d'extension. +1


Pourquoi ilist au lieu de icollection ?


Cela viole la contrainte 'La séquence est trop longue pour économiser en mémoire' si je remarque que vous avez probablement répondu avant que la contrainte ait été ajoutée ...


@Amitmittal Cette contrainte n'était pas là quand j'ai répondu à la question.


ahh ... juste quand je montageais le commentaire après avoir remarqué que :)


@dasblinkenlight, @codeinchaos: J'ai dit IList car icollection n'a pas l'indexer [index] Notation. Donc, votre code édité ne compilera plus. : P



2
votes
    public static T RandomChoice<T> (this IEnumerable<T> source)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }

        var list = source.ToList();

        if (list.Count < 1)
        {
            throw new MissingMemberException();
        }

        var rnd = new Random();
        return list[rnd.Next(0, list.Count)];
    }

0 commentaires

1
votes

J'irais avec La réponse de Dasblinkenlight , avec un petit changement: Tirer parti du fait que source peut déjà être une collection indexée, auquel cas vous n'avez vraiment pas besoin de remplir un nouveau tableau (ou une nouvelle liste): xxx

Notez que j'ai également modifié l'interface de la réponse susmentionnée Pour que cela soit plus cohérent avec la version Python que vous avez référencée dans votre question: xxx

edit : j'aime est encore meilleure, en fait.


0 commentaires

14
votes

Pour créer une méthode qui compte uniquement la source une seule fois et ne doit pas allouer la mémoire pour la stocker temporairement, vous comptez le nombre d'éléments que vous avez itératés et déterminer la probabilité que l'élément actuel soit le résultat: < Pré> xxx

Lorsque vous êtes au premier élément, la probabilité est 1/1 qu'elle doit être utilisée (comme c'est le seul élément que vous avez vu aussi loin). Lorsque vous êtes au second élément, la probabilité est de 1/2 qu'il devrait remplacer le premier élément, et ainsi de suite.


Ceci utilisera naturellement un peu plus de processeur, car il crée un hasard Nombre par article, pas seulement un seul nombre aléatoire pour sélectionner un élément, car Dasblinkenlight a souligné. Vous pouvez vérifier si la source implémente ilist , comme le suggéra Dan Tao et utilisez une implémentation qui utilise les fonctionnalités pour obtenir la longueur de la collecte et d'accéder aux éléments par index: < Pré> xxx

Remarque: vous devez envisager d'envoyer l'instance aléatoire dans la méthode. Sinon, vous obtiendrez la même graine aléatoire si vous appelez la méthode deux fois trop près du temps, car la graine est créée à partir de l'heure actuelle.


résultat d'un test, cueillette un chiffre À partir d'un tableau contenant 0 - 9, 1000000 fois, pour montrer que la distribution des numéros choisis n'est pas asymétrique: xxx


6 commentaires

Ah, gentil. Je me souviens de devoir faire quelque chose comme ça une fois! La chose est, je pense que vous devrez montrer aux mathématiques pour convaincre tous les lecteurs que c'est correct (ce n'est pas immédiatement intuitif que c'est correct-au moins pour moi).


Je suggérerais également d'optimiser encore pour le cas où source est un ilist , car il sera évidemment plus rapide d'accéder à un seul élément de manière aléatoire par l'indice que sur la liste si vous n'êtes pas obligé.


+1 très gentil! Voici un Lien vers une simple preuve par induction que cet algorithme choisit un élément aléatoire avec la probabilité de 1/1 . Vous voudrez peut-être noter que cet algorithme enregistre la mémoire de la variable TEMP à la charge d'utilisation de CPU supplémentaire pour générer des numéros aléatoires N au lieu d'une seule. Peu importe avec le RNG régulier, mais en utilisant une forte cryptographique peut transformer cela en un compromis.


Imo le passage de l'instance de aléatoire devrait faire partie de votre code actuel et non seulement une note plusieurs paragraphes ultérieurement.


@dasblinkenlight même des crypto prngs sont assez rapides. Certains sont inférieurs à 8 CPB et même le RNGCryptoServiceService relativement lent est à 40 CPB.


@CODEINCHAOS: Oui, peut-être. Je voulais montrer une méthode qui avait la signature exacte que dans la question.



0
votes

en supposant que l'on a une méthode d'extension ienumérable.minby : xxx

la méthode minby ne sauvegarder pas la séquence en mémoire, Cela fonctionne comme ienumerable.min faisant une itération (voir MORLINQ ou ailleurs )


1 commentaires

Ce n'est pas très différent de ce que @dasblinkenlight a suggéré. Cela implique également de créer un certain nombre de nombres aléatoires, puis de décider quand se terminer (bien que les chèques soient différents dans les deux cas).