การสร้างตัวสร้างตัวเลขสุ่มใน Javascript


372

เป็นไปได้หรือไม่ที่จะสร้างโปรแกรมสร้างตัวเลขสุ่ม (Math.random) ใน Javascript?


มันยังไม่ชัดเจนว่าคุณต้องการให้เมล็ดเพื่อให้คุณได้รับผลลัพธ์เดียวกันซ้ำ ๆ สำหรับการทดสอบที่แตกต่างกันหรือว่าคุณต้องการเมล็ดด้วย 'บางอย่างที่ไม่ซ้ำกัน' ต่อผู้ใช้สำหรับการสุ่มที่ดีขึ้นระหว่างการใช้งาน
simbo1905

2
ไม่น่าเสียดายที่เป็นไปไม่ได้ jsrandเป็นห้องสมุดเล็ก ๆ ที่ฉันเขียนเมื่อฉันต้องการ PRNG ที่เป็นเมล็ด นอกจากนี้ยังมีห้องสมุดที่มีความซับซ้อนมากขึ้นที่คุณสามารถค้นหา googling ได้
Domenico De Felice

4
การเพิ่มคำถาม: มันเป็นความคิดที่ดีที่จะเสนอ PRNG ได้อย่างไรโดยไม่ต้องมีวิธีการเพาะมัน? มีเหตุผลที่ดีสำหรับสิ่งนี้หรือไม่?
อลัน

คำตอบ:


188

ไม่มันไม่ใช่ แต่มันค่อนข้างง่ายในการเขียนตัวกำเนิดของคุณเองหรือดีกว่า แต่ใช้ตัวที่มีอยู่แล้ว เช็คเอาต์: คำถามที่เกี่ยวข้องนี้

ยังดูบล็อกเดวิด Bau สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเพาะ


159

หมายเหตุ: แม้จะมีความกระชับและความสง่างามที่ชัดเจน (หรือมากกว่านั้นก็ตาม) อัลกอริทึมนี้ไม่ได้มีคุณภาพสูงในแง่ของการสุ่ม ค้นหาเช่นที่อยู่ในคำตอบนี้เพื่อผลลัพธ์ที่ดีกว่า

(ดัดแปลงมาจากแนวคิดที่ฉลาดนำเสนอในความคิดเห็นเป็นคำตอบอื่น)

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

คุณสามารถตั้งค่าseedให้เป็นตัวเลขใด ๆ เพียงแค่หลีกเลี่ยงศูนย์ (หรือหลาย ๆ แบบของ Math.PI)

ในความคิดของฉันความสง่างามของการแก้ปัญหานี้มาจากการขาดหมายเลข "วิเศษ" (นอกเหนือจาก 10,000 ซึ่งแสดงถึงจำนวนขั้นต่ำของตัวเลขที่คุณต้องทิ้งเพื่อหลีกเลี่ยงรูปแบบแปลก ๆ - ดูผลลัพธ์ที่มีค่า10 , 100 , 1,000 ) ความกะทัดรัดก็ดีเช่นกัน

มันช้ากว่า Math.random () (ประมาณ 2 หรือ 3) แต่ฉันเชื่อว่ามันเร็วพอ ๆ กับโซลูชันอื่น ๆ ที่เขียนด้วย JavaScript


20
มีวิธีพิสูจน์ RNG นี้สร้างตัวเลขที่กระจายอย่างสม่ำเสมอหรือไม่ ดูเหมือนว่าจะเป็น: jsfiddle.net/bhrLT
Nathan Breit

6
6,000,000 ops / วินาทีค่อนข้างเร็วฉันไม่ได้วางแผนจะสร้างมากกว่า 3,000,000 ต่อคลิก ล้อเล่นนี่มันยอดเยี่ยม
AMK

59
-1, นี่ไม่ใช่ตัวอย่างเครื่องแบบเลย - มันค่อนข้างเอนเอียงไปทาง 0 และ 1 (ดูjsfiddle.net/bhrLT/17ซึ่งอาจใช้เวลาสักครู่ในการคำนวณ) ค่าที่สัมพันธ์กันนั้นมีความสัมพันธ์กัน - ทุก ๆ 355 ค่าและยิ่งกว่านั้นทุก ๆ 710 นั้นจะเกี่ยวข้องกัน โปรดใช้ความคิดอย่างระมัดระวังมากขึ้น!
spencer nelson

