Menyemai generator angka acak dalam Javascript


372

Apakah mungkin untuk menaburkan generator angka acak (Math.random) dalam Javascript?


tidak jelas apakah Anda ingin menaburnya sehingga Anda mendapatkan hasil yang sama berulang kali untuk uji coba yang berbeda atau apakah Anda ingin menaburnya dengan 'sesuatu yang unik' per pengguna untuk keacakan yang lebih baik di antara penggunaan.
simbo1905

2
Tidak, sayangnya itu tidak mungkin. jsrand adalah perpustakaan kecil yang saya tulis ketika saya membutuhkan PRNG yang dapat ditaburkan. Ada juga perpustakaan lain yang lebih rumit yang bisa Anda temukan di Google untuk itu.
Domenico De Felice

4
Menambah pertanyaan: bagaimana mungkin ide yang baik untuk menawarkan PRNG tanpa sarana untuk menanamkannya ?? Apakah ada alasan bagus untuk ini?
Alan

Jawaban:



159

CATATAN: Meskipun (atau lebih tepatnya, karena) ringkas dan elegan, algoritma ini sama sekali tidak berkualitas tinggi dalam hal keacakan. Cari misalnya yang tercantum dalam jawaban ini untuk hasil yang lebih baik.

(Awalnya diadaptasi dari ide cerdas yang disajikan dalam komentar untuk jawaban lain.)

var seed = 1;
function random() {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
}

Anda dapat mengatur seedmenjadi nomor apa saja, hindari saja nol (atau kelipatan Math.PI).

Keanggunan solusi ini, menurut saya, berasal dari tidak adanya angka "ajaib" (selain 10.000, yang mewakili tentang jumlah minimum digit yang harus Anda buang untuk menghindari pola aneh - lihat hasil dengan nilai 10 , 100 , 1000 ). Keringkasan juga bagus.

Ini sedikit lebih lambat daripada Math.random () (dengan faktor 2 atau 3), tapi saya percaya ini tentang secepat solusi lain yang ditulis dalam JavaScript.


20
Apakah ada cara untuk membuktikan RNG ini menghasilkan angka yang didistribusikan secara seragam? Secara eksperimental tampaknya: jsfiddle.net/bhrLT
Nathan Breit

6
6.000.000 ops / detik cukup cepat, saya tidak berencana menghasilkan lebih dari ~ 3.000.000 per klik. Bercanda, ini brilian.
AMK

59
-1, Ini bukan sampler seragam sama sekali - ini cukup bias terhadap 0 dan 1 (lihat jsfiddle.net/bhrLT/17 , yang mungkin memerlukan waktu beberapa saat untuk menghitung). Nilai berturut-turut berkorelasi - setiap 355 nilai, dan terlebih lagi setiap 710, saling terkait. Silakan gunakan sesuatu yang lebih hati-hati!
spencer nelson

37
Pertanyaannya bukan tentang membuat generator angka acak yang aman secara kriptografis, tetapi sesuatu yang bekerja dalam javascript, berguna untuk demo cepat, dll. Saya akan mengambil sesuatu yang cepat dan sederhana yang memberikan distribusi yang terlihat lebih baik dari jutaan angka acak untuk tujuan itu.
Jason Goemaat

15
Hati-hati. Math.sin () dapat memberikan hasil yang berbeda pada klien dan server. Saya menggunakan Meteor (menggunakan javascript pada klien & server).
Obiwahn

145

Saya telah mengimplementasikan sejumlah fungsi Pseudorandom number generator (PRNG) yang baik, pendek dan cepat dalam JavaScript biasa. Semuanya dapat diunggulkan dan memberikan angka kualitas yang baik.

Pertama-tama, berhati-hatilah untuk menginisialisasi PRNG Anda dengan benar. Sebagian besar generator di bawah ini tidak memiliki prosedur penghasil benih built-in (demi kesederhanaan), tetapi menerima satu atau lebih nilai 32-bit sebagai keadaan awal PRNG. Benih yang serupa (misalnya benih sederhana 1 dan 2) dapat menyebabkan korelasi pada PRNG yang lebih lemah, menghasilkan output yang memiliki sifat yang sama (seperti tingkat yang dihasilkan secara acak serupa). Untuk menghindari hal ini, praktik terbaik adalah menginisialisasi PRNG dengan benih yang didistribusikan dengan baik.

