以下是一些需要考虑的算法:
A. 随机播放
- 洗牌数组;上)
- 选择前 Z 项;O(Z) 或更好
总体复杂度:O(N)
function A(array, z) {
return _.first(_.shuffle(array), z);
}
B. 重投随机选择
- 从 0..N-1 中选择一个随机数;O(1)
- 如果该号码之前已被选中,请转到步骤 1
- 记录选号;O(1)
- 从给定索引处的数组中选择一个项目;O(1)
- 如果我们选择的物品少于 Z,请转到第 1 步
总体复杂性:
对于 Z << 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;
}
C. 去除随机选择
- 制作数组的副本;上)
- 从数组中选择一个随机项;O(1)
- 从数组中删除项目;上)
- 如果我们选择的物品少于 Z,请转到第 2 步
总体复杂度: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。
简而言之,没有“简单”的解决方案总是具有出色的性能。