Losowa data w C #

141

Szukam jakiegoś zwięzłego, nowoczesnego kodu C # do generowania losowej daty między 1 stycznia 1995 a bieżącą datą.

Myślę, że jakieś rozwiązanie wykorzystujące Enumerable.Range może w jakiś sposób uczynić to bardziej zwięzłym.

Judah Gabriel Himango
źródło
Odpowiedź w Random DateTime between range -
niezunifikowane

Odpowiedzi:

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));
}

Aby uzyskać lepszą wydajność, jeśli będzie to wywoływane wielokrotnie, utwórz zmienne startand gen(a może nawet range) poza funkcją.

Joel Coehoorn
źródło
1
Losowość jest tylko pseudolosowa. Jeśli potrzebujesz naprawdę losowości, spróbuj użyć RNGCryptoServiceProvider z przestrzeni nazw System.Security.Cryptography.
tvanfosson
Dzięki tvanfosson. Do tego problemu wystarczy pseudolosowość.
Judah Gabriel Himango
5
Właściwie, Random nie jest nawet szczególnie pseudolosowy, chyba że trzymasz instancję przez jakiś czas i nadal uzyskujesz z niej wartości.
David Mitchell
2
Dlatego jest to tylko próbka, a nie kod produkcyjny.
Joel Coehoorn
1
Tak, to działa dla mnie; mój kod świata rzeczywistego będzie miał instancję Random poza samą metodą.
Judah Gabriel Himango
25

Jest to niewielka odpowiedź na komentarz Joela dotyczący stworzenia nieco bardziej zoptymalizowanej wersji. Zamiast zwracać losową datę bezpośrednio, dlaczego nie zwrócić funkcji generatora, którą można wielokrotnie wywoływać, aby utworzyć losową datę.

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
źródło
Czy możesz wyjaśnić, jak to jest korzystne? Nie można zamiast tego zacząć, gen i range być członkami klasy?
Mark A. Nicolosi,
Mogliby iw tym przypadku są. Pod maską wygeneruje to zamknięcie leksykalne, które jest klasą zawierającą początek, gen i zakres jako członków. To jest po prostu bardziej zwięzłe.
JaredPar
Niezła funkcja, mam tylko nadzieję, że nikt jej nie użyje jako:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Aidiakapi
2
Jak jest używana ta funkcja, czy ktoś może wyjaśnić? Mam na myśli, jak mogę to nazwać?
Burak Karakuş
2
@ BurakKarakuş: Najpierw dostajesz fabrykę: var getRandomDate = RandomDayFunc();następnie dzwonisz do niej, aby uzyskać losowe daty: var randomDate = getRandomDate();Pamiętaj, że musisz ponownie użyć getRandomDate, aby było to bardziej przydatne niż odpowiedź Joela.
Şafak Gür
8

Wziąłem odpowiedź @Joel Coehoorn i wprowadziłem zmiany, które doradził - wyrzuciłem zmienną z metody i umieściłem wszystko w klasie. A teraz czas też jest losowy. Oto wynik.

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));
    }
}

I przykład, jak użyć do zapisania 100 losowych DateTimes na konsoli:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}
prespic
źródło
Dlaczego dwukrotnie tworzysz Random ()? Raz w klasie deklaracja gen zmiennej a innym razem w c-tor?
piksel
Tak, raz wystarczy. Naprawiłem to.
prespicion
1
Wygenerowanie tylko jednej losowej liczby sekund i dodanie jej do daty rozpoczęcia jest około cztery razy szybsze: range = (int)(DateTime.Today - start).TotalSeconds;i return start.AddSeconds(gen.Next(range));.
Jurgy,
5

Cóż, jeśli chcesz przedstawić alternatywną optymalizację, możemy również wybrać iterator:

 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));        
}

możesz tego użyć w ten sposób:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}
James Curran
źródło
1
Jedną rzeczą do rozważenia między iteratorem a funkcją generatora jest to, że rozwiązanie iteratora wygeneruje wartość IDisposable. Zmusza to dzwoniącego do pozbycia się lub zapłacenia ceny za umieszczenie finalizatora w GC. Generator nie wymaga utylizacji
JaredPar
2
@JaredPar, to nie do końca w porządku. Tylko dlatego, że typ implementuje IDisposable, nie oznacza, że ​​można go sfinalizować.
Drew Noakes
3

Zacznij od obiektu z ustaloną datą (1 stycznia 1995) i dodaj losową liczbę dni za pomocą AddDays (oczywiście, zwróć uwagę, aby nie przekroczyć bieżącej daty).

Gabriele D'Antona
źródło
Dzięki Friol. Miałem zapytać, jak ograniczyć liczbę przekazywaną do losowej. Joel opublikował przykład z przykładowym kodem, więc oznaczę jego odpowiedź jako odpowiedź.
Judah Gabriel Himango,
0

Jestem trochę za późno w grze, ale oto jedno rozwiązanie, które działa dobrze:

    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;
            }
        }
    }
Hamit Gündogdu
źródło
0

Mała metoda, która zwraca losową datę w postaci ciągu na podstawie kilku prostych parametrów wejściowych. Zbudowany na podstawie odchyleń od powyższych odpowiedzi:

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);
}
BernardV
źródło