Untungnya, fungsi hash sangat baik dalam menghasilkan benih untuk PRNG dari string pendek. Fungsi hash yang baik akan menghasilkan hasil yang sangat berbeda bahkan ketika dua string serupa. Berikut ini contoh berdasarkan fungsi pencampuran MurmurHash3:

function xmur3(str) {
    for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++)
        h = Math.imul(h ^ str.charCodeAt(i), 3432918353),
        h = h << 13 | h >>> 19;
    return function() {
        h = Math.imul(h ^ h >>> 16, 2246822507);
        h = Math.imul(h ^ h >>> 13, 3266489909);
        return (h ^= h >>> 16) >>> 0;
    }
}

Setiap panggilan berikutnya ke fungsi kembali dari xmur3menghasilkan "acak" nilai hash 32-bit baru yang akan digunakan sebagai benih di PRNG a. Begini cara Anda menggunakannya:

// Create xmur3 state:
var seed = xmur3("apples");
// Output four 32-bit hashes to provide the seed for sfc32.
var rand = sfc32(seed(), seed(), seed(), seed());

// Output one 32-bit hash to provide the seed for mulberry32.
var rand = mulberry32(seed());

// Obtain sequential random numbers like so:
rand();
rand();

Atau, cukup pilih beberapa data dummy untuk mengisi benih, dan majukan generator beberapa kali (12-20 iterasi) untuk mencampur kondisi awal secara menyeluruh. Ini sering terlihat dalam implementasi referensi PRNG, tetapi ia membatasi jumlah keadaan awal.

var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();

Output dari fungsi-fungsi PRNG ini menghasilkan angka 32-bit positif (0 hingga 2 32 -1) yang kemudian dikonversi ke angka floating-point antara 0-1 (0 inklusif, 1 eksklusif) setara dengan Math.random(), jika Anda menginginkan angka acak untuk rentang tertentu, baca artikel ini di MDN . Jika Anda hanya menginginkan bit mentah, cukup hapus operasi pembagian akhir.

Hal lain yang perlu diperhatikan adalah keterbatasan JS. Angka hanya dapat mewakili seluruh bilangan bulat hingga resolusi 53-bit. Dan ketika menggunakan operasi bitwise, ini dikurangi menjadi 32. Ini membuatnya sulit untuk mengimplementasikan algoritma yang ditulis dalam C atau C ++, yang menggunakan angka 64-bit. Porting kode 64-bit membutuhkan shims yang dapat secara drastis mengurangi kinerja. Jadi demi kesederhanaan dan efisiensi, saya hanya mempertimbangkan algoritma yang menggunakan matematika 32-bit, karena secara langsung kompatibel dengan JS.

Sekarang, lanjutkan ke generator. (Saya memelihara daftar lengkap dengan referensi di sini )


sfc32 (Penghitung Cepat Sederhana)

sfc32 adalah bagian dari suite pengujian nomor acak PractRand (yang lolos tentu saja). sfc32 memiliki status 128-bit dan sangat cepat di JS.

function sfc32(a, b, c, d) {
    return function() {
      a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; 
      var t = (a + b) | 0;
      a = b ^ b >>> 9;
      b = c + (c << 3) | 0;
      c = (c << 21 | c >>> 11);
      d = d + 1 | 0;
      t = t + d | 0;
      c = c + t | 0;
      return (t >>> 0) / 4294967296;
    }
}

Mulberry32

Mulberry32 adalah generator sederhana dengan keadaan 32-bit, tetapi sangat cepat dan memiliki kualitas yang baik (penulis menyatakan bahwa ia lulus semua pengujian suite pengujian gjrand dan memiliki periode 2 32 penuh , tetapi saya belum memverifikasi).

