Tanggal acak di C #

143

Saya mencari beberapa kode C # modern yang ringkas untuk menghasilkan tanggal acak antara 1 Jan 1995 dan tanggal sekarang.

Saya sedang memikirkan solusi yang memanfaatkan Enumerable. Rentang entah bagaimana dapat membuat ini lebih ringkas.

Judah Gabriel Himango
sumber
Jawaban dalam Tanggal Acak Waktu antara rentang - keluaran tidak terpadu memiliki metode pembantu dengan parameter tanggal Dari / Ke
Michael Freidgeim

Jawaban:

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

Untuk kinerja yang lebih baik jika ini akan dipanggil berulang kali, buat variabel startdan gen(dan mungkin bahkan range) di luar fungsi.

Joel Coehoorn
sumber
1
Acak hanyalah pseudo-random. Jika Anda benar-benar perlu acak, coba gunakan RNGCryptoServiceProvider dari namespace System.Security.Cryptography.
tvanfosson
Terima kasih tvanfosson. Pseudo-random cukup untuk masalah ini.
Judah Gabriel Himango
5
Sebenarnya, Random bahkan bukan pseudo-random kecuali Anda menyimpan instance untuk sementara dan terus mendapatkan nilai darinya.
David Mitchell
2
Itulah mengapa ini hanya contoh, bukan kode produksi.
Joel Coehoorn
1
Ya, itu berhasil untuk saya; kode dunia nyata saya akan memiliki contoh Acak di luar metode itu sendiri.
Judah Gabriel Himango
26

Ini adalah tanggapan kecil atas komentar Joel tentang membuat versi yang sedikit lebih dioptimalkan. Daripada mengembalikan tanggal acak secara langsung, mengapa tidak mengembalikan fungsi generator yang bisa dipanggil berulang kali untuk membuat tanggal acak.

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
sumber
Bisakah Anda menjelaskan bagaimana ini bermanfaat? Tidak dapat memulai, gen, dan rentang menjadi anggota kelas sebagai gantinya?
Mark A. Nicolosi
Mereka bisa dan dalam hal ini mereka. Di bawah tenda ini akan menghasilkan penutupan leksikal yang merupakan kelas yang berisi start, gen dan range sebagai anggota. Ini hanya lebih ringkas.
JaredPar
Fungsi yang bagus, saya hanya berharap tidak ada yang akan menggunakannya sebagai:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Aidiakapi
2
Bagaimana fungsi ini digunakan, bisakah seseorang menjelaskan? Maksud saya bagaimana saya bisa menyebutnya?
Burak Karakuş
2
@ BurakKarakuş: Anda mendapatkan pabrik terlebih dahulu: var getRandomDate = RandomDayFunc();kemudian Anda menyebutnya untuk mendapatkan tanggal acak: var randomDate = getRandomDate();Ingatlah bahwa Anda perlu menggunakan kembali getRandomDate agar ini lebih berguna daripada jawaban Joel.
Şafak Gür
9

Saya telah mengambil jawaban @Joel Coehoorn dan membuat perubahan yang dia sarankan - keluarkan variabel dari metode dan taruh semuanya di kelas. Ditambah sekarang waktunya juga acak. Inilah hasilnya.

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

Dan contoh bagaimana menggunakan untuk menulis 100 DateTimes acak ke konsol:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}
prespic
sumber
Mengapa Anda membuat Random () dua kali? Setelah di deklarasi gen variabel kelas dan waktu lain di c-tor?
piksel
Ya, sekali saja sudah cukup. Aku telah memperbaikinya.
prespic
1
Ini sekitar empat kali lebih cepat untuk menghasilkan hanya satu angka acak dari detik dan menambahkannya ke tanggal mulai Anda: range = (int)(DateTime.Today - start).TotalSeconds;dan return start.AddSeconds(gen.Next(range));.
Jurgen
5

Nah, jika Anda ingin menampilkan pengoptimalan alternatif, kami juga dapat menggunakan 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));        
}

Anda bisa menggunakannya seperti ini:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}
James Curran
sumber
1
Satu hal yang perlu diperhatikan antara fungsi iterator vs generator adalah bahwa solusi iterator akan menghasilkan nilai IDisposable. Ini memaksa penelepon untuk membuang atau membayar harga untuk memiliki finalizer yang tinggal di GC. Generator tidak perlu dibuang
JaredPar
2
@JaredPar, itu kurang tepat. Hanya karena suatu tipe mengimplementasikan IDisposable tidak berarti itu dapat diselesaikan.
Drew Noakes
3

Mulailah dengan objek tanggal tetap (Jan 1, 1995), dan tambahkan jumlah hari acak dengan AddDays (jelas-jelas, perhatikan tidak melebihi tanggal saat ini).

Gabriele D'Antona
sumber
Terima kasih Friol. Saya akan bertanya bagaimana membatasi nomor yang diteruskan menjadi acak. Joel telah memposting contoh dengan contoh kode, jadi saya akan menandai tanggapannya sebagai jawabannya.
Judah Gabriel Himango
0

Saya agak terlambat dalam permainan ini, tetapi berikut adalah satu solusi yang berfungsi dengan baik:

    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
sumber
0

Metode kecil yang mengembalikan tanggal acak sebagai string, berdasarkan beberapa parameter masukan sederhana. Dibangun berdasarkan variasi dari jawaban di atas:

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
sumber