สร้างตัวเลขสุ่มที่ไม่ซ้ำกันระหว่าง 1 ถึง 100


105

ฉันจะสร้างตัวเลขสุ่มที่ไม่ซ้ำกันระหว่าง 1 ถึง 100 โดยใช้ JavaScript ได้อย่างไร


20
ไม่ใช่คนหลอกลวงเพราะเน้นที่จาวาสคริปต์
dotty

2
@dotty well ไม่มีความแตกต่างที่สำคัญระหว่างการทำสิ่งนี้ใน Javascript กับการทำในภาษาอื่น ๆ แต่ฉันจะไม่โหวตให้ปิด
Pointy

1
ฉันจะไม่ปิดโหวตเช่นกัน นี่เป็นสิ่งที่เฉพาะเจาะจงเพียงพอ
Josh Stodola


1
มีวิธีอื่นที่สะอาดกว่านี้ในการทำstackoverflow.com/questions/51898200/…
Huangism

คำตอบ:


186

ตัวอย่างเช่นในการสร้างตัวเลขสุ่ม 8 ตัวที่ไม่ซ้ำกันและเก็บไว้ในอาร์เรย์คุณสามารถทำได้ง่ายๆ:

var arr = [];
while(arr.length < 8){
    var r = Math.floor(Math.random() * 100) + 1;
    if(arr.indexOf(r) === -1) arr.push(r);
}
console.log(arr);


15
รหัสจริงนั้นดีกว่าสำหรับคำถามเช่นนี้มากกว่ารหัสเทียม;) (ลบคำตอบของฉันที่เป็นรหัสเทียม ... )
Roman Starkov

3
O สามารถเลือกได้ ใช้ var randomnumber = Math.ceil (Math.random () * 100)
Alsciende

10
-1: อัลกอริทึมนี้เป็นแนวทางที่ไร้เดียงสา มันไม่มีประสิทธิภาพมาก
Frerich Raabe

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

4
มีโอกาสที่ฟังก์ชันจะส่งคืน 0 ในอาร์เรย์ ตามลิงค์นี้เลย: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , Math.random () Returns a random number between 0 (inclusive) and 1 (exclusive). หากthe Math.random()ส่งกลับ 0 โดยไม่ได้ตั้งใจค่านั้นMath.ceil(0)ก็เป็น 0 เช่นกันแม้ว่าโอกาสจะต่ำ
Qian Chen

43
  1. เติมอาร์เรย์ด้วยตัวเลข 1 ถึง 100
  2. สุ่มสับ
  3. ใช้ 8 องค์ประกอบแรกของอาร์เรย์ผลลัพธ์

8
แน่นอนว่ามันมีประสิทธิภาพมากกว่าในการปรับแต่งโค้ดเพื่อทำการสับเปลี่ยน 8 ครั้งแรกเท่านั้น? (แล้วนำ 8 องค์ประกอบสุดท้ายของอาร์เรย์กึ่งสับเปลี่ยน)
วินาที

1
นี่คือวิธีที่ฉันมักจะทำเช่นกัน randlines file | head -10ดังนั้นถ้าผมอยากสิบสายการสุ่มจากไฟล์ที่มีพวงของสายในนั้นที่ฉันทำ
tchrist

1
ฉันคิดว่าเป็นคำตอบที่เหมาะสมเพราะยังคงรักษาระดับความน่าจะเป็นซึ่งคำตอบที่ยอมรับไม่ได้
roberto tomás

2
เกิดอะไรขึ้นถ้า N = 10 ^ 12? ไม่ค่อยมีประสิทธิภาพ
shinzou

2
@shinzou ในโลกแห่งความเป็นจริงคุณจะไม่เรียงตัวเลข 10 ^ 12 โดยใช้ JavaScript คำถามเกี่ยวกับเรื่องเล็กน้อยเรียกร้องให้มีคำตอบที่ไม่สำคัญ ฉันไม่ได้มาที่นี่เพื่อแก้ปัญหาความหิวโหยของโลก ฉันมีความพร้อมที่จะทำเช่นนั้น แต่นั่นไม่ใช่สิ่งที่เป็นคำถาม
ЯegDwight

14

สร้างการเรียงสับเปลี่ยนของตัวเลข 100 จากนั้นเลือกตามลำดับ

ใช้Knuth สลับ (aka สับเปลี่ยน Fisher-Yates) อัลกอริทึม

JavaScript:

  function fisherYates ( myArray,stop_count ) {
  var i = myArray.length;
  if ( i == 0 ) return false;
  int c = 0;
  while ( --i ) {
     var j = Math.floor( Math.random() * ( i + 1 ) );
     var tempi = myArray[i];
     var tempj = myArray[j];
     myArray[i] = tempj;
     myArray[j] = tempi;

     // Edited thanks to Frerich Raabe
     c++;
     if(c == stop_count)return;

   }
}

รหัสที่คัดลอกมาจากลิงค์

แก้ไข :

ปรับปรุงรหัส:

function fisherYates(myArray,nb_picks)
{
    for (i = myArray.length-1; i > 1  ; i--)
    {
        var r = Math.floor(Math.random()*i);
        var t = myArray[i];
        myArray[i] = myArray[r];
        myArray[r] = t;
    }

    return myArray.slice(0,nb_picks);
}