function mulberry32(a) {
    return function() {
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t | 1);
      t ^= t + Math.imul(t ^ t >>> 7, t | 61);
      return ((t ^ t >>> 14) >>> 0) / 4294967296;
    }
}

Saya akan merekomendasikan ini jika Anda hanya membutuhkan PRNG yang sederhana namun layak dan tidak perlu miliaran angka acak (lihat masalah Ulang Tahun ).

xoshiro128 **

Pada Mei 2018, xoshiro128 ** adalah anggota baru keluarga Xorshift , oleh Vigna / Blackman (yang juga menulis xoroshiro, yang digunakan di Chrome). Ini adalah generator tercepat yang menawarkan status 128-bit.

function xoshiro128ss(a, b, c, d) {
    return function() {
        var t = b << 9, r = a * 5; r = (r << 7 | r >>> 25) * 9;
        c ^= a; d ^= b;
        b ^= c; a ^= d; c ^= t;
        d = d << 11 | d >>> 21;
        return (r >>> 0) / 4294967296;
    }
}

Para penulis mengklaim itu lulus tes keacakan dengan baik ( meskipun dengan peringatan ). Peneliti lain telah menunjukkan bahwa gagal beberapa tes di TestU01 (khususnya LinearComp dan BinaryRank). Dalam praktiknya, seharusnya tidak menyebabkan masalah ketika float digunakan (seperti implementasi ini), tetapi dapat menyebabkan masalah jika mengandalkan bit mentah yang rendah.

JSF (Jenkins Small Fast)

Ini adalah JSF atau 'smallprng' oleh Bob Jenkins (2007), pria yang membuat ISAAC dan SpookyHash . Itu melewati tes PractRand dan harus cukup cepat, meskipun tidak secepat SFC.

function jsf32(a, b, c, d) {
    return function() {
        a |= 0; b |= 0; c |= 0; d |= 0;
        var t = a - (b << 27 | b >>> 5) | 0;
        a = b ^ (c << 17 | c >>> 15);
        b = c + d | 0;
        c = d + t | 0;
        d = a + t | 0;
        return (d >>> 0) / 4294967296;
    }
}

LCG (alias Lehmer / Park-Miller RNG atau MCG)

LCG sangat cepat dan sederhana, tetapi kualitas keacakannya sangat rendah, sehingga penggunaan yang tidak benar dapat menyebabkan bug di program Anda! Meskipun demikian, ini jauh lebih baik daripada beberapa jawaban yang menyarankan untuk menggunakan Math.sinatau Math.PI! Ini adalah one-liner, yang bagus :).

var LCG=s=>()=>(2**31-1&(s=Math.imul(48271,s)))/2**31;

Implementasi ini disebut RNG standar minimal seperti yang diusulkan oleh Park – Miller pada tahun 1988 & 1993 dan diimplementasikan dalam C ++ 11 sebagai minstd_rand. Perlu diingat bahwa statusnya adalah 31-bit (31 bit memberi 2 miliar kemungkinan status, 32 bit memberikan dua kali lipat dari itu). Ini adalah tipe PRNG yang coba diganti oleh orang lain!

Ini akan berhasil, tapi saya tidak akan menggunakannya kecuali Anda benar - benar membutuhkan kecepatan dan tidak peduli dengan kualitas keacakan (apa sih yang acak?). Sangat cocok untuk permainan yang macet atau demo atau sesuatu. LCG menderita korelasi benih, jadi yang terbaik adalah membuang hasil pertama LCG. Dan jika Anda bersikeras menggunakan LCG, menambahkan nilai kenaikan dapat meningkatkan hasil, tetapi mungkin latihan sia-sia ketika ada pilihan yang jauh lebih baik.

Tampaknya ada pengganda lain yang menawarkan keadaan 32-bit (peningkatan ruang-ruang):

var LCG=s=>()=>(s=Math.imul(741103597,s)>>>0)/2**32;
var LCG=s=>()=>(s=Math.imul(1597334677,s)>>>0)/2**32;