37
คำถามไม่ได้เกี่ยวกับการสร้างตัวสร้างตัวเลขสุ่มแบบเข้ารหัสความปลอดภัย แต่สิ่งที่ใช้งานได้ในจาวาสคริปต์มีประโยชน์สำหรับการสาธิตอย่างรวดเร็ว ฯลฯ ฉันจะใช้สิ่งที่ง่ายและรวดเร็วที่ให้การแจกแจงที่ดี
Jason Goemaat

15
ระวัง. Math.sin () สามารถให้ผลลัพธ์ที่แตกต่างกันในไคลเอนต์และเซิร์ฟเวอร์ ฉันใช้ Meteor (ใช้จาวาสคริปต์บนไคลเอนต์ & เซิร์ฟเวอร์)
Obiwahn

145

ฉันใช้งานตัวสร้างPseudorandom number (PRNG) จำนวนสั้น ๆ และรวดเร็วใน JavaScript ธรรมดา พวกเขาทั้งหมดสามารถเพาะและให้ตัวเลขที่มีคุณภาพดี

ก่อนอื่นให้ระมัดระวังในการเริ่มต้น PRNG ของคุณอย่างถูกต้อง เครื่องกำเนิดไฟฟ้าส่วนใหญ่ด้านล่างไม่มีกระบวนการสร้างเมล็ดพันธุ์ในตัว (เพื่อความเรียบง่าย) แต่ยอมรับค่า 32- บิตอย่างน้อยหนึ่งค่าเป็นสถานะเริ่มต้นของ PRNG เมล็ดที่คล้ายกัน (เช่นเมล็ดที่เรียบง่ายของ 1 และ 2) สามารถทำให้เกิดความสัมพันธ์ใน PRNG ที่อ่อนแอกว่าซึ่งส่งผลให้ผลผลิตมีคุณสมบัติที่คล้ายกัน (เช่นระดับที่สร้างแบบสุ่มจะคล้ายกัน) เพื่อหลีกเลี่ยงปัญหานี้เป็นวิธีที่ดีที่สุดในการเริ่มต้น PRNG ด้วยเมล็ดที่กระจายอย่างดี

โชคดีที่ฟังก์ชันแฮชดีในการสร้างเมล็ดสำหรับ PRNG จากสตริงสั้น ๆ ฟังก์ชันแฮชที่ดีจะสร้างผลลัพธ์ที่แตกต่างกันมากแม้ว่าจะมีสองสายเหมือนกันก็ตาม นี่คือตัวอย่างจากฟังก์ชั่นการผสมของ 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;
    }
}

แต่ละโทรตามมากับฟังก์ชั่นการกลับมาของxmur3ผลิตใหม่ "สุ่ม" ค่าแฮ 32 บิตเพื่อนำไปใช้เป็นเมล็ดพันธุ์ใน PRNG ได้ นี่คือวิธีที่คุณจะใช้:

// 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();

อีกวิธีหนึ่งคือเลือกข้อมูลจำลองเพื่อรองเมล็ดด้วยและเลื่อนตัวสร้างขึ้นสองสามครั้ง (การทำซ้ำ 12-20 ครั้ง) เพื่อผสมสถานะเริ่มต้นให้ละเอียด นี่มักจะเห็นในการใช้งานอ้างอิงของ PRNGs แต่จะ จำกัด จำนวนของสถานะเริ่มต้น

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

ผลลัพธ์ของฟังก์ชั่น PRNG เหล่านี้จะสร้างจำนวนบวก 32- บิต (0 ถึง 2 32 -1) ซึ่งจะถูกแปลงเป็นตัวเลขทศนิยมระหว่าง 0-1 (รวม 0, 1 พิเศษ) เทียบเท่ากับMath.random()ถ้าคุณต้องการตัวเลขสุ่ม ในช่วงที่เฉพาะเจาะจงให้อ่านบทความนี้ใน MDN หากคุณต้องการบิตดิบเพียงแค่ลบการดำเนินการหารสุดท้าย

สิ่งที่ควรทราบอีกประการหนึ่งคือข้อ จำกัด ของ JS ตัวเลขสามารถแสดงจำนวนเต็มทั้งหมดได้สูงสุดที่ความละเอียด 53 บิต และเมื่อใช้การดำเนินการระดับบิตค่านี้จะลดลงเป็น 32 ทำให้ยากที่จะใช้อัลกอริธึมที่เขียนใน C หรือ C ++ ซึ่งใช้ตัวเลข 64 บิต การย้ายรหัส 64 บิตต้องใช้ shims ที่สามารถลดประสิทธิภาพได้อย่างมาก ดังนั้นเพื่อความเรียบง่ายและมีประสิทธิภาพฉันจึงพิจารณาเฉพาะอัลกอริธึมที่ใช้คณิตศาสตร์แบบ 32 บิตเนื่องจากมันเข้ากันได้กับ JS โดยตรง