ปัญหาที่อาจเกิดขึ้น:

สมมติว่าเรามีอาร์เรย์ 100 หมายเลข {เช่น [1,2,3 ... 100]} และเราหยุดการแลกเปลี่ยนหลังจากการแลกเปลี่ยน 8 ครั้ง จากนั้นอาร์เรย์เวลาส่วนใหญ่จะมีลักษณะเป็น {1,2,3,76,5,6,7,8, ... ตัวเลขที่นี่จะถูกสับ ... 10}

เนื่องจากตัวเลขทุกตัวจะถูกสลับด้วยความน่าจะเป็น 1/100 ดังนั้นความน่าจะเป็น ของการแลกเปลี่ยน 8 หมายเลขแรกคือ 8/100 ในขณะที่พร็อบ ของการแลกเปลี่ยนอื่น ๆ 92 คือ 92/100

แต่ถ้าเราเรียกใช้อัลกอริทึมสำหรับอาร์เรย์เต็มรูปแบบเราจะแน่ใจว่า (เกือบ) ทุกรายการถูกสลับ

มิฉะนั้นเราต้องเผชิญกับคำถาม: หมายเลข 8 ตัวใดให้เลือก?


5
วิธีนี้ถูกต้อง แต่ไม่เหมาะสม: คุณสามารถหยุดการสับหลังจากการแลกเปลี่ยนแปดครั้งได้เนื่องจากคุณต้องการตัวเลขสุ่มเพียงแปดตัว โค้ดด้านบนสลับอาร์เรย์ทั้งหมด (ในสถานการณ์นี้ 100 องค์ประกอบ)
Frerich Raabe

โค้ดสามารถปรับปรุงได้อย่างจริงจัง การคืนค่าผลข้างเคียงและการใช้งานฟังก์ชั่นล้วนเป็น IMO ที่พร่ามัวจริงๆ บางทีถ้าคุณเขียนฟังก์ชันที่ตอบโจทย์เดิมอย่างตรงประเด็นโดยใช้ฟังก์ชัน fisherYates ของคุณก็จะชัดเจนขึ้น
Alsciende

1
คำตอบอัปเดตด้วยโค้ดที่ปรับปรุงแล้ว นอกจากนี้ @Frerich Raabe: ปัญหาเกี่ยวกับการหยุดหลังจากพูดถึงการแลกเปลี่ยนแปดครั้ง
Pratik Deoghare

อัลกอริทึม Fisher-Yates ของคุณไม่ถูกต้อง r ควรขึ้นอยู่กับ i. ดู anwser ของฉัน: stackoverflow.com/questions/2380019/…
Alsciende

อ๊ะขอโทษความผิดพลาดที่น่ากลัวของฉัน !! การใช้งานของคุณยอดเยี่ยม ชอบมัน. +1. โปรดแจ้งให้เราทราบหากมีสิ่งผิดปกติเกิดขึ้นอีกขอบคุณ
Pratik Deoghare

12

Modern JS Solution โดยใช้Set (และตัวพิมพ์เล็ก O (n))

const nums = new Set();
while(nums.size !== 8) {
  nums.add(Math.floor(Math.random() * 100) + 1);
}

console.log([...nums]);


ทำไมจึงเป็น O (n)? มันวนซ้ำตามอำเภอใจไม่ได้หรือ?
Anthony Wieser

@AnthonyWieser คุณพูดถูกกรณีที่เลวร้ายที่สุด ฉันหมายถึงกรณีเฉลี่ยตั้งแต่ Set.add คือ o (1)
Alister

ฉันคิดว่าสิ่งนี้สามารถคืนค่า 0 เหมือนคำตอบของฉันก่อนที่จะเปลี่ยนไปใช้Math.floor(Math.random()*100) + 1
adam0101

เจ๋งมากที่ค้นพบSetใน JS! อย่างไรก็ตามวิธีนี้จะไม่ทำให้เกิดการสร้างตัวเลขโดยไม่จำเป็นจนกว่าจะมีจำนวนที่ตรงกับความต้องการของความเป็นเอกลักษณ์โดยเฉพาะอย่างยิ่งในการทำซ้ำครั้งล่าสุดถ้า 8 ใกล้เคียงกับ 100 ดังนั้นฉันคิดว่าฉันชอบคำตอบที่หรูหราด้วยsortด้านล่าง
Gilad Barner

11

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

เพื่อแก้ปัญหาของคุณโดยเฉพาะการใช้ Chance นั้นง่ายพอ ๆ กับ:

// One line!
var uniques = chance.unique(chance.natural, 8, {min: 1, max: 100});

// Print it out to the document for this snippet so we can see it in action
document.write(JSON.stringify(uniques));
<script src="http://chancejs.com/chance.min.js"></script>

Disclaimer ในฐานะผู้เขียน Chance ฉันมีอคติเล็กน้อย;)


โหวตขึ้นเพราะฉันไม่เคยเห็นข้อมูลโค้ดรันมาก่อน
surfmuggle