Nilai LCG ini berasal dari: P. L'Ecuyer: Tabel Linear Congruential Generator dengan berbagai ukuran dan struktur kisi yang bagus, 30 April 1997.


5
Ini jawaban yang luar biasa. Saya yakin akan kembali ke ini.
DavidsKanal

1
Saya percaya nilai-nilai yang Anda kutip dari "Tables of Linear Congruential Generator ..." oleh Pierre L'ecuyer dapat melebihi ukuran integer maksimum dalam Javascript. Benih maksimum (2 ^ 32-1) * 741103597 ≈ 3e18, yang lebih besar dari ukuran maksimum JavaScript JavaScript ≈ 9e15. Saya pikir mengikuti nilai-nilai dari buku Pierre memiliki periode terbesar dalam batas asli: seed = (seed * 185852 + 1) % 34359738337.
Lachmanski

1
@Lachmanski benar, tetapi mereka terikat oleh 32-bit (dan Park-Miller 31-bit). Penggunaan Math.imulmemungkinkan untuk meluap seperti saat menggunakan perkalian dalam C pada bilangan bulat 32-bit. Apa yang Anda sarankan adalah LCG yang memanfaatkan ruang integer JS lengkap, yang tentunya juga merupakan area yang menarik untuk dijelajahi. :)
bryc

1
Ini luar biasa! Bisakah saya menyalin sfc32 Anda ke program LGPL?
user334639

4
@ blobber2 tidak yakin apa yang Anda maksud, tetapi kode asli dari sini (dengan yang lain): github.com/bryc/code/blob/master/jshash/PRNGs.md . kurang lebih inti di dalam repositori :-)
bryc

39

Tidak, tapi ini generator pseudorandom sederhana, sebuah implementasi Multiply-with-carry I yang diadaptasi dari Wikipedia (telah dihapus sejak):

var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = (123456789 + i) & mask;
    m_z = (987654321 - i) & mask;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
    m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
    m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
    var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
    result /= 4294967296;
    return result;
}

EDIT: memperbaiki fungsi seed dengan membuatnya reset m_z
EDIT2: Kelemahan implementasi serius telah diperbaiki


3
Adakah yang menguji fungsi ini untuk keacakannya?
Justin

3
Ini adalah generator acak multiply-with-carry (MWC) dengan periode yang cukup lama. Diadaptasi dari wikipedia, Penghasil Angka Acak
Michael_Scharf

10
The seedFungsi tidak me-reset acak generator, karena mz_zvariabel berubah ketika random()disebut. Oleh karena itu atur mz_z = 987654321(atau nilai lain apa pun) diseed
Michael_Scharf

Ketika saya menggunakannya dengan generator warna acak (HSL) saya, itu hanya menghasilkan warna hijau dan cyan. Generator acak asli menghasilkan semua warna. Jadi, itu tidak sama atau tidak berfungsi.
Tomas Kubes

@Michael_Scharf 1) Benih berubah m_w, bukan m_z. 2) Keduanya m_wdan m_zdiubah BERDASARKAN pada nilai sebelumnya, sehingga ia memodifikasi hasilnya.
ESL

26

Algoritma Antti Sykäri bagus dan pendek. Saya awalnya membuat variasi yang menggantikan Math.random Javascript ketika Anda memanggil Math.seed, tetapi kemudian Jason berkomentar bahwa mengembalikan fungsi akan lebih baik:

Math.seed = function(s) {
    return function() {
        s = Math.sin(s) * 10000; return s - Math.floor(s);
    };
};

// usage:
var random1 = Math.seed(42);
var random2 = Math.seed(random1());
Math.random = Math.seed(random2());

Ini memberi Anda fungsionalitas lain yang tidak dimiliki Javascript: beberapa generator acak independen. Itu sangat penting jika Anda ingin memiliki beberapa simulasi berulang yang berjalan pada saat yang sama.


3
Jika Anda mengembalikan fungsi alih-alih pengaturan Math.randomyang memungkinkan Anda memiliki beberapa generator independen, bukan?
Jason Goemaat

