请教用 JavaScript 计算这个东西的方法

2018-03-12 18:12:39 +08:00
 70599
假设我有一个数组:
[11,21,34,65,14,5,66,17,88,21,45,61,50]

我想要算出这个数组中每个值(包含它自己)之后 10 个值的整数平均值,并输出到一个数组。
当后面的值不到 10 个的时候,就算出它自己到最后 1 个值的平均值。

觉得是一个简单的事情,可是想了好久一筹莫展,请大家帮忙。
5521 次点击
所在节点    JavaScript
41 条回复
msg7086
2018-03-12 22:25:42 +08:00
@ipwx 亲,大 O 的常数倍率需要忽略不计的问题了解一下。
snw
2018-03-12 23:14:33 +08:00
先用最笨拙的方法写就行,以后再优化。
同意上面说的 V8 跑循环真心快
nino
2018-03-12 23:21:42 +08:00
```
[...].map((_, index, arr) => avg(arr.slice(index, index + 10)))
```

你要的 reduce 的写法,你的问题是不熟悉 Array.prototype.slice 这个 API 吧
ipwx
2018-03-12 23:30:46 +08:00
@msg7086 @lightening 这里可不适用忽略不计……更准确的复杂度是 O(kn) 和 O(2n),其中 k 在本问题中取 10。再说,这么简单一个程序,为啥不用最佳写法?
ipwx
2018-03-12 23:39:01 +08:00
@msg7086 @lightening 而且所谓的复杂度分析时忽略常数,只是因为不同阶的复杂度,当 N 趋向于无穷时,比值渐进趋向于 0 (或无穷),没有比较常数的必要。比如 O(logN) 和 O(N) 有阶差,此时比较常数没有意义。然而当同阶时,你都忽略常数了,你还比啥?

复杂度分析理论是为了在没有运行算法的前提下比较算法优劣的理论方法。一切教科书上的定式、过程,都要为了这个目的让路。切记。
ipwx
2018-03-12 23:43:54 +08:00
@msg7086 @lightening 不过话说回来,不同阶不比常数也不是绝对的。写算法的时候,偶尔也会根据 N 的不同,使用不同阶的算法(因为常数有差别,高阶低常数算法在 N 小的时候反而更快)。比如 GCC C++ STL 中快速排序( O(N log N))的实现,在数据量小的时候(或者递归之后数据量小的时候)是 O(N^2) 的希尔排序。当然,你问我这两个算法的确切常数,我是不确定的,不过反正写 G++ 的人很厉害,我也就相信他们的判断了。
ipwx
2018-03-12 23:46:14 +08:00
@msg7086 @lightening 好吧根据维基百科(希尔排序),优化过的希尔排序其期望复杂度是 O(N (log N)^2),也就比快速排序的期望复杂度 O(N log N) 慢那么一点。。。
msg7086
2018-03-13 00:03:49 +08:00
@ipwx 大 O 描述的是算法与输入项之间增长变化的比率,和具体的算法时间无关。
比如本题中 O(n)表示的是对于输入项长度 n 来说,算法的增长与 n 的增长是线性关系的。
别说 O(10n),就算是 O(1000000n),也还是 O(n)。

如果你要分析算法具体运算量的话,那是 kn 或者 2n,但不是 O(2n)。
但是实际运算量还要看运算类型,比如加法和内存读写的时间又不能简单相加等等,这算起来就复杂了。

这和是不是用最佳写法完全无关。
msg7086
2018-03-13 00:05:02 +08:00
打字时间过长,一刷新又多了三个回复,无视我上面说的吧。
Merlini
2018-03-13 00:06:20 +08:00
楼主的写法简单易懂,就是这个每次都要重新加一遍有点难受,可不可以利用以前加的值,往动态规划方面靠靠? 当然这只是个人想法。
msg7086
2018-03-13 00:07:14 +08:00
TL;DR:大 O 不关心常数。同阶下比较算法快慢不应该用大 O 来表示。
narcotics
2018-03-13 00:45:34 +08:00
循环入队列,满十求和
和 减去队列头,出队列
循环
Sparetire
2018-03-13 01:21:19 +08:00
这不就是个滑动窗口,最后窗口缩小的事嘛。。
lightening
2018-03-13 05:16:10 +08:00
@ipwx
对你来说可能是“这么简单一个程序”,不过楼主既然想不出怎么写,那么最佳写法就是他能理解的写法。

我明白你的意思,10n 和 1n 实际的复杂度确实是有区别的。不过大 O 符号本来只就是阶数符号,我的意思只是说你用 O(10n) 和 O(n) 这两个符号数学上是等价的,你用它们来表示 10 倍的运算量差距不科学。
zhujinliang
2018-03-13 09:02:31 +08:00
来了,你们要的 O(n)版本:

var arr = [11,21,34,65,14,5,66,17,88,21,45,61,50]

var winSize = 10
var sum = arr.slice(0, winSize-1).reduce((acc, cur) => acc + cur)
var arrLen = arr.length
console.log(arr.map((item, index, arr) => {
if (index > 0) sum -= arr[index-1];
if (index+winSize <= arrLen) sum += arr[index+winSize-1];
return sum / Math.min(winSize, arrLen-index)
}))
est
2018-03-13 09:02:54 +08:00
window average?

好像是个经典面试题
ipwx
2018-03-13 10:27:09 +08:00
@msg7086 @lightening 确实,我大 O 符号用得不合适,这种细节应该注意一下,哈哈。不过我的意思很明确,这个场景需要考虑常数。
dd0754
2018-03-13 11:09:47 +08:00
const arr2 = [11, 21, 34, 65, 14, 5, 66, 17, 88, 21, 45, 61, 50].map((e, i, arr) => {
   const tmp = arr.slice(i, i + 11);
   return tmp.reduce((prev, current) => prev + current) / tmp.length;
});
console.log(arr2);
mashirozx
2018-03-13 11:34:22 +08:00
移动平均吗 233
zouyyu
2018-03-13 13:40:49 +08:00
function calAVG(array, interval){

if(!(Array.isArray(array))) throw '参数异常'

for(let ele of array){
if(isNaN(parseFloat(ele))){
throw '数组中有不能转换为数字的元素'
}
}

const result = [],
INTERVAL = interval || 1,
reducer = (accumulator, currentValue) => accumulator + currentValue;
let arrayLength = array.length;
if(arrayLength <= interval){
console.info(array.reduce(reducer))
result.push(array.reduce(reducer)/arrayLength);
return result;
}

for(let[index, value] of array.entries()){
let endIndex = index + INTERVAL,
currentArray = array.slice(index, endIndex + 1);

if(endIndex >= arrayLength){
result.push(currentArray.reduce(reducer)/(currentArray.length))
break;
}
result.push(currentArray.reduce(reducer)/(INTERVAL + 1));
}
return result;
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/437360

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX