6
votes

Comment puis-je obtenir un temps aléatoire entre maintenant et un point de temps précédent (par exemple, il y a 1 heure)?

J'essaie de faire une méthode d'extension qui me permet de créer une durée aléatoire entre maintenant et que certains utilisateurs ont demandé le point de temps historique de la forme d'une période de temps.

Par exemple: Un temps aléatoire entre maintenant et il y a 1 heure.

Donc, j'ai proposé le suivant aléatoire (..) méthode d'extension.

Je pensais faire La graine aléatoire non statique, mais si j'appelle cette méthode beaucoup et rapidement (par exemple, dans une boucle), alors je pensais que la graine (qui est basée sur le temps) n'est pas vraiment aléatoire pour des appels vraiment rapides. Est-ce vrai? (Il semble que je vérifie mes résultats) xxx


0 commentaires

3 Réponses :


-1
votes

Vous devez semer le générateur de nombres aléatoires. Une bonne pratique serait de faire ce qui suit:

var random = new Random((int)DateTime.Now.Ticks);


8 commentaires

Mais si l'instance aléatoire est statique, tout résulterait-il pas le même? N'est-ce pas pourquoi vous voulez une nouvelle graine à chaque fois?


Non, vous n'avez pas besoin d'une nouvelle graine à chaque fois. Le RNG maintient son état d'un appel à un autre. Jetez un coup d'œil ici: en.wikipedia.org/wiki/pseudorandom_number_generator


Ce n'est pas une bonne pratique. C'est un mauvais pratique. Vous pouvez toujours finir par appeler cela plusieurs fois et obtenir la même graine à chaque fois. Utilisez une seule instance via une variable statique normale ne sera pas thread-coffre-fort - la classe aléatoire n'est pas du thread-Safe. Vous devez utiliser une instance par fil, comme indiqué par Luke.


Non, c'est exactement pourquoi vous ne voulez pas créer un nouveau à chaque fois et que vous ne voulez pas une nouvelle semence à chaque fois.


Le code que j'ai présenté était de montrer un meilleur moyen d'initialiser le RNG afin qu'il obtiendrait un numéro aléatoire différent. L'initialisation normale entraînera la même graine initiale et la même séquence de nombres à chaque fois. Le code que j'ai présentéais j'ai utilisé sans problème.


@ADAM: Non, le constructeur sans paramètre pour des utilisations aléatoires Environment.TickCount. Du Docs: "Initialise une nouvelle instance de la classe aléatoire, en utilisant une valeur de semences par défaut dépendante du temps."


@Adam: Vous dites - "Le code que j'ai présentéais j'ai utilisé sans problème plusieurs fois." Cela suggère simplement que vous n'avez pas utilisé ce code plusieurs fois dans une succession très rapide, ou vous n'avez pas remarqué que cela donne les mêmes résultats pour plusieurs appels presque simultanés. Il vous suggère également que vous n'avez pas remarqué que les documents du constructeur sans paramètre sont aléatoires. Il ne résout pas le problème que l'OP a rencontré. Vous voulez que je fournisse un exemple montrant votre code ne fonctionne pas? Je pourrais probablement l'adapter (non formaté) dans un commentaire ...


Échantillon de défaillance: utilisation du système; Utiliser System.Collections.Generic; test de classe {statique vide (chaîne [] args) {var list = nouvelle liste (); pour (int i = 0; i <100; i ++) {list.add (adamcode ()); } foreach (int x dans la liste) {console.writeline (x); }} static int adamcode () {var aléatoire = nouveau aléatoire ((int) datetime.now.ticks); retourner aléatoire.next (100); }}



7
votes

Utilisez un objet aléatoire code> créé / initialisé une fois em> et pas em> chaque fois que la méthode est appelée. Une autre option consiste à transmettre une instance aléatoire > aléatoire dans la méthode lorsque vous l'appelez.

Vous pouvez également créer des surcharges qui vous permettent de faire l'une des options ci-dessus: P>

public static DateTimeOffset Random(this DateTimeOffset value, TimeSpan timeSpan)
{
    if (_threadStaticRng == null)
        _threadStaticRng = new Random();

    return value.Random(timeSpan, _threadStaticRng);
}