1
Pastikan untuk melihat komentar di atas tentang distribusi keacakan jika itu penting bagi Anda: stackoverflow.com/questions/521295/…
jocull

Bagaimana tebusan yang dihasilkan oleh ini dapat diulang? Itu terus memberikan nomor baru setiap kali
SMUsamaShah

setiap kali Anda melakukan Math.seed(42);itu me-reset fungsi, jadi jika Anda var random = Math.seed(42); random(); random();Anda mendapatkan 0.70..., kemudian 0.38.... Jika Anda mengatur ulang dengan menelepon var random = Math.seed(42);lagi, maka saat Anda menelepon lagi random()akan Anda dapatkan 0.70...lagi, dan kali berikutnya Anda akan mendapatkannya 0.38...lagi.
DITEMUKAN Belakangan

1
Tolong jangan gunakan ini. Luangkan waktu sebagai gantinya gunakan variabel lokal bernama randomalih-alih menimpa fungsi javascript asli. Timpa Math.randomdapat menyebabkan kompiler JIST untuk mengoptimalkan semua kode Anda.
Jack Giffin

11

Silakan lihat karya Pierre L'Ecuyer kembali ke akhir 1980-an dan awal 1990-an. Ada juga yang lain. Membuat generator bilangan acak (semu) sendiri, jika Anda bukan ahli, cukup berbahaya, karena ada kemungkinan besar hasilnya tidak acak secara statistik atau memiliki periode kecil. Pierre (dan lainnya) telah mengumpulkan beberapa generator angka acak yang baik (pseudo) yang mudah diimplementasikan. Saya menggunakan salah satu generator LFSR-nya.

https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf

Phil Troy


1
Jawaban bagus, tetapi tidak terkait dengan javascript :)
Nikolay Fominyh

3
Kode untuk mengimplementasikan karya Profesor L'Ecuyer tersedia untuk umum untuk java dan mudah diterjemahkan oleh sebagian besar programmer ke dalam Javascript.
user2383235

6

Menggabungkan beberapa jawaban sebelumnya, ini adalah fungsi acak unggulan yang Anda cari:

Math.seed = function(s) {
    var mask = 0xffffffff;
    var m_w  = (123456789 + s) & mask;
    var m_z  = (987654321 - s) & mask;

    return function() {
      m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask;
      m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask;

      var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
      result /= 4294967296;
      return result;
    }
}

var myRandomFunction = Math.seed(1234);
var randomNumber = myRandomFunction();

4
Ini menghasilkan hasil yang sangat mirip di awal urutan dengan biji yang berbeda. Misalnya, Math.seed(0)()mengembalikan 0.2322845458984375, dan Math.seed(1)()mengembalikan 0.23228873685002327. Mengubah keduanya m_wdan m_zmenurut benih tampaknya membantu. var m_w = 987654321 + s; var m_z = 123456789 - s;menghasilkan distribusi nilai pertama yang bagus dengan benih yang berbeda.
undefined

1
@memperbatasi masalah yang Anda jelaskan telah diperbaiki pada pengeditan terakhir, itu adalah bug dalam implementasi MWC.
bryc

Bekerja dengan baik sekarang, pada Januari 2020. Unggulan dengan 0, dapatkan 0,7322976540308446. Benih dengan 1, 0,16818441334180534, dengan 2: 0,6040864314418286, dengan 3: 0,03998844954185188. Terima kasih semuanya!
Eureka

3

Untuk menulis generator acak semu sendiri cukup sederhana.

Saran dari Dave Scotese berguna tetapi, seperti yang ditunjukkan oleh orang lain, tidak terdistribusi secara merata.

Namun, itu bukan karena argumen bilangan bulat tentang dosa. Itu hanya karena rentang dosa, yang kebetulan merupakan proyeksi satu dimensi dari sebuah lingkaran. Jika Anda mengambil sudut lingkaran, itu akan menjadi seragam.

Jadi alih-alih dosa (x) gunakan arg (exp (i * x)) / (2 * PI).