ตอนนี้เป็นต้นไปที่เครื่องกำเนิดไฟฟ้า (ฉันเก็บรักษารายการทั้งหมดไว้พร้อมอ้างอิงที่นี่ )


sfc32 (Simple Fast Counter)

sfc32เป็นส่วนหนึ่งของชุดทดสอบหมายเลขแบบสุ่มPractRand (ซึ่งผ่านการทดสอบแล้ว) sfc32 มีสถานะ 128 บิตและเร็วมากใน 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 เป็นเครื่องกำเนิดไฟฟ้าอย่างง่ายที่มีสถานะ 32 บิต แต่เร็วมากและมีคุณภาพดี (ผู้เขียนระบุว่ามันผ่านการทดสอบทั้งหมดของชุดการทดสอบgjrandและมีช่วงเต็มรูปแบบ 2 32แต่ฉันยังไม่ได้ยืนยัน)

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

ฉันจะแนะนำสิ่งนี้หากคุณต้องการเพียงPRNG ที่เรียบง่าย แต่เหมาะสมและไม่ต้องการตัวเลขสุ่มนับพันล้าน (ดูปัญหาวันเกิด )

xoshiro128 **

ตั้งแต่เดือนพฤษภาคมปี 2018 xoshiro128 **เป็นสมาชิกใหม่ของตระกูล Xorshiftโดย Vigna / Blackman (ผู้เขียน xoroshiro ซึ่งใช้ใน Chrome ด้วย) มันเป็นเครื่องกำเนิดที่เร็วที่สุดที่ให้สถานะ 128 บิต

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

ผู้เขียนอ้างว่าผ่านการทดสอบแบบสุ่มได้ดี ( แม้ว่าจะมีคำเตือน ) นักวิจัยคนอื่น ๆ ชี้ให้เห็นว่าล้มเหลวในการทดสอบใน TestU01 (โดยเฉพาะ LinearComp และ BinaryRank) ในทางปฏิบัติมันไม่ควรทำให้เกิดปัญหาเมื่อมีการใช้ลอยตัว (เช่นการใช้งานเหล่านี้) แต่อาจทำให้เกิดปัญหาหากใช้บิตเรตต่ำ

JSF (Jenkins 'Small Fast)

นี่คือ JSF หรือ 'smallprng' โดยบ๊อบเจนกินส์ (2007), คนที่ทำISAACและSpookyHash มันผ่านการทดสอบ PractRand และควรจะค่อนข้างเร็วแม้ว่าจะไม่เร็วเท่า 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 (aka Lehmer / Park-Miller RNG หรือ MCG)

LCG เป็นอย่างมากที่ง่ายและรวดเร็ว แต่คุณภาพของการสุ่มของมันเป็นอย่างต่ำที่ใช้ที่ไม่เหมาะสมจริงสามารถทำให้เกิดข้อบกพร่องในโปรแกรมของคุณ! อย่างไรก็ตามมันดีกว่าคำตอบบางอย่างที่แนะนำให้ใช้Math.sinหรือMath.PI! มันเป็นหนึ่งในสายการบินที่ดี :)

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

การดำเนินการนี้เรียกว่ามาตรฐานน้อยที่สุด RNG ที่เสนอโดยปาร์คมิลเลอร์ในปี 1988 และ 1993และดำเนินการใน C ++ minstd_rand11 โปรดทราบว่าสถานะเป็น 31 บิต (31 บิตให้สถานะเป็นไปได้ 2 พันล้านรัฐและ 32 บิตมอบเป็นสองเท่า) นี่คือ PRNG ประเภทที่ผู้อื่นพยายามแทนที่!

