ProgramingTip

배열에서 n 개의 요소를 무작위로 얻는 방법

bestdevel 2020. 11. 22. 20:20
반응형

배열에서 n 개의 요소를 무작위로 얻는 방법


나는 '자바 펼쳐의 배열에서 무작위로 요소에 액세스하는 방법'을 연구하고 있습니다. 이것에 관한 많은 링크를 찾았습니다. 좋아요 : JavaScript 배열에서 임의의 항목 가져 오기

var item = items[Math.floor(Math.random()*items.length)];

질문 : 여기서는 배열에서 하나의 항목 만 선택할 수 있습니다. 하나 이상의 요소를 어떻게 달성 할 수 있습니까?이 문장에서 배열에서 하나 이상의 요소를 얻을 수있는 방법을 알려주세요.


다음과 같은 비파괴 ( 빠른 ) 기능을 중지합니다 .

function getRandom(arr, n) {
    var result = new Array(n),
        len = arr.length,
        taken = new Array(len);
    if (n > len)
        throw new RangeError("getRandom: more elements taken than available");
    while (n--) {
        var x = Math.floor(Math.random() * len);
        result[n] = arr[x in taken ? taken[x] : x];
        taken[x] = --len in taken ? taken[len] : len;
    }
    return result;
}

두 줄 :

// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());

// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);

스템 :


다음과 같은 기능을 만듭니다.

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
        result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]);
    }
    return result;
}

또한 sourceArray에 반환 할 요소가 있는지 확인해야합니다. 고유 한 한 요소를 반환 할 항목 소스 배열에서 선택한 요소를 제거해야합니다.


원래 배열을 변경하지 않고 무작위 항목 5 개 제공 :

const n = 5;
const sample = items
  .map(x => ({ x, r: Math.random() }))
  .sort((a, b) => a.r - b.r)
  .map(a => a.x)
  .slice(0, n);

(큰 목록에는 사용하지 않습니다)


.samplePython 표준 라이브러리에서 포팅 :

function sample(population, k){
    /*
        Chooses k unique random elements from a population sequence or set.

        Returns a new list containing elements from the population while
        leaving the original population unchanged.  The resulting list is
        in selection order so that all sub-slices will also be valid random
        samples.  This allows raffle winners (the sample) to be partitioned
        into grand prize and second place winners (the subslices).

        Members of the population need not be hashable or unique.  If the
        population contains repeats, then each occurrence is a possible
        selection in the sample.

        To choose a sample in a range of integers, use range as an argument.
        This is especially fast and space efficient for sampling from a
        large population:   sample(range(10000000), 60)

        Sampling without replacement entails tracking either potential
        selections (the pool) in a list or previous selections in a set.

        When the number of selections is small compared to the
        population, then tracking selections is efficient, requiring
        only a small set and an occasional reselection.  For
        a larger number of selections, the pool tracking method is
        preferred since the list takes less space than the
        set and it doesn't suffer from frequent reselections.
    */

    if(!Array.isArray(population))
        throw new TypeError("Population must be an array.");
    var n = population.length;
    if(k < 0 || k > n)
        throw new RangeError("Sample larger than population or is negative");

    var result = new Array(k);
    var setsize = 21;   // size of a small set minus size of an empty list

    if(k > 5)
        setsize += Math.pow(4, Math.ceil(Math.log(k * 3, 4)))

    if(n <= setsize){
        // An n-length list is smaller than a k-length set
        var pool = population.slice();
        for(var i = 0; i < k; i++){          // invariant:  non-selected at [0,n-i)
            var j = Math.random() * (n - i) | 0;
            result[i] = pool[j];
            pool[j] = pool[n - i - 1];       // move non-selected item into vacancy
        }
    }else{
        var selected = new Set();
        for(var i = 0; i < k; i++){
            var j = Math.random() * (n - i) | 0;
            while(selected.has(j)){
                j = Math.random() * (n - i) | 0;
            }
            selected.add(j);
            result[i] = population[j];
        }
    }

    return result;
}

Lib / random.py 에서 이식 된 구현 입니다.

메모 :

  • setsize효율성을 위해 Python의 특성을 기반으로 설정됩니다. JavaScript에 맞게 조정되지 않은 알고리즘은 여전히 ​​예상대로 작동합니다.
  • 이 페이지에 설명 된 다른 답변은 .NET Framework의 오용으로 인해 ECMAScript 사양에 따라 안전하지 않습니다 Array.prototype.sort. 그러나이 알고리즘은 유한 한 시간 돌연변이 종료됩니다.
  • 하지 않는 이전 브라우저의 Set구현, 설정은 대체 할 수 Array.has(j)교체 .indexOf(j) > -1.

허용 된 답변에 대한 성능 :


ES6 구문

const pickRandom = (arr,count) => {
  let _arr = [...arr];
  return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] ); 
}

반복 항목없이 루프에서 배열에서 항목을 무작위로 가져 오기 다음을 사용하여 배열에서 선택을 제거합니다 splice.

var items = [1, 2, 3, 4, 5];
var newItems = [];

for(var i = 0; i < 3; i++) {
    var idx = Math.floor(Math.random() * items.length);
    newItems.push(items[idx]);
    items.splice(idx, 1);
}

바이올린 예


Array.prototype.getnkill = function() {
    var a = Math.floor(Math.random()*this.length);
    var dead = this[a];
    this.splice(a,1);
    return dead;
}

//.getnkill() removes element in the array 
//so if you like you can keep a copy of the array first:

//var original= items.slice(0); 


var item = items.getnkill();

var anotheritem = items.getnkill();