หากฉันต้องการสร้างรหัส (สตริงตัวเลขและตัวอักษรแบบสุ่ม 8 ตัว) สำหรับคูปองซึ่งต้องไม่ซ้ำกันฉันจะทำอย่างไรกับ Chance.js หมายเหตุ: คูปองจะทำตามความต้องการดังนั้นจำนวนรหัสจะไม่มีกำหนด
Oscar Yuandinata

@OscarYuandinata นั้นง่ายเพียงทำvar codes = chance.unique(chance.string, 8)หากคุณต้องการรหัสที่ดึงมาจากกลุ่มอักขระเฉพาะคุณสามารถระบุได้เช่นนี้chance.unique(chance.string, 8, {pool: "abcd1234"})โดยที่ abcd1234 สามารถเป็นอักขระใดก็ได้ที่คุณต้องการในพูล ดูโอกาส js.com/#string
Victor Quinn

@VictorQuinn ขอโทษฉันไม่ได้ทำให้ตัวเองชัดเจน ฉันหมายความว่ารหัสคูปองจะเป็นสตริงตัวเลขและตัวอักษรแบบสุ่ม 8 ตัวไม่ใช่อาร์เรย์ของสตริงตัวเลขและตัวอักษรแบบสุ่ม 8 ตัว ฮ่า ๆ ๆ ..
Oscar Yuandinata

โอ้ @OscarYuandinata มันง่ายกว่ามากchance.string({ length: 8 })และถ้าคุณต้องการให้อักขระบางตัวปรากฏในสตริงนั้นเท่านั้นchance.string({ pool: 'abcd1234', length: 8 })ซึ่งจะส่งคืนสตริงอักขระแบบสุ่ม 8 ตัวจากอักขระ abcd1234 เช่น "2c2c44bc" หรือ "331141cc"
Victor Quinn

9

อีกวิธีหนึ่งคือการสร้างอาร์เรย์ 100 รายการที่มีตัวเลขจากน้อยไปมากและจัดเรียงแบบสุ่ม สิ่งนี้นำไปสู่ตัวอย่างง่าย ๆ สั้น ๆ และ (ในความคิดของฉัน)

const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));


นี่คือคำตอบที่ฉันชอบที่สุดสำหรับพวกเขาทั้งหมด Dunno ทำไมได้รับเพียง 6 โหวต หรูหราและมีความซับซ้อนที่ดี (มีให้sortใช้งานได้ดีซึ่งฉันแน่ใจว่าเป็นอย่างนั้น)
Gilad Barner

8

เพื่อหลีกเลี่ยงการสับเปลี่ยนที่ยาวนานและไม่น่าเชื่อถือฉันจะทำสิ่งต่อไปนี้ ...

  1. สร้างอาร์เรย์ที่มีตัวเลขระหว่าง 1 ถึง 100 ตามลำดับ
  2. สร้างตัวเลขสุ่มระหว่าง 1 ถึง 100
  3. ค้นหาตัวเลขที่ดัชนีนี้ในอาร์เรย์และเก็บไว้ในผลลัพธ์ของคุณ
  4. ลบ elemnt ออกจากอาร์เรย์ทำให้สั้นลง
  5. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 99 เป็นขีด จำกัด บนของตัวเลขสุ่ม
  6. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 98 เป็นขีด จำกัด บนของตัวเลขสุ่ม
  7. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 97 เป็นขีด จำกัด บนของจำนวนสุ่ม
  8. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 96 เป็นขีด จำกัด บนของตัวเลขสุ่ม
  9. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 95 เป็นขีด จำกัด บนของตัวเลขสุ่ม
  10. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 94 เป็นขีด จำกัด บนของจำนวนสุ่ม
  11. ทำซ้ำจากขั้นตอนที่ 2 แต่ใช้ 93 เป็นขีด จำกัด บนของตัวเลขสุ่ม

Voila - ไม่มีตัวเลขซ้ำ

ฉันจะโพสต์รหัสจริงในภายหลังหากมีใครสนใจ

แก้ไข: อาจเป็นกระแสการแข่งขันในตัวฉัน แต่เมื่อเห็นโพสต์โดย @Alsciende ฉันไม่สามารถต้านทานการโพสต์รหัสที่ฉันสัญญาไว้ได้

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>8 unique random number between 1 and 100</title>
<script type="text/javascript" language="Javascript">
    function pick(n, min, max){
        var values = [], i = max;
        while(i >= min) values.push(i--);
        var results = [];
        var maxIndex = max;
        for(i=1; i <= n; i++){
            maxIndex--;
            var index = Math.floor(maxIndex * Math.random());
            results.push(values[index]);
            values[index] = values[maxIndex];
        }
        return results;
    }
    function go(){
        var running = true;
        do{
            if(!confirm(pick(8, 1, 100).sort(function(a,b){return a - b;}))){
                running = false;
            }
        }while(running)
    }
</script>
</head>

<body>
    <h1>8 unique random number between 1 and 100</h1>
    <p><button onclick="go()">Click me</button> to start generating numbers.</p>
    <p>When the numbers appear, click OK to generate another set, or Cancel to stop.</p>
</body>


