Випадкова дата в C #


141

Я шукаю короткий, сучасний код C # для створення випадкової дати між 1 січня 1995 року та поточною датою.

Я думаю, що рішення, яке використовує Enumerable.Range якось може зробити це більш лаконічним.


Відповідь у випадковій даті
Час

Відповіді:


241
private Random gen = new Random();
DateTime RandomDay()
{
    DateTime start = new DateTime(1995, 1, 1);
    int range = (DateTime.Today - start).Days;           
    return start.AddDays(gen.Next(range));
}

Для кращої продуктивності, якщо це буде викликано неодноразово, створіть startі gen(а може, навіть range) змінні за межами функції.


1
Випадковий лише псевдовипадковий. Якщо вам потрібен справді випадковий вибір, спробуйте скористатися RNGCryptoServiceProvider з простору імен System.Security.Cryptography.
tvanfosson

Дякую tvanfosson. Псевдовипадкових достатньо для цієї проблеми.
Іуда Габріель Хіманго

5
Насправді, Random навіть не є особливо псевдовипадковим, якщо ви деякий час не тримаєте примірник і не отримуєте від нього значення.
Девід Мітчелл

2
Ось чому це лише зразок, а не виробничий код.
Джоел Куехорн

1
Так, це працює для мене; мій реальний код матиме випадковий випадок поза самим методом.
Іуда Габріель Хіманго

25

Це є незначною відповіддю на коментар Джоеля щодо створення трохи більш оптимізованої версії. Замість повернення випадкової дати безпосередньо, чому б не повернути функцію генератора, яку можна повторно викликати для створення випадкової дати.

Func<DateTime> RandomDayFunc()
{
    DateTime start = new DateTime(1995, 1, 1); 
    Random gen = new Random(); 
    int range = ((TimeSpan)(DateTime.Today - start)).Days; 
    return () => start.AddDays(gen.Next(range));
}

Чи можете ви пояснити, як це вигідно? Не могли натомість почати, ген та діапазон бути членами класу?
Марк А. Ніколосі

Вони могли і в цьому випадку є. Під кришкою це створить лексичне закриття, яке є класом, що містить початок, ген та діапазон як члени. Це просто більш стисло.
JaredPar

Хороша функція, я просто сподіваюся, що ніхто не збирається використовувати її як:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Aidiakapi

2
Як ця функція використовується, чи може хтось пояснити, будь ласка? Я маю на увазі, як я можу це назвати?
Бурак Каракуш

2
@ BurakKarakuş: Спочатку ти отримуєш завод, var getRandomDate = RandomDayFunc();потім називаєш його, щоб отримати випадкові дати: var randomDate = getRandomDate();Зверніть увагу, що вам потрібно повторно використовувати getRandomDate, щоб це було корисніше, ніж відповідь Джоела.
Şafak Gür

8

Я прийняв відповідь @Joel Coehoorn і вніс зміни, які він порадив - вивів змінну з методу і поставив усіх у класі. Плюс зараз час теж випадковий. Ось результат.

class RandomDateTime
{
    DateTime start;
    Random gen;
    int range;

    public RandomDateTime()
    {
        start = new DateTime(1995, 1, 1);
        gen = new Random();
        range = (DateTime.Today - start).Days;
    }

    public DateTime Next()
    {
        return start.AddDays(gen.Next(range)).AddHours(gen.Next(0,24)).AddMinutes(gen.Next(0,60)).AddSeconds(gen.Next(0,60));
    }
}

І приклад, як використовувати для написання 100 випадкових DateTimes для консолі:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}

Чому ви створюєте Random () двічі? Опинившись у оголошенні gen змін у класі та інший час у c-tor?
піксель

Так, раз достатньо. Я полагодив це.
Преспіш

1
Це приблизно в чотири рази швидше генерувати лише одну випадкову кількість секунд і додати це до вашої дати початку: range = (int)(DateTime.Today - start).TotalSeconds;і return start.AddSeconds(gen.Next(range));.
Юргій

5

Що ж, якщо ви запропонуєте альтернативну оптимізацію, ми також можемо перейти до ітератора:

 static IEnumerable<DateTime> RandomDay()
 {
    DateTime start = new DateTime(1995, 1, 1);
    Random gen = new Random();
    int range = ((TimeSpan)(DateTime.Today - start)).Days;
    while (true)
        yield return  start.AddDays(gen.Next(range));        
}

Ви можете використовувати його так:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}

1
Одне, що слід врахувати між ітератором та генераторною функцією, - це те, що рішення ітератора дасть значення, яке не може бути встановленим ідентифікатором. Це змушує абонента розпоряджатися або сплачувати ціну за те, що фіналізатор житиме в GC. Генератору не потрібне утилізація
JaredPar

2
@JaredPar, це не зовсім правильно. Тільки тому, що тип реалізує IDisposable, не означає, що він є доопрацьованим.
Дрю Ноакс

3

Почніть з об'єкта з фіксованою датою (1 січня 1995 р.) І додайте випадкову кількість днів за допомогою AddDays (явно зверніть увагу, не перевершуючи поточну дату).


Дякую Friol Я збирався запитати, як обмежити число, що передається випадковим чином. Джоел опублікував приклад із зразком коду, тому я позначу його відповідь як відповідь.
Іуда Габріель Хіманго

0

Я трохи запізнююся в грі, але ось одне рішення, яке працює чудово:

    void Main()
    {
        var dateResult = GetRandomDates(new DateTime(1995, 1, 1), DateTime.UtcNow, 100);
        foreach (var r in dateResult)
            Console.WriteLine(r);
    }

    public static IList<DateTime> GetRandomDates(DateTime startDate, DateTime maxDate, int range)
    {
        var randomResult = GetRandomNumbers(range).ToArray();

        var calculationValue = maxDate.Subtract(startDate).TotalMinutes / int.MaxValue;
        var dateResults = randomResult.Select(s => startDate.AddMinutes(s * calculationValue)).ToList();
        return dateResults;
    }

    public static IEnumerable<int> GetRandomNumbers(int size)
    {
        var data = new byte[4];
        using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(data))
        {
            for (int i = 0; i < size; i++)
            {
                rng.GetBytes(data);

                var value = BitConverter.ToInt32(data, 0);
                yield return value < 0 ? value * -1 : value;
            }
        }
    }

0

Невеликий метод, який повертає випадкову дату як рядок, заснований на деяких простих вхідних параметрах. Побудований на основі варіацій із наведених відповідей:

public string RandomDate(int startYear = 1960, string outputDateFormat = "yyyy-MM-dd")
{
   DateTime start = new DateTime(startYear, 1, 1);
   Random gen = new Random(Guid.NewGuid().GetHashCode());
   int range = (DateTime.Today - start).Days;
   return start.AddDays(gen.Next(range)).ToString(outputDateFormat);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.