Saya ingin menghasilkan angka acak unik antara 0 dan 1000 yang tidak pernah diulang (yaitu 6 tidak muncul dua kali), tetapi itu tidak menggunakan sesuatu seperti pencarian O (N) dari nilai sebelumnya untuk melakukannya. Apakah ini mungkin?
algorithm
math
random
language-agnostic
dicroce
sumber
sumber
O(n)
dalam waktu atau memori), maka banyak dari jawaban di bawah ini salah, termasuk jawaban yang diterima.Jawaban:
Inisialisasi array 1001 integer dengan nilai 0-1000 dan atur variabel, maks, ke indeks maks saat ini dari array (dimulai dengan 1000). Pilih angka acak, r, antara 0 dan maks, tukar angka di posisi r dengan angka di posisi max dan kembalikan angka sekarang di posisi maks. Mengurangi maks. 1 dan melanjutkan. Ketika max adalah 0, atur max kembali ke ukuran array - 1 dan mulai lagi tanpa perlu menginisialisasi ulang array.
Pembaruan: Meskipun saya menemukan metode ini sendiri ketika saya menjawab pertanyaan, setelah beberapa penelitian saya menyadari ini adalah versi modifikasi dari Fisher-Yates yang dikenal sebagai Durstenfeld-Fisher-Yates atau Knuth-Fisher-Yates. Karena uraiannya mungkin agak sulit untuk diikuti, saya telah memberikan contoh di bawah ini (menggunakan 11 elemen, bukan 1001):
Array dimulai dengan 11 elemen yang diinisialisasi ke array [n] = n, maks dimulai pada 10:
Pada setiap iterasi, angka acak r dipilih antara 0 dan maks, array [r] dan array [maks] ditukar, array baru [maks] dikembalikan, dan maks dikurangi:
Setelah 11 iterasi, semua angka dalam array telah dipilih, maks == 0, dan elemen array diacak:
Pada titik ini, maks dapat diatur ulang ke 10 dan proses dapat dilanjutkan.
sumber
N
iterasi (11 dalam contoh ini) untuk mendapatkan hasil yang diinginkan setiap kali berarti ituO(n)
? Karena Anda perlu melakukanN
iterasi untuk mendapatkanN!
kombinasi dari kondisi awal yang sama, jika tidak, output Anda hanya akan menjadi salah satu dari N status.Kamu bisa melakukan ini:
Jadi ini tidak memerlukan pencarian nilai-nilai lama setiap kali, tetapi masih membutuhkan O (N) untuk pengocokan awal. Tetapi seperti yang ditunjukkan Nils dalam komentar, ini diamortisasi O (1).
sumber
Gunakan Register Pergeseran Umpan Balik Linier Maksimal .
Ini dapat diimplementasikan dalam beberapa baris C dan pada saat runtime melakukan sedikit lebih dari beberapa tes / cabang, sedikit tambahan dan sedikit bergeser. Ini tidak acak, tetapi menipu kebanyakan orang.
sumber
Anda bisa menggunakan A Linear Congruential Generator . Di mana
m
(modulus) akan menjadi bilangan prima terdekat yang lebih besar dari 1000. Saat Anda mendapatkan angka di luar rentang, dapatkan yang berikutnya. Urutan hanya akan mengulangi setelah semua elemen terjadi, dan Anda tidak perlu menggunakan tabel. Berhati-hatilah dengan kelemahan generator ini (termasuk kurangnya keacakan).sumber
k
terpisah dalam urutan tidak pernah dapat terjadi bersama-sama).Anda bisa menggunakan Enkripsi Format-Pelestarian untuk mengenkripsi penghitung. Penghitung Anda hanya beranjak dari 0 ke atas, dan enkripsi menggunakan kunci pilihan Anda untuk mengubahnya menjadi nilai acak dari radix dan lebar apa pun yang Anda inginkan. Misalnya untuk contoh dalam pertanyaan ini: radix 10, lebar 3.
Cipher blok biasanya memiliki ukuran blok tetap misalnya 64 atau 128 bit. Tapi Enkripsi Format-Preserving memungkinkan Anda untuk mengambil cipher standar seperti AES dan membuat cipher-lebar yang lebih kecil, dari apa pun radix dan lebar yang Anda inginkan, dengan algoritma yang masih kuat secara kriptografi.
Dijamin tidak akan ada tabrakan (karena algoritma kriptografi membuat pemetaan 1: 1). Ini juga reversibel (pemetaan 2 arah), sehingga Anda dapat mengambil angka yang dihasilkan dan kembali ke nilai penghitung yang Anda mulai.
Teknik ini tidak memerlukan memori untuk menyimpan array shuffled dll, yang dapat menjadi keuntungan pada sistem dengan memori terbatas.
AES-FFX adalah salah satu metode standar yang diusulkan untuk mencapai ini. Saya telah bereksperimen dengan beberapa kode Python dasar yang didasarkan pada ide AES-FFX, meskipun tidak sepenuhnya sesuai - lihat kode Python di sini . Misalnya, mengenkripsi penghitung ke angka desimal 7 digit yang terlihat acak, atau angka 16-bit. Berikut adalah contoh radix 10, lebar 3 (untuk memberikan angka antara 0 dan 999 inklusif) seperti yang dinyatakan pertanyaan:
Untuk mendapatkan urutan pseudo-acak non-berulang yang berbeda, ubah kunci enkripsi. Setiap kunci enkripsi menghasilkan urutan pseudo-acak non-berulang yang berbeda.
sumber
k
terpisah dalam urutan tidak pernah dapat terjadi bersamaan).k
?1,2,...,N
dengan urutan nomor yang sama di beberapa urutan lain, tetapi masih konstan. Angka-angka tersebut kemudian ditarik dari urutan ini satu per satu.k
adalah jumlah nilai yang dipilih (OP tidak menentukan huruf untuk itu jadi saya harus memperkenalkannya).Untuk angka rendah seperti 0 ... 1000, buat daftar yang berisi semua angka dan mengocoknya lurus ke depan. Tetapi jika himpunan angka untuk menarik sangat besar ada cara lain yang elegan: Anda dapat membangun permutasi pseudorandom menggunakan kunci dan fungsi hash kriptografi. Lihat C ++ - ish contoh kode pseudo berikut:
Di sini,
hash
hanya beberapa fungsi acak semu yang memetakan string karakter ke integer unsigned yang mungkin besar. Fungsirandperm
ini adalah permutasi dari semua angka dalam 0 ... pow (2, bits) -1 dengan asumsi kunci tetap. Ini mengikuti dari konstruksi karena setiap langkah yang mengubah variabelindex
reversibel. Ini terinspirasi oleh cipher Feistel .sumber
hash()
, seperti yang digunakan dalam kode di atas, adalah fungsi pseudorandom yang aman, konstruksi ini akan terbukti (Luby & Rackoff, 1988) menghasilkan permutasi pseudorandom , yang tidak dapat dibedakan dari acak acak yang benar-benar menggunakan usaha yang jauh lebih sedikit daripada yang lengkap. mencari seluruh ruang kunci, yang eksponensial dalam panjang kunci. Bahkan untuk kunci berukuran wajar (katakanlah, 128 bit), ini berada di luar daya komputasi total yang tersedia di Bumi.hash( key + "/" + int2str(temp) )
konstruksi ad hoc di atas dengan HMAC , yang keamanannya dapat dibuktikan berkurang menjadi fungsi kompresi hash yang mendasarinya. Juga, menggunakan HMAC mungkin membuat kecil kemungkinannya bagi seseorang untuk secara keliru mencoba menggunakan konstruksi ini dengan fungsi hash non-crypto yang tidak aman.)Anda dapat menggunakan algoritma Xincrol saya yang dijelaskan di sini:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Ini adalah metode algoritmik murni untuk menghasilkan angka acak tetapi unik tanpa array, daftar, permutasi, atau beban CPU yang berat.
Versi terbaru juga memungkinkan untuk mengatur kisaran angka, Misalnya, jika saya ingin angka acak unik dalam kisaran 0-1073741821.
Saya praktis menggunakannya untuk
Ini terbuka, gratis. Cobalah...
sumber
k
terpisah dalam urutan tidak pernah dapat terjadi bersamaan).Anda bahkan tidak memerlukan array untuk menyelesaikannya.
Anda membutuhkan bitmask dan counter.
Inisialisasi penghitung ke nol dan tambahkan pada panggilan yang berurutan. XOR penghitung dengan bitmask (dipilih secara acak saat startup, atau diperbaiki) untuk menghasilkan nomor psuedorandom. Jika Anda tidak dapat memiliki angka yang melebihi 1000, jangan gunakan bitmask yang lebih lebar dari 9 bit. (Dengan kata lain, bitmask adalah bilangan bulat tidak di atas 511.)
Pastikan bahwa ketika penghitung melewati 1000, Anda meresetnya ke nol. Pada saat ini Anda dapat memilih bitmask acak lain - jika diinginkan - untuk menghasilkan rangkaian angka yang sama dalam urutan yang berbeda.
sumber
Saya pikir generator kongruensial Linear akan menjadi solusi paling sederhana.
dan hanya ada 3 pembatasan pada suatu , c dan m nilai-nilai
PS metode telah disebutkan tetapi posting memiliki asumsi yang salah tentang nilai konstan. Konstanta di bawah ini akan berfungsi dengan baik untuk kasus Anda
Dalam kasus Anda Anda dapat menggunakan
a = 1002
,c = 757
,m = 1001
sumber
Berikut ini beberapa kode yang saya ketikkan yang menggunakan logika solusi pertama. Saya tahu ini "agnostik bahasa" tetapi hanya ingin menyajikan ini sebagai contoh dalam C # jika ada yang mencari solusi praktis cepat.
sumber
Metode ini menghasilkan tepat ketika batasnya tinggi dan Anda hanya ingin menghasilkan beberapa angka acak.
Perhatikan bahwa angka-angka tersebut dihasilkan dalam urutan menaik, tetapi Anda dapat mengocoknya kemudian.
sumber
(top,n)=(100,10)
adalah:(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Saya diuji dengan Python, jadi sedikit perbedaan dalam matematika mungkin memainkan peran di sini (saya memang memastikan semua operasi untuk menghitungr
adalah floating-point).Anda bisa menggunakan generator angka pseudo-acak yang baik dengan 10 bit dan membuang 1001 ke 1023 meninggalkan 0 hingga 1000.
Dari sini kita mendapatkan desain untuk PRNG 10 bit ..
10 bit, umpan balik polinomial x ^ 10 + x ^ 7 + 1 (periode 1023)
gunakan Galois LFSR untuk mendapatkan kode cepat
sumber
N Angka acak yang tidak berulang akan menjadi O (n) rumit, seperti yang diperlukan.
Catatan: Acak harus statis dengan keamanan ulir diterapkan.
sumber
Katakanlah Anda ingin membahas daftar yang diacak berulang-ulang, tanpa
O(n)
penundaan setiap kali Anda memulai untuk mengacaknya lagi, dalam hal ini kita dapat melakukan ini:Buat 2 daftar A dan B, dengan 0 hingga 1000, membutuhkan
2n
ruang.Daftar acak A menggunakan Fisher-Yates, membutuhkan
n
waktu.Saat menggambar nomor, lakukan shuffle Fisher-Yates 1-langkah pada daftar lainnya.
Ketika kursor berada di ujung daftar, beralih ke daftar lainnya.
Praproses
Seri
sumber
[1,3,4,5,2]
akan menghasilkan hasil yang sama dengan pengocokan[1,2,3,4,5]
.Pertanyaannya Bagaimana Anda secara efisien menghasilkan daftar bilangan bulat non-berulang K antara 0 dan batas atas N dihubungkan sebagai duplikat - dan jika Anda menginginkan sesuatu yang O (1) per angka acak yang dihasilkan (tanpa O (n) biaya awal)) ada perubahan sederhana dari jawaban yang diterima.
Buat peta kosong yang tidak berurutan (peta yang kosong akan mengambil O (log k) per elemen) dari integer ke integer - alih-alih menggunakan array yang diinisialisasi. Tetapkan maks ke 1000 jika itu adalah maksimum,
Satu-satunya perbedaan dibandingkan dengan menggunakan array diinisialisasi adalah bahwa inisialisasi elemen ditunda / dilewati - tetapi akan menghasilkan angka yang sama persis dari PRNG yang sama.
sumber
Kemungkinan lain:
Anda dapat menggunakan berbagai flag. Dan ambil yang berikutnya ketika sudah dipilih.
Namun, waspadalah setelah 1000 panggilan, fungsi ini tidak akan pernah berakhir sehingga Anda harus membuat perlindungan.
sumber
Berikut ini beberapa contoh kode COBOL yang dapat Anda mainkan.
Saya dapat mengirimi Anda file RANDGEN.exe sehingga Anda dapat bermain dengannya untuk melihat apakah ia menginginkannya.
sumber
Sebagian besar jawaban di sini gagal menjamin bahwa mereka tidak akan mengembalikan nomor yang sama dua kali. Inilah solusi yang benar:
Saya tidak yakin batasannya ditentukan dengan baik. Satu mengasumsikan bahwa setelah 1000 output lainnya nilai diizinkan untuk diulang, tetapi bahwa secara naif memungkinkan 0 untuk mengikuti segera setelah 0 asalkan keduanya muncul di akhir dan mulai dari set 1000. Sebaliknya, sementara itu dimungkinkan untuk menjaga jarak dari 1000 nilai lain di antara pengulangan, melakukan hal itu memaksa situasi di mana urutan memutar ulang dengan cara yang persis sama setiap kali karena tidak ada nilai lain yang terjadi di luar batas itu.
Berikut adalah metode yang selalu menjamin setidaknya 500 nilai lain sebelum nilai dapat diulang:
sumber
Ketika N lebih besar dari 1000 dan Anda perlu menggambar sampel acak K Anda bisa menggunakan satu set yang berisi sampel sejauh ini. Untuk setiap undian, Anda menggunakan sampel penolakan , yang akan menjadi operasi "hampir" O (1), sehingga total waktu berjalan hampir O (K) dengan penyimpanan O (N).
Algoritma ini mengalami tabrakan ketika K "dekat" N. Ini berarti waktu berjalan akan jauh lebih buruk daripada O (K). Perbaikan sederhana adalah membalikkan logika sehingga, untuk K> N / 2, Anda menyimpan catatan semua sampel yang belum diambil. Setiap undian menghapus sampel dari set penolakan.
Masalah lain yang jelas dengan sampel penolakan adalah penyimpanan O (N), yang merupakan berita buruk jika N ada dalam miliaran atau lebih. Namun, ada algoritma yang memecahkan masalah itu. Algoritma ini disebut algoritma Vitter setelah penemunya. Algoritma dijelaskan di sini . Inti dari algoritma Vitter adalah bahwa setelah setiap pengundian, Anda menghitung lompatan acak menggunakan distribusi tertentu yang menjamin pengambilan sampel yang seragam.
sumber
Fisher Yates
Ini sebenarnya O (n-1) karena Anda hanya perlu satu swap untuk dua yang terakhir.
Ini adalah C #
sumber
Silakan lihat jawaban saya di https://stackoverflow.com/a/46807110/8794687
Ini adalah salah satu algoritma paling sederhana yang memiliki rata-rata waktu kompleksitas O ( s log s ), s yang menunjukkan ukuran sampel. Ada juga beberapa tautan di sana ke algoritma tabel hash yang kompleksitasnya diklaim sebagai O ( s ).
sumber
Seseorang memposting "membuat angka acak di excel". Saya menggunakan ideal ini. Buat struktur dengan 2 bagian, str.index dan str.ran; Untuk 10 angka acak, buat array 10 struktur. Atur str.index dari 0 hingga 9 dan str.ran ke nomor acak berbeda.
Urutkan array pada nilai-nilai di arr [i] .ran. Str.index sekarang dalam urutan acak. Di bawah ini adalah kode c:
sumber