แต่แล้วหมายเลขแปดของคุณจะสุ่มจาก 1 ถึง 92 ไม่ใช่ 1 ถึง 100 หากคุณต้องเลือก 90 หมายเลขหมายเลขสุดท้ายของคุณจะถูกเลือกจาก 1 ถึง 10 เท่านั้นใช่หรือไม่?
adam0101

@ adam0101 ไม่ใช่เพราะเขาลบตัวเลขออกขณะหยิบ ดังนั้นในขั้นตอนที่ 5 มีเพียง 99 หมายเลขในอาร์เรย์ของเขา @belugabob คุณไม่ได้มีประสิทธิภาพมากกว่า Knuth Shuffle ในความเป็นจริงการต่อสายอาจแพงกว่าการสับเปลี่ยน (ซึ่งเชื่อถือได้อย่างสมบูรณ์แบบ)
Alsciende

@ adam0101: เขาลบองค์ประกอบที่เลือกออกจากอาร์เรย์ (ดูขั้นตอนที่ 4 ด้านบน) ดังนั้นจึงหลีกเลี่ยงไม่ให้องค์ประกอบใด ๆ ถูกเลือกสองครั้ง จากนั้นเขาใช้ขอบเขตบนล่างสำหรับตัวเลขสุ่มถัดไปเพียงเพราะอาร์เรย์สั้นกว่า
Frerich Raabe

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

1
เหตุผลที่ไม่ลดค่าความยาวคือไม่มีการรับประกันว่าการลดความยาวของอาร์เรย์จะไม่ทำได้โดยการจัดสรรหน่วยความจำใหม่ การใช้ maxIndex มีผลเหมือนกันโดยเพียงแค่เพิกเฉยต่อรายการสุดท้ายในอาร์เรย์เนื่องจากไม่เกี่ยวข้อง
belugabob

3

ฉันจะทำสิ่งนี้:

function randomInt(min, max) {
    return Math.round(min + Math.random()*(max-min));
}
var index = {}, numbers = [];
for (var i=0; i<8; ++i) {
    var number;
    do {
        number = randomInt(1, 100);
    } while (index.hasOwnProperty("_"+number));
    index["_"+number] = true;
    numbers.push(number);
}
delete index;

3

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

/* Creates an array of random integers between the range specified 
     len = length of the array you want to generate
     min = min value you require
     max = max value you require
     unique = whether you want unique or not (assume 'true' for this answer)
*/
    function _arrayRandom(len, min, max, unique) {
        var len = (len) ? len : 10,
                min = (min !== undefined) ? min : 1,
                max = (max !== undefined) ? max : 100,
                unique = (unique) ? unique : false,
                toReturn = [], tempObj = {}, i = 0;

        if(unique === true) {
            for(; i < len; i++) {
                var randomInt = Math.floor(Math.random() * ((max - min) + min));
                if(tempObj['key_'+ randomInt] === undefined) {
                    tempObj['key_'+ randomInt] = randomInt;
                    toReturn.push(randomInt);
                } else {
                    i--;
                }
            }
        } else {
            for(; i < len; i++) {
                toReturn.push(Math.floor(Math.random() * ((max - min) + min)));
            }
        }

        return toReturn;
    }

ที่นี่ 'tempObj' เป็น obj ที่มีประโยชน์มากเนื่องจากตัวเลขสุ่มทุกตัวที่สร้างขึ้นจะตรวจสอบโดยตรงใน tempObj นี้ว่ามีคีย์นั้นอยู่แล้วหรือไม่ถ้าไม่มีเราจะลด i ลงทีละรายการเนื่องจากเราต้องการการรันเพิ่มอีก 1 ครั้งเนื่องจากมีตัวเลขสุ่มปัจจุบันอยู่แล้ว .

ในกรณีของคุณให้เรียกใช้สิ่งต่อไปนี้

_arrayRandom(8, 1, 100, true);

นั่นคือทั้งหมด


จะเกิดอะไรขึ้นถ้าฉันต้องการรวม 0 บรรทัดmin = (min) ? min : 1,จะส่งกลับ 1 เสมอ (ดังนั้น 0 จะไม่ถูกเลือก)
TBE

จุดที่ดีมาก :). ขอบคุณฉันได้ทำการเปลี่ยนแปลงที่เหมาะสมแล้ว ตอนนี้จะกลับมาแม้ว่าคุณจะผ่าน 0
kaizer1v

2

การสลับตัวเลขจาก 1 ถึง 100 เป็นกลยุทธ์พื้นฐานที่ถูกต้อง แต่ถ้าคุณต้องการเพียง 8 หมายเลขที่สับไม่จำเป็นต้องสลับตัวเลขทั้งหมด 100 หมายเลข

ฉันไม่รู้จัก Javascript เป็นอย่างดี แต่ฉันเชื่อว่ามันง่ายที่จะสร้างอาร์เรย์ 100 null อย่างรวดเร็ว จากนั้นเป็นเวลา 8 รอบคุณจะสลับองค์ประกอบที่ n ของอาร์เรย์ (n เริ่มต้นที่ 0) กับองค์ประกอบที่เลือกแบบสุ่มจาก n + 1 ถึง 99 แน่นอนว่าองค์ประกอบใด ๆ ที่ยังไม่ได้รับการเติมก็หมายความว่าองค์ประกอบนั้นจะเป็น ดัชนีเดิมบวก 1 ดังนั้นจึงเป็นเรื่องเล็กน้อยที่จะแยกตัวประกอบเมื่อคุณทำ 8 รอบเสร็จแล้ว 8 องค์ประกอบแรกของอาร์เรย์ของคุณจะมีตัวเลขสับ 8 ตัว


