【问题标题】:Function slows down when working with high numbers处理大数字时功能变慢
【发布时间】:2015-11-02 19:20:45
【问题描述】:

我编写了一个脚本,它不会给出一组数据的实际平均值,而是返回一个包含大多数数据点的窗口。让我展示一些代码:

time.tic()
var selectedAverage = 0;
var highestPointCount = 0;
for (var i = 1; (i*step) <= maxValue; i++) {
    var dataPointCount = 0;
    for (var j = 0; j < myArray.length; j++) {
        if (myArray[j] >= minValue+(i-1)*step && myArray[j] <= minValue+i*step) {
            dataPointCount++;
        }
    }
    if (dataPointCount > highestPointCount) {
        highestPointCount = dataPointCount;
        selectedAverage = (minValue+(i-1)*step)+Math.round(0.5*step);
    }
}
console.log(time.toct().ms) 
return selectedAverage;

首先通过从最大值中减去最小值然后乘以 10 来计算步长值。因此有 10 个“水平”窗口。然后脚本计算每个窗口内的数据点数量并返回适当的平均值。

然而,当传入一个较大数字的数组(例如 1.000.000)时,脚本的速度似乎非常慢(有时超过 200 倍)。数组长度大约为 200,但总是相同的长度,因此它必须与实际值相关联。知道哪里出错了吗?

编辑: 获取步长值的代码:

var minValue = myArray.min();
var maxValue = myArray.max();
var step = Math.round((maxValue-minValue)/10);
if (step === 0) {
    step = 1
}

.min() 和 .max() 是附加到 Array 的原型。但这一切都进行得非常快。我已经测量了每一步,是 for 循环变慢了。

【问题讨论】:

  • 不确定步骤是什么?
  • 如果没有看到更多代码,很难判断代码应该做什么,但问题与嵌套循环有关,它看起来就像代码是做的工作比它应该做的多得多。
  • 当然,您可以只遍历数组一次并以数字方式确定每个值所在的“步长”。无需为每个间隔一遍又一遍地搜索整个数组.
  • 好点我可以改变这个。但我的具体问题是为什么某些数组在 ±0.5 毫秒内计算,而其他数组(长度相同但数字很大)可能需要长达 10 秒。
  • @SecondLemon 只需将 for (var i = 1 更改为 for (var i = minValue 并查看差异。

标签: javascript performance loops average


【解决方案1】:

如果我正确理解了您的算法,这应该会消除所有不必要的计算并且速度会更快:

var arr = [];
var maxQty=0;
var wantedAverage = 0;
for (var j = 0; j < 11; j++) {
    arr[j]=0;
}
for (var j = 0; j < myArray.length; j++) {
    var stepIndex = Math.floor((myArray[j]-minValue)/step)
    arr[stepIndex]+=1;

    if(arr[stepIndex] > maxQty ){
        wantedAverage = minValue + stepIndex*step +Math.round(0.5*step);
        maxQty = arr[stepIndex]
    }
}
console.log(maxQty, wantedAverage)

我们只遍历数组的每个元素一次,并计算它所属窗口的索引,将数量数组加一。然后如果我们在窗口中找到更多的点,我们会更新wantedAverage

【讨论】:

  • 我现在正在实施它。我会尽快通知您!
  • 我真的很喜欢你的方法。但是,似乎还有什么不对劲的地方。它每次返回 0, 0。我会自己看一下,看看这里有什么不同。乍一看它应该工作。编辑:哎呀我的错误。注释掉步数计算。完美运行!
  • @SecondLemon 尝试使用您的数据数组编辑 jsfiddle,我可以检查
  • 我已经成功了。奇怪的是,时间并没有太大的不同。甚至稍微慢一点:10 秒 931.118273 毫秒。与 10 秒 ±555 毫秒相比。不过干净多了!
  • @SecondLemon 如果数组的长度为 200,则运行此代码不应该花费 10 秒,应该不到一秒。您是多次运行此代码还是您的数组长度更大?
【解决方案2】:

我对你的问题有两种不同的看法:

  1. 删除了不必要的/重复的计算

在您的嵌套代码中,您每次都会为 minValueistep 的相同值计算 minValue+(i-1)*stepminValue+i*step。 您应该在它变成的第二个 for 循环之前将其拉起:

var dataPointCount = 0;
var lowerLimit = minValue+(i-1)*step;
var higherLimit = minValue+1*step;
for (var j = 0; j < myArray.length; j++) {
    if (myArray[j] >= lowerLimit && myArray[j] <= higherLimit) {
        dataPointCount++;
    }
}
  1. 您在处理大数据阵列时受到严重的性能影响,这可能是由内存交换引起的。从您的角度来看,您正在处理单个数组实例,但是当您拥有如此大的数组时,JavaScript VM 不太可能访问连续的内存空间来保存所有这些值。 JavaScript VM 很可能必须处理从操作系统获取的多个内存块,并且必须花费额外的精力来翻译在读/写期间哪个值在哪里。

【讨论】:

  • 感谢您的第一个建议很好,但并没有提高速度。它从 10 秒 553 毫秒变为 10 秒 513 毫秒。主要问题是 Pointy 发现的问题(在主帖的 cmets 中)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 2016-04-28
  • 2017-07-28
  • 2012-01-25
相关资源
最近更新 更多