在Z时间从大小为N的数组中获取Z个随机项?

时间:2013-04-27 02:52:15

标签: javascript algorithm underscore.js

我有一个大小为N的数组,可能会以某种方式排序。我想在<中获取此数组中的Z个随机项。 O(N)时间。

我的理解是,如果我使用Underscore的_.shuffle()对我的数组进行洗牌,那将花费O(N)时间。所以,洗牌,然后抓住第一个Z项目。

如果我在N之间生成Z个随机数,我想我可以进入非常丑陋的最坏情况。这是因为如果N是105,Z就是100 ..好吧,会有很多重叠,也许我会重新滚动Z几百次。

我想知道这个问题是否有简单的解决方案?我没有看到任何特定于任务的Underscore方法。

2 个答案:

答案 0 :(得分:2)

以下是一些需要考虑的算法:

:一种。混洗

  1. 随机阵列; O(N)
  2. 选择第一个Z项; O(Z)或更好
  3. 总体复杂性:O(N)

    function A(array, z) {
      return _.first(_.shuffle(array), z);
    }
    

    <强> B中。通过重新滚动随机选择

    1. 从0..N-1中选择一个随机数; O(1)
    2. 如果之前已选择该号码,请转到步骤1
    3. 记录所选号码; O(1)
    4. 从给定索引处的数组中选择一个项目; O(1)
    5. 如果我们选择的项目少于Z项,请转到步骤1
    6. 整体复杂性:

      对于Z&lt;&lt; N,O(Z)平均情况

      对于Z = N,O(N ^ 2)平均情况

      function B(array, z) {
        var pickedIndices = {};
        var result = [];
        while (result.length < z) {
          var randomIndex = Math.floor(Math.random() * array.length);
          if (!(randomIndex in pickedIndices)) {
            pickedIndices[randomIndex] = 1;
            result.push(array[randomIndex]);
          }
        }
        return result;
      }
      

      <强>℃。随机选择与删除

      1. 制作阵列的副本; O(N)
      2. 从数组中选择一个随机项; O(1)
      3. 从阵列中删除该项目; O(N)
      4. 如果我们选择的项目少于Z项,请转到第2步
      5. 总体复杂性:O(Z * N)

        function C(array, z) {
          var result = [];
          array = array.slice(0);
          for (var i = 0; i < z; i++) {
            var randomIndex = Math.floor(Math.random() * array.length);
            result.push(array.splice(randomIndex, 1)[0]);
          }
          return result;
        }
        

        效果测试

        http://jsperf.com/fetch-z-random-items-from-array-of-size-n

        当N = 100且Z = 10时,算法C是最快的(可能是因为大多数逻辑使用本机函数和/或易于优化,对于N和Z的小值比算法复杂度更重要)。

        当N = 100且Z = 100时,算法A是最快的。

        当N = 1000且Z = 100时,算法B是最快的。

        <强>结论

        我考虑过的人中没有一个最好的算法;这取决于您的数据的特征。如果您的数据特征可能不同,则可能值得进一步测试并根据N和Z的值创建一些标准,以选择性地选择最佳算法。

        例如,如果Z <= N / 2,则可以使用算法B;否则,算法A.

        简而言之,没有“简单”的解决方案始终具有出色的性能。

答案 1 :(得分:1)

我不认为我完全理解你的问题,但是如果你想从阵列中获取一个随机元素并且不能重复它,那么你只能滚动的次数少于那里是元素,然后你可以试试这个

&#13;
&#13;
function shuffle(obj, rounds, deep) {
  var length = obj.length;
  if (length < 2) {
    return;
  }

  var rounds32 = rounds >>> 0 || 1;
  var deepBool = deep === true;
  var roundCount = 0;
  var index, rnd, tmp;
  while (roundCount < rounds32) {
    index = length;
    while (index) {
      if (Array.isArray(obj[index - 1])) {
        shuffle(obj[i], rounds32, deepBool);
      }

      rnd = Math.floor(Math.random() * index);
      index -= 1;
      tmp = obj[index];
      obj[index] = obj[rnd];
      obj[rnd] = tmp;
    }

    roundCount += 1;
  }
}

var array = [];
for (var count = 0; count < 100; count += 1) {
  array.push(count);
}

shuffle(array);

var rolls = 10;
console.log(array.slice(0, rolls));
&#13;
&#13;
&#13;