2
var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  if(arr.indexOf(randomnumber) === -1){arr.push(randomnumber)}  
}
document.write(arr);

สั้นกว่าคำตอบอื่น ๆ ที่ฉันเคยเห็น


1

อัลกอริธึมการเปลี่ยนรูปแบบเดียวกับ The Machine Charmer แต่มีการใช้งานต้นแบบ เหมาะกับการเลือกจำนวนมาก ใช้การมอบหมายการทำลายโครงสร้าง js 1.7ถ้ามี

// swaps elements at index i and j in array this
// swapping is easy on js 1.7 (feature detection)
Array.prototype.swap = (function () {
    var i=0, j=1;
    try { [i,j]=[j,i]; }
    catch (e) {}
    if(i) {
        return function(i,j) {
            [this[i],this[j]] = [this[j],this[i]];
            return this;
        }
    } else {
        return function(i,j) {
            var temp = this[i];
            this[i] = this[j];
            this[j] = temp;
            return this;
        }
    }
})();


// shuffles array this
Array.prototype.shuffle = function() {
    for(var i=this.length; i>1; i--) {
        this.swap(i-1, Math.floor(i*Math.random()));
    }
    return this;
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.shuffle().slice(0,n);
}

pick(8,1,100);

แก้ไข: ข้อเสนออื่น ๆ ที่เหมาะกับการเลือกจำนวนน้อยโดยพิจารณาจากคำตอบของ belugabob เพื่อรับประกันความเป็นเอกลักษณ์เราจะลบตัวเลขที่เลือกออกจากอาร์เรย์

// removes n random elements from array this
// and returns them
Array.prototype.pick = function(n) {
    if(!n || !this.length) return [];
    var i = Math.floor(this.length*Math.random());
    return this.splice(i,1).concat(this.pick(n-1));
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.pick(n);
}

pick(8,1,100);

การใช้งานแบบวนซ้ำที่ดี - ฉันได้โพสต์ทางเลือกอื่นในคำตอบของฉันที่ไม่ใช้การต่อเนื่องจากฉันรู้สึกว่านี่เป็นผลงานที่หลีกเลี่ยงได้ (ไม่ใช่ว่า OP มีปัญหาใด ๆ กับประสิทธิภาพ)
belugabob

วิธีแก้ปัญหาของคุณฉลาด แต่ฉันจะไม่ใช้มันในวิธีการเลือก Array # ของฉันเพราะฉันไม่ต้องการให้สิ่งนี้มีการสับเปลี่ยนองค์ประกอบรอบ ๆ เมื่อฉันส่งคืน
Alsciende

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

อันเดิม. ฉันใช้วิธี Array # pick ทั่วไปซึ่งฉันพบว่ามีประโยชน์ ฟังก์ชั่นนี้ไม่ทราบว่านี้เป็นอาร์เรย์การทำงานหรือไม่ เพื่อให้เป็นแบบทั่วไปจะไม่เปลี่ยนแปลงสิ่งนี้มากเกินความจำเป็น
Alsciende

แต่มันก็ยังคงเปลี่ยนแปลงแม้เพียงเล็กน้อยซึ่งไม่สามารถใช้งานได้เมื่อใช้เทคนิคนี้
belugabob

1

สำหรับอาร์เรย์ที่มีรูแบบนี้[,2,,4,,6,7,,] เพราะปัญหาของฉันคือเติมช่องเหล่านี้ ดังนั้นฉันจึงแก้ไขตามความต้องการของฉัน :)

วิธีแก้ไขต่อไปนี้ใช้ได้ผลสำหรับฉัน :)

var arr = [,2,,4,,6,7,,]; //example
while(arr.length < 9){
  var randomnumber=Math.floor(Math.random()*9+1);
  var found=false;
  for(var i=0;i<arr.length;i++){
    if(arr[i]==randomnumber){found=true;break;}
  }

  if(!found)
    for(k=0;k<9;k++)
    {if(!arr[k]) //if it's empty  !!MODIFICATION
      {arr[k]=randomnumber; break;}}
}

alert(arr); //outputs on the screen

1

คำตอบก่อนหน้านี้ที่ดีที่สุดคือคำตอบโดย sje397คำตอบที่ดีที่สุดก่อนหน้านี้คือคำตอบจากคุณจะได้รับหมายเลขสุ่มที่ดีที่สุดเท่าที่จะทำได้โดยเร็วที่สุด

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

function selectKOutOfN(k, n) {
  if (k>n) throw "k>n";
  var selection = [];
  var sorted = [];
  for (var i = 0; i < k; i++) {
    var rand = Math.floor(Math.random()*(n - i));
    for (var j = 0; j < i; j++) {
      if (sorted[j]<=rand)
        rand++;
      else
        break;
    }
    selection.push(rand);
    sorted.splice(j, 0, rand);
  }
  return selection;
}