public static DateTimeOffset Random(
    this DateTimeOffset value, TimeSpan timeSpan, Random rng)
{
    DateTimeOffset minDateTime = value.Subtract(timeSpan);
    int range = ((DateTime.Today - minDateTime)).Hours;
    return minDateTime.AddHours(rng.Next(range));
}

[ThreadStatic]
private static Random _threadStaticRng;


8 commentaires

À l'origine, j'ai eu l'instance aléatoire un objet statique, en utilisant un verrouillage double nul pour la protéger. Je n'en ai pas eu de fil statique, cependant. Cela signifie que j'ai utilisé le même code que vous l'avez fait, dans votre première méthode, sauf que j'ai eu le chèque null enveloppé dans une serrure et un autre wrapper de chèques nuls. Que fait Threadstatic, comparé à la fabrication statique?


@ Pure.krome: threadstatic garantit que chaque thread utilise un objet statique différent. Cela garantit la sécurité du thread sans avoir besoin de serrures, etc.


Ce n'est exactement ce que vous pensez. C'est statique par fil. Ce qui signifie que chaque thread se retrouvera avec sa propre instance.


La seule mauvaise chose à propos de ce code est que lorsqu'il crée une nouvelle instance, elle utilise toujours l'heure actuelle. Si beaucoup de threads commencent en même temps, ils obtiendront tous la même séquence. Je pense que je vais ajouter une réponse qui aborde ceci ...


Bravo Jon :) Et je sais que vous savez que vous savez tout à propos de la durée .. même si cela ne concerne pas le temps de noda que nous discutons :)


Une chose à noter que le semblait être transmis dans ma réponse, c'est que dans ce cas, le RNG instancierait toujours avec la même graine, donc si vous avez chargé l'application et que vous avez toujours exécuté les mêmes données dans le même ordre, vous obtiendriez les mêmes résultats. .


@Adam: Non, vous ne le feriez pas. Le constructeur utilise environnement.TickCount comme la graine si vous ne passez pas un.


@Luke: C'est étrange, mon mauvais alors. Pour une raison quelconque, l'application de test que je viens de jeter ensemble semblait toujours retourner le même nombre entre les points successifs.



9
votes

Comme d'autres personnes ont dit, le problème est que nouveau aléatoire () utilise l'heure actuelle pour former la graine, et vous obtenez la même chose de temps.

essentiellement que vous voulez créer relativement peu d'instances. Comme aléatoire n'est pas thread-coffre-fort, vous devez avoir besoin threadstatic ou threadlocal - ce dernier est nouveau à .NET 4.0. Voici un échantillon Staticrandom Classe (à l'aide de .NET 4.0) qui vous permet d'utiliser la propriété instance pour obtenir une instance valide pour ce fil. Notez que sur l'initialisation de type, un compteur est défini de l'heure actuelle. Ceci est ensuite utilisé pour les graines successives. xxx

alors vous pouvez simplement utiliser staticrandom.instance chaque fois que vous avez besoin d'une instance de aléatoire .

Pour revenir à la question initiale, il n'est pas tout à fait clair que votre méthode d'extension actuelle fait. Pourquoi utilisez-vous datetime.today du tout? Je soupçonne que tu veux que tu veux quelque chose comme: xxx

Cependant, cela vous donnera un complètement temps aléatoire - il est presque tenu de faire partie d'un milliseconde , par exemple. Est-ce que ça va, ou voulez-vous effectivement voulez-vous qu'il s'agisse d'un nombre exact de secondes (ou de minutes ou d'heures) en fonction de la période originale?


13 commentaires

@All, toute suggestion sur la rendant ma réponse meilleure?, Veuillez souligner les choses négatives qu'il a et aussi des suggestions pour l'améliorer, si vous obtenez du temps. Merci!


@Jon: Un temps totalement aléatoire est parfait, mais entre deux points de temps. Donc, si je vérifie votre code .. Dans la méthode aléatoire (..), si vous étiez à la place des secondes aléatoires (lecture: int) la valeur entre la valeur.TotalSeconds et tispan.totalseconds .. par exemple. Staticrandom.instance.next (Timespan.TotalSeconds, valeur.TotalSeconds) Deuxièmement, je ne sais pas pourquoi j'ai utilisé aujourd'hui . C'est horrible. J'utilise toujours maintenant . blush


@ Pure.krome: Non, ça fait déjà la bonne chose. Il s'agit d'une portée aléatoire dans la plage donnée et de la soustraide de la date / heure donnée. Donc, si vous fournissez «23 décembre, 10h39» et «1 heure», cela vous donnera du temps entre le 23 décembre de 9h39 et le 23 décembre 10h39.


@ Pure.krome: Pourquoi DateTime.Now serait-il pertinent non plus? Quelle pertinence le temps actuel a-t-il à votre méthode? Si c'est est impliqué alors je ne comprends pas ce que vous essayez de faire ...


@Jon: Quand je parle de maintenant je ne faisais que référence à un échantillon de temps que la variable valeur représentée. Il pourrait s'agir d'une valeur DateTimeOffset. Comme 23 à 10h39. Cela signifie que je suis vraiment blonde et que je ne comprends pas les deux premières lignes de code de votre méthode blush . Alors laissez-moi essayer de le comprendre (appologies d'être noob à cela). Utilisation de vos dates, la première ligne indique "Dec 23 à 9h39 (en secondes) * Un int aléatoire entre 1 <-> int32.maxvalue. Ensuite, vous convertissez ces secondes en une tempute. Puis tu prends 23 10:39 - Nouveau Timespan. Ce que je ne reçois pas, c'est la première ligne, à moins que je ne l'ai mal compris?


.. CONT. bien plus gros que les deux points de temps (éventuellement).


Non, il faut une heure en secondes. Il crée ensuite une période de temps aléatoire entre 0 et une heure et soustrait que du 23 décembre 10h39. 23 décembre 10h39 n'est pas une tempier, c'est une date d'heure. Une tempier est quelque chose comme "une heure" ou "5 minutes".


@ Pure.krome: Notez que de côté de la manière d'obtenir une instance de hasard, ma réponse est fonctionnellement équivalente à celle de Tanascius. J'aime créer le Timespan explicitement plutôt que d'utiliser addseconds , mais ils sont vraiment équivalents.


@Jon: ahh, je vous ai maintenant. Mais, er .. ce n'est pas comme ça que je lis votre code. Deuxièmement, le code jette une exception: "Timespan débordé parce que la durée est trop longue." ce qui a du sens si je comprends ce que vous faites. Pourquoi obtenez-vous le nombre de secondes, pendant 1 heure (ou toute période de trimestion .. 1 heure, 1 jour. 1 an ..), puis multiplier que (nombre de secondes) par un nombre aléatoire entre 0 <-> int.maxvalue? Je pensais que tu voulais quelque chose comme ça. Valeur . ?


Doh - Pas tout à fait, je voulais utiliser Random.NextDouble, qui donne une valeur comprise entre 0,0 et 0,1. Va le réparer.


Ahhhhhhhhhhh :) Ok. Cela fait beaucoup plus de sens? Dernière question ... est-elle mauvaise d'utiliser une distribution à la place? par exemple var secondes = staticrandom.instance.next ((((int) tispan.totalseconds); Je comprends que tout décimal soit tronqué. Mais je me demande si je manque aussi si je suis aussi absent sur quelque chose. (Je préfère toujours la solution suivante (), mais je veux juste demander à ce sujet, alors que j'ai votre aimable attention :)).


Nope, ce serait bien si longtemps que la Seconde totale ne dépassait pas int.maxvalue :) Vous voudrez peut-être ajouter 1 de sorte que la gamme finisse par être inclusive - si vous voulez une gamme sur un minute, vous voulez une valeur dans la plage [0, 60] que vous obtenez en appelant aléatoire.Next (61).


Merci beaucoup de travail pour m'aider avec ça et toutes les questions :) Je l'apprécie sincèrement. Joyeux Noël et bon moment avec la famille, etc. :) Cheer :)