如何在javascript中快速缩放二维数组?
Posted
技术标签:
【中文标题】如何在javascript中快速缩放二维数组?【英文标题】:How to scale a two dimensional array in javascript fast? 【发布时间】:2018-09-12 04:47:37 【问题描述】:给定二维数组a:
let a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 1]
]
如何按给定因子对其进行缩放?例如,数组 b 是数组 a 缩放 4:
let b =[
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
]
这是我为执行此操作而编写的代码,但在处理大型数组 (200 x 200) 时速度很慢(客户端浏览器:Chrome),并且缩放比例为 16。
// scale an array by a factor of 'scale'
const scaledMatrixArray = (arr, scale) =>
let newArr = [];
arr.forEach((el) =>
let newArrRow = [];
el.forEach((el) =>
for (let j = 0; j < scale; j++)
newArrRow.push(el);
);
for(let i = 0; i < scale ; i++)
newArr.push(newArrRow);
);
return newArr;
;
我知道我的实现是 O(n^2) 的一些变体并且效率非常低。我正在寻找一种更好的方法来做到这一点,或者寻找一个做得更好更快的库。我的最终结果是,我的 N X N 数组具有超过 N > 200 可以以最有效、最快且内存占用最少的方式扩展到 800 x 800 的数组。
【问题讨论】:
请注意,任何采用N*N
数组并将其缩放某个因子 M
的实现都会产生总共 N*N*M
值,因此将是 O(mn^2 )
@Hamms 所以没有办法让它更快?
取决于你到底想做什么,可能有比制作一个巨大的数组更快/更好的方法
如果你真的很在意速度,你可以在任何地方使用for
循环而不是forEach
,它在执行大量重复操作时性能提升很小。
似乎您可以在 50x50 矩阵上找到路径并将生成的路径点乘以 16。meta.stackexchange.com/questions/66377/what-is-the-xy-problem
【参考方案1】:
这是一种非常简化的方式,使用Array().fill
,至少在我的浏览器中它的运行速度比其他答案快。
我添加了两个版本,一个使用扩展运算符,另一个使用.apply
。我使用apply
获得更快的结果。
function scaleSpread(array, factor)
const scaled = [];
for(const row of array)
let x = [];
for(const item of row)
x.push(...Array(factor).fill(item));
scaled.push(...Array(factor).fill(x));
return scaled;
function scaleApply(array, factor)
const scaled = [];
for(const row of array)
let x = [];
for(const item of row)
x.push.apply(x, Array(factor).fill(item));
scaled.push.apply(scaled, Array(factor).fill(x));
return scaled;
function scaleConcat(array, factor)
let scaled = [];
for(const row of array)
let x = [];
for(const item of row)
x = x.concat(Array(factor).fill(item));
scaled = scaled.concat(Array(factor).fill(x));
return scaled;
var a = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
console.time('spread');
scaleSpread(a, 10000);
console.timeEnd('spread');
console.time('apply');
scaleApply(a, 10000);
console.timeEnd('apply');
console.time('concat');
scaleConcat(a, 10000);
console.timeEnd('concat');
编辑:添加了一个使用 .concat
的版本,因为 apply 和 spread 导致 Maximum call stack size exceeded
具有非常大的数组。
【讨论】:
@EmmanuelNK 添加了另一个版本,使用apply
更快,至少在我的浏览器中是这样。
是的,这在我的浏览器中也明显更快。
我昨天没有发帖,因为在 node 中它比 Emmanuel 的慢。你得到什么号码? spread: 7.900ms apply: 1.200ms
我怀疑apply
比直接调用push
更快。 Anyhoos,传播时间约为 10 毫秒,适用于我的时间约为 7.6 毫秒
好的哇,这绝对是更快。我在浏览器中得到 2.8 毫秒的 concat【参考方案2】:
此方法使用for loop
,为确定的n
次迭代n 维数组。
这使用Array.splice 方法,通过获取源值并将其插入到数组中的某个索引处。
PS:源数组(a
)在这里发生了变异。但是,您始终可以克隆原始数组并根据需要为结果创建 b
。
var a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 1]
],
scale = 4,
scaleTheArray = function (arrayToScale, nTimes)
for (var idx = 0, i = 0, len = arrayToScale.length * nTimes; i < len; i++)
var elem = arrayToScale[idx];
/* Insert the element into (idx + 1) */
arrayToScale.splice(idx + 1, 0, elem);
/* Add idx for the next elements */
if ((i + 1) % nTimes === 0)
idx += nTimes + 1;
;
console.time('testScale');
/* 1. Expand each of the a[n] length */
for (var i = 0, len = a.length; i < len; i++)
var arr = a[i];
scaleTheArray(arr, scale - 1);
/* 2. Expand each of the a length */
scaleTheArray(a, scale - 1);
console.timeEnd('testScale');
【讨论】:
天哪,这速度很快。10000 scale = 1328.18995ms
@Zze 对你来说太糟糕了,对我来说10000 scale = 302.428955078125ms
8)
@choz 今天晚些时候继续关注我的个人电脑报告!
这速度非常快...我收到了10000 scale = 367.779052734375ms
@EmmanuelNK 会发布一个答案,我的方法至少在我的浏览器中比 choz' 快。 Choz 和 EmmanuelNK 愿意告诉我我发布后你得到了什么?【参考方案3】:
一般来说,更少的函数调用 = 更少的开销:
function scale1D(arr, n)
for (var i = arr.length *= n; i; )
arr[--i] = arr[i / n | 0]
function scale2D(arr, n)
for (var i = arr.length; i; )
scale1D(arr[--i], n)
scale1D(arr, n)
var a = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
console.time( 1e5 )
scale2D(a, 1e5)
console.timeEnd( 1e5 )
var b = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
scale2D(b, 4)
console.log( JSON.stringify( b ).replace(/],/g, '],\n ') )
主要的优化是在调整每一行的大小后,它们会重复,而不是创建所有的 # rows * scale 行。因此,不是处理 n * scale 数组,而是只处理 n 个数组。另一个可能的优化可能是在某些浏览器上,arr.length *= n
可能会一次分配所有需要的连续内存。
为了比较,上面的函数式方法慢了大约 2 倍:
const scale1D = (arr, n) => [...Array(arr.length * n)].map((_, i) => arr[i / n | 0])
const scale2D = (arr, n) => scale1D( arr.map((row, i) => scale1D(row, n)), n )
console.time( 1e5 )
let a = scale2D([ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ], 1e5)
console.timeEnd( 1e5 )
let b = scale2D([ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ], 4)
console.log( JSON.stringify( b ).replace(/],/g, '],\n ') )
【讨论】:
这要快得多。我应该考虑将其向后插入以使not using splice
成为可能。不错的方法。【参考方案4】:
有点有趣,如果你没有访问很多值,你可以懒惰地做。没有对这段代码进行太多测试,但应该可以工作
var a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 42]
],
scale = 4;
for (var idx = 0; idx < a.length; idx++)
a[idx] = new Proxy(a[idx],
get: function(target, i)
return target[Math.floor(i/scale)];
);
a = new Proxy(a,
get: function(target, i)
return target[Math.floor(i/scale)];
);
console.log(a[16-1][16-1])
for (var ii = 0; ii < 16;ii++)
for(var j=0;j<16;j++)
console.log(a[ii][j])
【讨论】:
以上是关于如何在javascript中快速缩放二维数组?的主要内容,如果未能解决你的问题,请参考以下文章