alert(selectKOutOfN(8, 100));

1

นี่คือเวอร์ชัน ES6 ของฉันที่ฉันปูด้วยกัน ฉันแน่ใจว่ามันสามารถรวมกันได้อีกเล็กน้อย

function randomArray(i, min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  
  let arr = Array.from({length: i}, () => Math.floor(Math.random()* (max - min)) + min);
  
  return arr.sort();
 }
 
 let uniqueItems = [...new Set(randomArray(8, 0, 100))]
 console.log(uniqueItems);


0

วิธีการใช้คุณสมบัติวัตถุเป็นตารางแฮช ? วิธีนี้สถานการณ์ที่ดีที่สุดของคุณคือสุ่ม 8 ครั้งเท่านั้น จะได้ผลก็ต่อเมื่อคุณต้องการส่วนเล็ก ๆ ของช่วงของตัวเลข นอกจากนี้ยังใช้หน่วยความจำน้อยกว่า Fisher-Yates มากเนื่องจากคุณไม่ต้องจัดสรรพื้นที่สำหรับอาร์เรย์

var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));

จากนั้นฉันก็พบว่าObject.keys (obj)เป็นคุณสมบัติ ECMAScript 5 ดังนั้นข้างต้นจึงไร้ประโยชน์มากกับ internets ในตอนนี้ ไม่ต้องกลัวเพราะฉันทำให้ ECMAScript 3 เข้ากันได้โดยการเพิ่มฟังก์ชั่นปุ่มเช่นนี้

if (typeof keys == "undefined") 
{ 
  var keys = function(obj) 
  {
    props=[];
    for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
    return props;
  }
}

0
var bombout=0;
var checkArr=[];
var arr=[];
while(arr.length < 8 && bombout<100){
  bombout++;
  var randomNumber=Math.ceil(Math.random()*100);
  if(typeof checkArr[randomNumber] == "undefined"){
    checkArr[randomNumber]=1;
    arr.push(randomNumber);
  }
}​

// untested - hence bombout

0

หากคุณต้องการเอกลักษณ์มากขึ้นคุณต้องสร้างอาร์เรย์ (1..100)

var arr=[];
function generateRandoms(){
for(var i=1;i<=100;i++) arr.push(i);
}
function extractUniqueRandom()
{
   if (arr.length==0) generateRandoms();
   var randIndex=Math.floor(arr.length*Math.random());
   var result=arr[randIndex];
   arr.splice(randIndex,1);
   return result;

}
function extractUniqueRandomArray(n)
{
   var resultArr=[];
   for(var i=0;i<n;i++) resultArr.push(extractUniqueRandom());
   return resultArr;
}

โค้ดด้านบนเร็วกว่า:
extractUniqueRandomArray (50) => [2, 79, 38, 59, 63, 42, 52, 22, 78, 50, 39, 77, 1, 88, 40, 23, 48, 84, 91, 49, 4, 54, 93, 36, 100, 82, 62, 41, 89, 12, 24, 31, 86, 92, 64, 75, 70, 61, 67, 98, 76, 80, 56, 90, 83, 44, 43, 47, 7, 53]


0

การเพิ่มรหัสเดียวกันรุ่นอื่นที่ดีกว่า (คำตอบที่ยอมรับ) ด้วยฟังก์ชัน JavaScript 1.6 indexOf ไม่จำเป็นต้องวนซ้ำทั้งอาร์เรย์ทุกครั้งที่คุณตรวจสอบรายการที่ซ้ำกัน

var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  var found=false;
    if(arr.indexOf(randomnumber) > -1){found=true;}
  if(!found)arr[arr.length]=randomnumber;
}

Javascript เวอร์ชันเก่ายังสามารถใช้เวอร์ชันด้านบนได้

PS: พยายามแนะนำการอัปเดตวิกิ แต่ถูกปฏิเสธ ฉันยังคิดว่ามันอาจมีประโยชน์สำหรับคนอื่น ๆ


0

นี่คือวิธีแก้ปัญหาส่วนตัวของฉัน:

<script>

var i, k;
var numbers = new Array();
k = Math.floor((Math.random()*8));
numbers[0]=k;
    for (var j=1;j<8;j++){
        k = Math.floor((Math.random()*8));
i=0;
while (i < numbers.length){
if (numbers[i] == k){
    k = Math.floor((Math.random()*8));
    i=0;
}else {i++;}
}
numbers[j]=k;
    }
    for (var j=0;j<8;j++){
alert (numbers[j]);
    }
</script>

มันสร้างค่าอาร์เรย์ที่ไม่ซ้ำกัน 8 ค่าแบบสุ่ม (ระหว่าง 0 ถึง 7) จากนั้นแสดงโดยใช้กล่องแจ้งเตือน


0
function getUniqueRandomNos() {
    var indexedArrayOfRandomNo = [];
    for (var i = 0; i < 100; i++) {
        var randNo = Math.random();
        indexedArrayOfRandomNo.push([i, randNo]);
    }
    indexedArrayOfRandomNo.sort(function (arr1, arr2) {
        return arr1[1] - arr2[1]
    });
    var uniqueRandNoArray = [];
    for (i = 0; i < 8; i++) {
        uniqueRandNoArray.push(indexedArrayOfRandomNo[i][0]);
    }
    return uniqueRandNoArray;
}