편집

:이 솔루션은 몇 가지 요소 만 얻을 경우 여기에 제시된 다른 솔루션 (소스 배열 연결)보다 느립니다. 이 솔루션의 속도는 원래 배열의 요소 수 의존하는 반면 스 플라이 싱 솔루션의 속도는 배열에 필요한 요소의 수에 따라 늘어납니다.

반복되지 않는 임의의 요소를 원하는 경우 배열을 섞은 다음 원하는만큼만 얻을 수 있습니다.

function shuffle(array) {
    var counter = array.length, temp, index;

    // While there are elements in the array
    while (counter--) {
        // Pick a random index
        index = (Math.random() * counter) | 0;

        // And swap the last element with it
        temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }

    return array;
}

var arr = [0,1,2,3,4,5,7,8,9];

var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements

스템 : http://jsbin.com/UHUHuqi/1/edit

여기에서 내장 셔플 기능 : https://stackoverflow.com/a/6274398/1669279


이런 종류의 문제를 해결하는 기능이 필요 때문에 여기서 공유하겠습니다.

    const getRandomItem = function(arr) {
        return arr[Math.floor(Math.random() * arr.length)];
    }

    // original array
    let arr = [4, 3, 1, 6, 9, 8, 5];

    // number of random elements to get from arr
    let n = 4;

    let count = 0;
    // new array to push random item in
    let randomItems = []
    do {
        let item = getRandomItem(arr);
        randomItems.push(item);
        // update the original array and remove the recently pushed item
        arr.splice(arr.indexOf(item), 1);
        count++;
    } while(count < n);

    console.log(randomItems);
    console.log(arr);

참고 : n = arr.length기본적으로 배열을 셔플하고 셔플 된 배열 arrrandomItems반환합니다.

도장


여기에 잘 입력 된 버전이 있습니다. 실패하지 않습니다. 샘플 크기가 원래 배열의 길이보다 큰 경우 셔플 된 배열을 반환합니다.

function sampleArr<T>(arr: T[], size: number): T[] {
  const setOfIndexes = new Set<number>();
  while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
    setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
  }
  return Array.from(setOfIndexes.values()).map(i => arr[i]);
}

const randomIntFromInterval = (min: number, max: number): number =>
  Math.floor(Math.random() * (max - min + 1) + min);

srcArray에서 무작위 요소를 하나씩 추출하는 동안 충분하거나 추출 할 srcArray에 더 이상 요소가 있습니다. 안정적입니다.

function getNRandomValuesFromArray(srcArr, n) {
    // making copy to do not affect original srcArray
    srcArr = srcArr.slice();
    resultArr = [];
    // while srcArray isn't empty AND we didn't enough random elements
    while (srcArr.length && resultArr.length < n) {
        // remove one element from random position and add this element to the result array
        resultArr = resultArr.concat( // merge arrays
            srcArr.splice( // extract one random element
                Math.floor(Math.random() * srcArr.length),
                1
            )
        );
    }

    return resultArr;
}


2019 년

이것은 Laurynas Mališauskas의 답변 과 동일하지만 요소가 고유 한 점만 있습니다 ( 중복 없음).

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

이제 "jQuery로 여러 임의의 요소를 얻는 방법"이라는 원래 질문에 답한 다음을 수행하십시오.

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

var $set = $('.someClass');// <<<<< change this please

var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
    allIndexes.push(i);
}

var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);

var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
    var randomIndex = randomIndexes[i];
    if($randomElements === null) {
        $randomElements = $set.eq(randomIndex);
    } else {
        $randomElements.add($set.eq(randomIndex));
    }
}

// $randomElements is ready
$randomElements.css('backgroundColor', 'red');

다음은 교체 여부에 관계없이 배열을 쉽게 샘플링 할 수있는 함수입니다.

  // Returns a random sample (either with or without replacement) from an array
  const randomSample = (arr, k, withReplacement = false) => {
    let sample;
    if (withReplacement === true) {  // sample with replacement
      sample = Array.from({length: k}, () => arr[Math.floor(Math.random() *  arr.length)]);
    } else { // sample without replacement
      if (k > arr.length) {
        throw new RangeError('Sample size must be less than or equal to array length         when sampling without replacement.')
      }
      sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
        return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]); 
      };
    return sample;
  };

사용은 간단합니다.

대체하지 않음 (기본 동작)

randomSample([1, 2, 3], 2) 돌아올 수있다 [2, 1]

교체 포함

randomSample([1, 2, 3, 4, 5, 6], 4) 돌아올 수있다 [2, 3, 3, 2]


var getRandomElements = function(sourceArray, requiredLength) {
    var result = [];
    while(result.length<requiredLength){
        random = Math.floor(Math.random()*sourceArray.length);
        if(result.indexOf(sourceArray[random])==-1){
            result.push(sourceArray[random]);
        }
    }
    return result;
}

여기에 한 줄 고유 한 솔루션이 있습니다.

 array.sort(() => {Math.random() - Math.random()}).slice(0, n)

여기에 가장 정답이 제공되며 Random + Unique 요소를 제공합니다.

function randomize(array, n)
{
    var final = [];
    array = array.filter(function(elem, index, self) {
        return index == self.indexOf(elem);
    }).sort(function() { return 0.5 - Math.random() });

    var len = array.length,
    n = n > len ? len : n;

    for(var i = 0; i < n; i ++)
    {
        final[i] = array[i];
    }

    return final;
}

// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this

참고 URL : https://stackoverflow.com/questions/19269545/how-to-get-n-no-elements-randomly-from-an-array

반응형