Как я могу получить случайное время между Сейчас и некоторым предыдущим моментом времени (например, 1 час назад)? - PullRequest
5 голосов
/ 23 декабря 2009

Я пытаюсь создать метод расширения, который позволяет мне создавать случайное время между Сейчас и каким-то пользователем, запрашивающим исторический момент времени в форме TimeSpan.

Например: случайное время с настоящего момента до 1 часа назад.

Итак, я придумал следующий Random(..) метод расширения.

Я думал, что случайное начальное число НЕ статично, но если я называю этот метод БОЛЬШИМ и БЫСТРОМ (например, в цикле), то я думал, что начальное число (основанное на времени) на самом деле не случайно быстрые звонки. это правда? (кажется, когда я проверяю свои результаты)

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

Ответы [ 3 ]

9 голосов
/ 23 декабря 2009

Как уже говорили другие, проблема в том, что new Random() использует текущее время для формирования начального числа, и вы получаете одно и то же много раз.

По сути, вы хотите создать относительно немного экземпляров. Поскольку Random не является потокобезопасным, вам нужно ThreadStatic или ThreadLocal<T> - последний является новым для .NET 4.0. Вот пример класса StaticRandom (с использованием .NET 4.0), который позволяет использовать свойство Instance, чтобы получить действительный экземпляр для этого потока. Обратите внимание, что при инициализации типа счетчик устанавливается с текущего времени. Это тогда используется для последовательных семян.

using System;
using System.Threading;

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

Тогда вы можете просто использовать StaticRandom.Instance всякий раз, когда вам нужен экземпляр Random.

Теперь, чтобы вернуться к исходному вопросу, не совсем понятно, что делает ваш текущий метод расширения. Почему вы вообще используете DateTime.Today? Я подозреваю, что вы хотите что-то вроде:

public static DateTimeOffset Random(this DateTimeOffset value, TimeSpan timeSpan)
{
    double seconds = timeSpan.TotalSeconds * StaticRandom.Instance.NextDouble();

    // Alternatively: return value.AddSeconds(-seconds);
    TimeSpan span = TimeSpan.FromSeconds(seconds);
    return value - span;
}

Однако, это даст вам полностью случайное время - например, оно почти наверняка пройдет часть пути через миллисекунду. Это нормально, или вы действительно хотите, чтобы это было точное количество секунд (или минут, или часов) в зависимости от исходного времени?

7 голосов
/ 23 декабря 2009

Используйте Random объект, который создается / инициализируется один раз и , а не каждый раз, когда вызывается метод. Другой вариант - передать экземпляр Random в метод при его вызове.

Вы также можете создавать перегрузки, которые позволяют вам выполнить любой из указанных выше вариантов:

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;
0 голосов
/ 23 декабря 2009

Вы должны заполнить генератор случайных чисел. Хорошей практикой будет следующее:

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

Это должно сделать его более случайным для вас.

Вы также можете создать генератор Random как переменную статического класса, чтобы вам не приходилось каждый раз создавать его экземпляры.

...