มันจะใช้งานได้ แต่ฉันจะไม่ใช้มันถ้าคุณไม่ต้องการความเร็วและไม่สนใจคุณภาพของการสุ่ม ยอดเยี่ยมสำหรับเกมติดขัดหรือตัวอย่างหรืออะไรบางอย่าง LCG ต้องทนทุกข์จากสหสัมพันธ์ของเมล็ดดังนั้นจึงเป็นการดีที่สุดที่จะทิ้งผลแรกของ LCG และถ้าคุณยืนยันในการใช้ LCG การเพิ่มค่าที่เพิ่มขึ้นอาจปรับปรุงผลลัพธ์ แต่อาจเป็นการออกกำลังกายที่ไร้ประโยชน์เมื่อมีตัวเลือกที่ดีกว่ามาก

ดูเหมือนว่าจะมีตัวคูณอื่น ๆ เสนอรัฐ 32 บิต (เพิ่มพื้นที่รัฐ):

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

ค่า LCG เหล่านี้มาจาก: P. L'Ecuyer: ตารางของเครื่องกำเนิดไฟฟ้าเชิงเส้นตรงขนาดต่าง ๆ และโครงสร้างโครงตาข่ายที่ดีวันที่ 30 เมษายน 1997


5
นี่คือคำตอบที่น่าอัศจรรย์ ฉันแน่ใจว่าจะกลับมาที่นี่อีก
DavidsKanal

1
ฉันเชื่อว่าค่าที่คุณยกมาจาก "Tables of Linear Congruential Generators ... " โดย Pierre L'ecuyer อาจเกินขนาดเต็มจำนวนสูงสุดใน Javascript จำนวนสูงสุดของ (2 ^ 32-1) * 741103597 ≈ 3e18 ซึ่งมากกว่าขนาดสูงสุดของ JavaScript เท่ากับ≈ 9e15 seed = (seed * 185852 + 1) % 34359738337ผมคิดว่าค่าต่อไปนี้มาจากหนังสือของปิแอร์มีระยะเวลาที่ใหญ่ที่สุดภายในวงเงินพื้นเมือง:
Lachmanski

1
@Lachmanski จริง แต่สิ่งนั้นถูกผูกไว้ด้วย 32- บิต (และ Park-Miller 31-bits) การใช้Math.imulช่วยให้มันล้นเช่นเดียวกับเมื่อใช้การคูณใน C กับจำนวนเต็ม 32 บิต สิ่งที่คุณแนะนำคือ LCG ใช้พื้นที่เต็มจำนวนเต็มของ JS ซึ่งเป็นพื้นที่ที่น่าสนใจในการสำรวจเช่นกัน :)
bryc

1
นี่มันเจ๋งมาก! ฉันสามารถคัดลอก sfc32 ของคุณไปยังโปรแกรม LGPL ได้หรือไม่
user334639

4
@ blobber2 ไม่แน่ใจว่าสิ่งที่คุณหมายถึง แต่รหัสเดิมคือจากที่นี่ (กับคนอื่น ๆ ): github.com/bryc/code/blob/master/jshash/PRNGs.md ส่วนสำคัญมากขึ้นหรือน้อยลงในที่เก็บ :-)
bryc

39

ไม่ แต่นี่เป็นเครื่องกำเนิดไฟฟ้าแบบหลอกง่ายการใช้Multiply-with-carry ที่ฉันดัดแปลงมาจากWikipedia (ถูกลบไปแล้ว):

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

แก้ไข: ฟังก์ชั่นเมล็ดคงที่โดยทำให้มันรีเซ็ต m_z
EDIT2: ข้อบกพร่องการใช้งานที่จริงจังได้รับการแก้ไข


3
มีใครทดสอบฟังก์ชั่นนี้เพื่อความสุ่มของมันหรือไม่?
Justin

3
นี่คือเครื่องกำเนิดไฟฟ้าแบบสุ่มทวีคูณพร้อมพกพา (MWC) ที่มีระยะเวลาค่อนข้างยาว ดัดแปลงมาจากwikipedia Random Number Generators
Michael_Scharf

10
seedฟังก์ชั่นไม่รีเซ็ตเครื่องกำเนิดไฟฟ้าแบบสุ่มเพราะmz_zตัวแปรเปลี่ยนไปเมื่อrandom()ถูกเรียกว่า ดังนั้นจึงตั้งค่าmz_z = 987654321(หรือค่าอื่น ๆ ) ในseed
Michael_Scharf

เมื่อฉันใช้กับเครื่องสร้างสีแบบสุ่ม (HSL) มันจะสร้างเฉพาะสีเขียวและสีฟ้า ตัวสร้างแบบสุ่มดั้งเดิมสร้างทุกสี ดังนั้นจึงไม่เหมือนกันหรือใช้งานไม่ได้
Tomas Kubes