ฉันคิดว่าวิธีนี้แตกต่างจากวิธีการที่ให้ไว้ในคำตอบส่วนใหญ่ดังนั้นฉันคิดว่าฉันอาจเพิ่มคำตอบที่นี่ (แม้ว่าคำถามจะถูกถามเมื่อ 4 ปีที่แล้วก็ตาม)

เราสร้างหมายเลขสุ่ม 100 หมายเลขและแท็กแต่ละหมายเลขด้วยตัวเลขตั้งแต่ 1 ถึง 100 จากนั้นเราเรียงลำดับหมายเลขสุ่มที่ติดแท็กเหล่านี้และแท็กจะถูกสุ่มแบบสุ่ม หรือตามต้องการในคำถามนี้เราสามารถทำได้เพียงแค่ค้นหา 8 อันดับแรกของตัวเลขสุ่มที่ติดแท็ก การค้นหารายการ 8 อันดับแรกมีราคาถูกกว่าการจัดเรียงอาร์เรย์ทั้งหมด

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


0

สิ่งนี้สามารถรองรับการสร้างหมายเลขสุ่ม UNIQUE ได้ไม่เกิน 20 หลัก

JS

 var generatedNumbers = [];

    function generateRandomNumber(precision) { // input --> number precision in integer 
        if (precision <= 20) {
            var randomNum = Math.round(Math.random().toFixed(precision) * Math.pow(10, precision));
            if (generatedNumbers.indexOf(randomNum) > -1) {
                if (generatedNumbers.length == Math.pow(10, precision))
                    return "Generated all values with this precision";
                    return generateRandomNumber(precision);
            } else {
                generatedNumbers.push(randomNum);
                return randomNum;
            }
        } else
           return "Number Precision shoould not exceed 20";
    }
    generateRandomNumber(1);

ป้อนคำอธิบายภาพที่นี่

jsFiddle


0

โซลูชันนี้ใช้แฮชซึ่งมีประสิทธิภาพมากกว่า O (1) มากกว่าการตรวจสอบว่ามีอยู่ในอาร์เรย์หรือไม่ มีการตรวจสอบความปลอดภัยเป็นพิเศษด้วย หวังว่าจะช่วยได้

function uniqueArray(minRange, maxRange, arrayLength) {
  var arrayLength = (arrayLength) ? arrayLength : 10
  var minRange = (minRange !== undefined) ? minRange : 1
  var maxRange = (maxRange !== undefined) ? maxRange : 100
  var numberOfItemsInArray = 0
  var hash = {}
  var array = []

  if ( arrayLength > (maxRange - minRange) ) throw new Error('Cannot generate unique array: Array length too high')

  while(numberOfItemsInArray < arrayLength){
    // var randomNumber = Math.floor(Math.random() * (maxRange - minRange + 1) + minRange)
    // following line used for performance benefits
    var randomNumber = (Math.random() * (maxRange - minRange + 1) + minRange) << 0

    if (!hash[randomNumber]) {
      hash[randomNumber] = true
      array.push(randomNumber)
      numberOfItemsInArray++
    }
  }
  return array
}
document.write(uniqueArray(1, 100, 8))

0

การใช้สิ่งนี้เป็นเครื่องกำเนิดไฟฟ้าทำให้ใช้งานได้ดี หมายเหตุการใช้งานนี้แตกต่างจากการใช้งานที่ต้องสับอาร์เรย์อินพุตทั้งหมดก่อน

sampleฟังก์ชั่นนี้ทำงานอย่างเฉื่อยชาโดยให้คุณสุ่ม1รายการต่อการทำซ้ำจนถึงNรายการที่คุณขอ นี่เป็นสิ่งที่ดีเพราะหากคุณต้องการเพียง3รายการจากรายการ1,000รายการคุณไม่จำเป็นต้องแตะรายการทั้งหมด 1,000 รายการก่อน

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let ys = xs.slice(0);
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield ys.splice(i,1)[0];
    n--; len--;
  }
}

// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// get 3 random items
for (let i of sample(3) (items))
  console.log(i); // f g c

// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
  console.log(i); // 3 8 7

// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]

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

ตัวอย่างเช่นshuffleฟังก์ชันอาจต้องการเปลี่ยนอาร์เรย์อินพุตดั้งเดิม หรือคุณอาจต้องการตัวอย่างจากอินพุตเดียวกันหลายครั้งอัปเดตอินพุตทุกครั้ง

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield xs.splice(i,1)[0];
    n--; len--;
  }
}

// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));

// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');

// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))

console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]

// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]

sampleไม่ใช่ฟังก์ชันบริสุทธิ์อีกต่อไปเนื่องจากการกลายพันธุ์ของอินพุตอาร์เรย์ แต่ในบางสถานการณ์ (แสดงไว้ด้านบน) อาจมีเหตุผลมากกว่านี้


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

