ES6 天生 range 数组和 random 数组

作者 @zwhu
原文章 @github

建立数组除了字面量和 new Array() 外,还能够经由过程 Array(n) 建立,n 为数组的长度。 Array(n) 天生了长度为 n 的空数组,注重,和数组中元素赋值为 undefined 是有区分的;chrome 中检察空数组为[undefined * n],而赋值为 undefined 的数组为 [undefined, undefined, ..... , undefined]

range:

let rangeArray = (start, end) => Array(end - start + 1).fill(0).map((v, i) => i + start)

rangeArray(0,10) // return [0,1,2,3,4,5,6,7,8,9,10]

因为map不能对数组中未赋值的元素举行遍历,所以能够经由过程 ES6 的新要领 fill 对数组举行添补,把数组中的所有数转为0(转成什么都无所谓),然后经由过程 map 要领将数组中所有0都转成对应的数字。

ES5 没有 fill 要领也能够经由过程 Array.apply(null, {length: end - start + 1}).map((v, i) => i + start) 搞定。提及来比第一种要领速率能够更快。

random:


let randomArray = (start, end) => {
  let range = rangeArray(start, end)
    , random = []
  while (range.length) {
    let i = Math.random() * range.length | 0
    random.push(range[i])
    range.splice(i, 1)
  }
  return random
}

// test
let random = randomArray(1, 50)

console.log(random.length === 50)
console.log(Math.min.apply(Math, random) === 1)
console.log(Math.max.apply(Math, random) === 50)
console.log(random.sort((a, b) => a - b).every((v, i, a) => v === a[i - 1] || 0 + 1))

详细道理就是:天生一个 range 数组,然后随机 range 数组的长度,获得下标 i,掏出 range 中下标为 i 的元素放入新数组 random 中, 删除 range 数组这个元素,接着轮回,直到 range 数组被删完。

近来在看「算法」,所以厚着脸皮剖析下时候复杂度吧,不对的处所迎接指出:天生一个 range 数组,2n 次轮回,轮回 range 数组 n 次,所以加起来就是 3n 次,所需时候为线性级别的,时候复杂度为 O(n),所以看起来照样挺快的。

作为对照,剖析一种之前常常用到的要领:

let randomArray = (start, end) => {
  let o = {}
    , length = end - start + 1
    , random = []
  while (random.length < length) {
    let i = (Math.random() * length + 1) | 0
    if (o[i]) continue
    else {
      o[i] = true
      random.push(i)
    }

  }
  return random
}

// test
let random = randomArray(1, 50)

console.log(random.length === 50)
console.log(Math.min.apply(Math, random) === 1)
console.log(Math.max.apply(Math, random) === 50)
console.log(random.sort((a, b) => a - b).every((v, i, a) => v === a[i - 1] || 0 + 1))

从上面代码能够看到在最好的状况下(不会涌现 random 效果反复的状况),所需的时候复杂度为 O(n),最坏的状况下(每次都反复的话…轮回到无数次),如许就只能算希冀了,数学不好就不瞎算了。

本身对上面的剖析以后以为在输入数迥殊大的状况下,前一种要领会把后一种要领碾压成渣,但是实际状况是反被轰成了渣滓,原因是疏忽了splice要领,splice 是对数组的元素举行挪动操纵,会耗费了大批时候。

let a = [], b = []
console.time('push')
for (let i = 0; i < 1e5; i++) {
  a.push(i)
}
console.timeEnd('push')  // ==> 3ms 
console.time('splice')
for (let i = 0; i < 1e5; i++) {
  b.splice(i, 1, i)
}
console.timeEnd('splice') // ==> 21ms

从上面能够看出 splice 消费的时候远远凌驾 push 。

写着写着就好像与题目相差了万八千里…. 不过详细写了什么就没所谓了,权当纪录。

======= 2015-11-4 更新 ======

let randomArray1 = (start, end) => {
  let range = rangeArray(start, end)
    , random = []
    , N = range.length


  while (N--) {
    let i = Math.random() * (N + 1) | 0
    random.push(range[i])
    range[i] = range[N]
    //range.splice(i, 1)
  }
  return random
}

防止运用 splice 要领,算法没什么变化;速率大大提拔,测试效果以下:

console.time('random1')
let random1 = randomArray1(1, 1e5)
console.timeEnd('random1')
console.time('random2')
let random2 = randomArray2(1, 1e5)
console.timeEnd('random2')

random1: 12ms
random2: 79ms

能够看到在我的电脑下,对1万条数据的处置惩罚速率提拔了靠近6倍。和之前的剖析效果还算是符合的。

    原文作者:zwhu
    原文地址: https://segmentfault.com/a/1190000003935593
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