@Michael_Scharf 1) การเปลี่ยนแปลงเมล็ดไม่m_w m_z2) ทั้งสองm_wและm_zเปลี่ยนแปลง BASED จากค่าก่อนหน้าดังนั้นจึงแก้ไขผลลัพธ์
ESL

26

อัลกอริทึมของ Antti Sykäriนั้นดีและสั้น ตอนแรกฉันสร้างรูปแบบที่แทนที่ Math.random ของ Javascript เมื่อคุณเรียก Math.seed (s) แต่แล้ว Jason ให้ความเห็นว่าการคืนค่าฟังก์ชันจะดีกว่า:

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

นี่เป็นอีกฟังก์ชั่นที่ Javascript ไม่มี: เครื่องกำเนิดไฟฟ้าสุ่มอิสระ นี่เป็นสิ่งสำคัญอย่างยิ่งหากคุณต้องการให้มีการจำลองที่สามารถทำซ้ำได้หลายครั้งพร้อมกัน


3
หากคุณส่งคืนฟังก์ชันแทนการตั้งค่าMath.randomที่จะให้คุณมีเครื่องกำเนิดไฟฟ้าอิสระหลายเครื่องใช่ไหม
Jason Goemaat

1
โปรดดูความคิดเห็นด้านบนเกี่ยวกับการกระจายตัวของการสุ่มหากมีความสำคัญกับคุณ: stackoverflow.com/questions/521295/…
jocull

randoms ที่เกิดจากสิ่งนี้สามารถทำซ้ำได้? มันให้ตัวเลขใหม่ทุกครั้ง
SMUsamaShah

ทุกครั้งที่คุณทำMath.seed(42);มันรีเซ็ตฟังก์ชั่นดังนั้นหากคุณไม่var random = Math.seed(42); random(); random();คุณจะได้รับแล้ว0.70... 0.38...หากคุณรีเซ็ตโดยการโทรvar random = Math.seed(42);อีกครั้งในครั้งต่อไปที่คุณโทรrandom()คุณจะได้รับ0.70...อีกครั้งและครั้งต่อไปที่คุณจะได้รับ0.38...อีกครั้ง
WOUNDEDStevenJones

1
โปรดอย่าใช้สิ่งนี้ โปรดสละเวลาเพื่อใช้ตัวแปรโลคอลที่มีชื่อrandomแทนการเขียนทับฟังก์ชันจาวาสคริปต์ดั้งเดิม การเขียนทับMath.randomอาจทำให้คอมไพเลอร์ JIST ยกเลิกการเพิ่มโค้ดทั้งหมดของคุณ
Jack Giffin

11

โปรดดูผลงานของ Pierre L'Ecuyer ที่ย้อนกลับไปในช่วงปลายทศวรรษ 1980 และต้นปี 1990 มีคนอื่นเช่นกัน การสร้างตัวสร้างตัวเลขสุ่ม (หลอก) ด้วยตัวคุณเองหากคุณไม่ใช่ผู้เชี่ยวชาญนั้นค่อนข้างอันตรายเพราะมีความเป็นไปได้สูงที่ผลลัพธ์ไม่ถูกสุ่มแบบสถิติหรือมีระยะเวลาน้อย ปิแอร์ (และคนอื่น ๆ ) ได้รวมตัวสร้างตัวเลขสุ่ม (หลอก) ที่ดีซึ่งใช้งานง่าย ฉันใช้เครื่องกำเนิดไฟฟ้า LFSR ของเขา

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

Phil Troy


1
คำตอบที่ดี แต่ไม่เกี่ยวข้องกับ javascript :)
Nikolay Fominyh

3
รหัสสำหรับการนำงานของศาสตราจารย์ L'Ecuyer ไปใช้ได้อย่างสาธารณะสำหรับจาวาและสามารถแปลได้อย่างง่ายดายโดยโปรแกรมเมอร์ส่วนใหญ่เป็นจาวาสคริปต์
user2383235

6

เมื่อรวมคำตอบก่อนหน้าเข้าด้วยกันนี่เป็นฟังก์ชั่นแบบสุ่มที่คุณสามารถค้นหาได้:

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
สิ่งนี้ให้ผลลัพธ์ที่คล้ายกันมากในตอนต้นของลำดับที่มีเมล็ดต่าง ตัวอย่างเช่นMath.seed(0)()ผลตอบแทน0.2322845458984375และผลตอบแทนMath.seed(1)() 0.23228873685002327การเปลี่ยนทั้งสองm_wและm_zตามเมล็ดดูเหมือนว่าจะช่วย var m_w = 987654321 + s; var m_z = 123456789 - s;สร้างการกระจายที่ดีของค่าแรกด้วยเมล็ดที่แตกต่างกัน
ไม่ได้กำหนด