บางทีฉันอาจต้องการเลขเฉพาะตัวแรกจากรายการตัวเลขสุ่ม 1,000,000 ตัว

  • "ฉันควรลองกี่ตัว" - คุณไม่จำเป็นต้องระบุ
  • "ฉันต้องหาไพรม์ทั้งหมดก่อนจากนั้นจึงเลือกไพรม์แบบสุ่มหรือไม่" - ไม่

เนื่องจากเรากำลังทำงานกับเครื่องกำเนิดไฟฟ้างานนี้จึงเป็นเรื่องเล็กน้อย

const randomPrimeNumber = listOfNumbers => {
  for (let x of sample(Infinity) (listOfNumbers)) {
    if (isPrime(x))
      return x;
  }
  return NaN;
}

การดำเนินการนี้จะสุ่มตัวอย่างอย่างต่อเนื่องทีละ 1 หมายเลขxตรวจสอบว่าเป็นจำนวนเฉพาะหรือไม่จากนั้นส่งคืนxหากเป็น หากรายการของตัวเลขหมดก่อนที่จะพบจำนวนเฉพาะNaNจะถูกส่งกลับ


บันทึก:

เดิมคำตอบนี้แชร์กับคำถามอื่นที่ถูกปิดเนื่องจากซ้ำกับคำถามนี้ เนื่องจากมันแตกต่างจากโซลูชันอื่น ๆ ที่ให้ไว้ที่นี่มากฉันจึงตัดสินใจแบ่งปันที่นี่เช่นกัน


0
getRandom (min, max) {
  return Math.floor(Math.random() * (max - min)) + min
}

getNRandom (min, max, n) {
  const numbers = []
  if (min > max) {
    return new Error('Max is gt min')
  }

  if (min === max) {
    return [min]
  }

  if ((max - min) >= n) {
    while (numbers.length < n) {
      let rand = this.getRandom(min, max + 1)
      if (numbers.indexOf(rand) === -1) {
        numbers.push(rand)
      }
    }
  }

  if ((max - min) < n) {
    for (let i = min; i <= max; i++) {
      numbers.push(i)
    }
  }
  return numbers
}

0

การใช้ a Setเป็นตัวเลือกที่เร็วที่สุดของคุณ นี่คือฟังก์ชั่นทั่วไปสำหรับการสุ่มเฉพาะที่ใช้ตัวสร้างการโทรกลับ ตอนนี้มันรวดเร็วและใช้ซ้ำได้

// Get a unique 'anything'
let unique = new Set()

function getUnique(generator) {
  let number = generator()
  while (!unique.add(number)) {
    number = generator()
  }
  return number;
}

// The generator.  Return anything, not just numbers.
const between_1_100 = () => 1 + Math.floor(Math.random() * 100)

// Test it
for (var i = 0; i < 8; i++) {
  const aNumber = getUnique(between_1_100)
}
// Dump the 'stored numbers'
console.log(Array.from(unique))


0

นี่คือการนำไปใช้งานของ Fisher Yates / Durstenfeld Shuffleแต่ไม่มีการสร้างอาร์เรย์จริงจึงลดความซับซ้อนของพื้นที่หรือจำเป็นต้องใช้หน่วยความจำเมื่อขนาดการเลือกมีขนาดเล็กเมื่อเทียบกับจำนวนองค์ประกอบที่มีอยู่

ในการเลือกตัวเลข 8 ตัวจาก 100 ไม่จำเป็นต้องสร้างอาร์เรย์ 100 องค์ประกอบ

สมมติว่ามีการสร้างอาร์เรย์

  • จากท้ายอาร์เรย์ (100) รับหมายเลขสุ่ม ( rnd) จาก 1 ถึง 100
  • สลับ 100 และหมายเลขสุ่ม rnd
  • ทำซ้ำขั้นตอนที่ 1 ด้วยอาร์เรย์ (99)

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

const getRandom_ = (start, end) => {
  return Math.floor(Math.random() * (end - start + 1)) + start;
};
const getRealValue_ = (map, rnd) => {
  if (map.has(rnd)) {
    return getRealValue_(map, map.get(rnd));
  } else {
    return rnd;
  }
};
const getRandomNumbers = (n, start, end) => {
  const out = new Map();
  while (n--) {
    const rnd = getRandom_(start, end--);
    out.set(getRealValue_(out, rnd), end + 1);
  }
  return [...out.keys()];
};

console.info(getRandomNumbers(8, 1, 100));
console.info(getRandomNumbers(8, 1, Math.pow(10, 12)));
console.info(getRandomNumbers(800000, 1, Math.pow(10, 15)));


0

นี่คือตัวอย่างของการสุ่มตัวเลข 5 ตัวที่นำมาจากช่วง 0 ถึง 100 (รวมทั้ง 0 และ 100) โดยไม่มีการทำซ้ำ

let finals = [];
const count = 5; // Considering 5 numbers
const max = 100;

for(let i = 0; i < max; i++){
  const rand = Math.round(Math.random() * max);
  !finals.includes(rand) && finals.push(rand)
}

finals = finals.slice(0, count)

-1

คุณสามารถทำได้ด้วยซับเดียวดังนี้:

[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]


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