Jika Anda tidak menyukai urutan linier, campur sedikit dengan xor. Faktor sebenarnya tidak terlalu penting.

Untuk menghasilkan n angka acak semu, seseorang dapat menggunakan kode:

function psora(k, n) {
  var r = Math.PI * (k ^ n)
  return r - Math.floor(r)
}
n = 42; for(k = 0; k < n; k++) console.log(psora(k, n))

Harap perhatikan juga bahwa Anda tidak dapat menggunakan urutan acak semu ketika entropi nyata diperlukan.


Saya bukan ahli, tetapi biji berurutan mengikuti pola yang konstan . Pixel berwarna adalah> = 0,5. Saya menduga itu hanya iterasi jari-jari berulang-ulang?
bryc


1

Math.randomtidak, tetapi perpustakaan ran memecahkan ini. Ini memiliki hampir semua distribusi yang dapat Anda bayangkan dan mendukung generasi nomor acak unggulan. Contoh:

ran.core.seed(0)
myDist = new ran.Dist.Uniform(0, 1)
samples = myDist.sample(1000)

-1

Saya telah menulis fungsi yang mengembalikan nomor acak yang diunggulkan, menggunakan Math.sin untuk memiliki nomor acak yang panjang dan menggunakan benih untuk memilih angka dari itu.

Gunakan:

seedRandom("k9]:2@", 15)

itu akan mengembalikan nomor yang Anda seeded parameter pertama adalah nilai string apa pun; benihmu. parameter kedua adalah berapa banyak digit yang akan kembali.

     function seedRandom(inputSeed, lengthOfNumber){

           var output = "";
           var seed = inputSeed.toString();
           var newSeed = 0;
           var characterArray = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','!','@','#','$','%','^','&','*','(',')',' ','[','{',']','}','|',';',':',"'",',','<','.','>','/','?','`','~','-','_','=','+'];
           var longNum = "";
           var counter = 0;
           var accumulator = 0;

           for(var i = 0; i < seed.length; i++){
                var a = seed.length - (i+1);
                for(var x = 0; x < characterArray.length; x++){
                     var tempX = x.toString();
                     var lastDigit = tempX.charAt(tempX.length-1);
                     var xOutput = parseInt(lastDigit);
                     addToSeed(characterArray[x], xOutput, a, i); 
                }                  
           }

                function addToSeed(character, value, a, i){
                     if(seed.charAt(i) === character){newSeed = newSeed + value * Math.pow(10, a)}
                }
                newSeed = newSeed.toString();

                var copy = newSeed;
           for(var i=0; i<lengthOfNumber*9; i++){
                newSeed = newSeed + copy;
                var x = Math.sin(20982+(i)) * 10000;
                var y = Math.floor((x - Math.floor(x))*10);
                longNum = longNum + y.toString()
           }

           for(var i=0; i<lengthOfNumber; i++){
                output = output + longNum.charAt(accumulator);
                counter++;
                accumulator = accumulator + parseInt(newSeed.charAt(counter));
           }
           return(output)
      }

1
Urutan angka yang dihasilkan oleh ini tidak benar-benar mendekati sifat urutan angka acak. Hasilkan 15 angka dengannya dan string yang dihasilkan hampir selalu dimulai dengan 7 untuk hampir semua tombol, misalnya.
Gabriel

-2

Pendekatan sederhana untuk seed tetap:

function fixedrandom(p){
    const seed = 43758.5453123;
    return (Math.abs(Math.sin(p)) * seed)%1;
}

-6

Untuk angka antara 0 dan 100.

Number.parseInt(Math.floor(Math.random() * 100))

3
Pertanyaannya adalah tentang penyemaian Math.randomsedemikian rupa sehingga setiap kali Math.randomdisemai dengan benih yang sama, itu akan menghasilkan serangkaian nomor acak yang sama berturut-turut. Pertanyaan ini bukan, katakanlah, tentang penggunaan / demonstrasi aktual dari Math.random.
Jack Giffin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.