1
@ ได้กำหนดปัญหาที่คุณอธิบายได้รับการแก้ไข ณ วันที่แก้ไขครั้งล่าสุดมันเป็นข้อผิดพลาดในการใช้งาน MWC
bryc

ทำงานได้ดีตั้งแต่วันที่มกราคม 2563 เริ่มต้นด้วย 0 รับ 0.7322976540308446 เมล็ดพันธุ์ที่มี 1, 0.16818441334180534, กับ 2: 0.6040864314418286, กับ 3: 0.03998844954185188 ขอบคุณทั้งคู่!
ยูเรก้า

3

ในการเขียนตัวกำเนิดสุ่มเทียมของคุณเองนั้นค่อนข้างง่าย

ข้อเสนอแนะของ Dave Scotese นั้นมีประโยชน์ แต่ตามที่คนอื่น ๆ ชี้ให้เห็นมันไม่ได้กระจายอย่างสม่ำเสมอ

อย่างไรก็ตามมันไม่ได้เป็นเพราะข้อโต้แย้งจำนวนเต็มของความบาป มันเป็นเพราะช่วงของความบาปที่เกิดขึ้นจากการฉายภาพหนึ่งมิติของวงกลม ถ้าคุณใช้มุมของวงกลมแทนมันจะเหมือนกัน

ดังนั้นแทนที่จะใช้ sin (x) ให้ใช้ arg (exp (i * x)) / (2 * PI)

หากคุณไม่ชอบคำสั่งเชิงเส้นให้ผสมกับ xor สักหน่อย ปัจจัยที่เกิดขึ้นจริงไม่ได้สำคัญมากเช่นกัน

เพื่อสร้างตัวเลขสุ่มหลอกคนหนึ่งสามารถใช้รหัส:

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

โปรดทราบว่าคุณไม่สามารถใช้ลำดับสุ่มหลอกได้เมื่อจำเป็นต้องใช้เอนโทรปีของจริง


ฉันไม่มีความเชี่ยวชาญ แต่เมล็ดลำดับตามรูปแบบคงที่ พิกเซลสีคือ> = 0.5 ฉันเดาว่ามันคงเป็นรัศมีซ้ำไปซ้ำมา?
bryc


1

Math.randomไม่มี แต่วิ่งห้องสมุดแก้นี้ มันมีการแจกแจงเกือบทั้งหมดที่คุณสามารถจินตนาการและสนับสนุนการสร้างหมายเลขสุ่มที่มีเมล็ด ตัวอย่าง:

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

-1

ฉันได้เขียนฟังก์ชั่นที่ส่งกลับค่าการสุ่มจำนวน seeded มันใช้ Math.sin เพื่อให้มีตัวเลขสุ่มยาวและใช้เมล็ดเพื่อเลือกหมายเลขจากที่

การใช้:

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

มันจะคืนค่าจำนวน seeded ของคุณพารามิเตอร์แรกคือค่าสตริงใด ๆ เมล็ดของคุณ พารามิเตอร์ที่สองคือจำนวนหลักที่จะส่งกลับ

     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
ลำดับของตัวเลขที่ผลิตโดยสิ่งนี้ไม่ได้ประมาณคุณสมบัติของลำดับตัวเลขสุ่ม สร้างตัวเลข 15 หมายเลขพร้อมกับสตริงผลลัพธ์มักเริ่มต้นด้วย 7 สำหรับเกือบทุกคีย์ตัวอย่างเช่น
Gabriel

-2

วิธีการง่ายๆสำหรับเมล็ดที่ตายตัว:

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

-6

สำหรับตัวเลขระหว่าง 0 ถึง 100

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

3
คำถามเกี่ยวกับการเพาะMath.randomเช่นว่าเมื่อใดก็ตามที่Math.randomมีการเพาะเมล็ดด้วยเมล็ดเดียวกันมันจะผลิตอนุกรมสุ่มตามลำดับ คำถามนี้ไม่ได้ต่อว่าเรื่องที่เกิดขึ้นจริงการใช้งาน / Math.randomการสาธิตของ
Jack Giffin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.