重置多维数组的最快方法?

Posted

技术标签:

【中文标题】重置多维数组的最快方法?【英文标题】:Fastest way to reset a multidimensional array? 【发布时间】:2012-10-27 20:53:07 【问题描述】:

假设我有一个二维数组:vectors[x][y],初始数组结构如下:

vectors = [    
 [0, 0, 0, 0, 0,],
 [0, 0, 0, 0, 0,],
 [0, 0, 0, 0, 0,],
 [0, 0, 0, 0, 0,],
 [0, 0, 0, 0, 0,]
]

经过一些计算,数组中的数据是随机的。将数组返回到初始状态的最快和最有效的方法是什么?

我知道我可以硬编码上面的归零数组并再次设置向量等于它,但我也知道这样的算法:

for (var x = 0; x < vectors.length; x++) 
    for (var y = 0; y < vectors[x].length; y++) 
        vectors[x][y] = 0;
    


是 O(x * y)。

那么哪种方法更好呢?有没有更好、更快/更有效的方法来解决这个问题?

对于将任意长度的多维数组归零的一般情况,最好的方法是什么? (如果重要的话,我正在使用 javascript

【问题讨论】:

可能相关:***.com/questions/1295584/… 您是否尝试过这两种方法并分别对它们进行了分析? @lc。我总是事先知道数组的长度,并且它们是手动初始化的。对于我的问题的特殊情况,数组是 5x5。 为什么我们担心 5x5 阵列的 Big-O 性能?我想你可以展开整个嵌套循环。 @A.Webb:因为纯粹的理论兴趣! (这就是为什么我也要求一般情况) 【参考方案1】:

这是我的两分钱:

我会保留原始阵列的干净副本以获得最快的性能。您可以保留引用的硬编码副本

var vectorsOrig = [    
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0]
];

或使用 slice 对初始数组进行动态干净克隆((在您的情况下递归地进行深度复制):

var clonedVectors = [0, 0, 0, 0, 0].slice(0);

无论如何,采用将矢量引用重置为原始副本的方法将比循环遍历并重置每个节点更快。如果您的旧向量数组对象不再被引用,JavaScript 将垃圾收集它。

话虽如此,问题就变成了每次都获得一份干净的副本。拥有一次硬编码实例将为您提供一个干净的副本,之后您必须克隆它。您也不想通过与重置选项类似的 for 循环进行动态生成。我的建议是编写一个克隆函数,它只返回一个新的硬编码或初始化数组:

function newVector() 
    return [    
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0]
    ];

var vector = newVector();
vector[1][2] = 11;
console.dir(vector);
vector = newVector();  // your old array will be garbage-collected if no longer referenced by any other reference
console.dir(vector);

理想情况下,最好对各种方法进行基准测试。

编辑 感谢 Vega 的输入,我修改了他的测试以测试三种方法。在 Chrome 和 IE9 中,这个解决方案似乎是最快的,在 FF (15.0.1) 中手动迭代似乎更快(FF 中的内存分配/管理可能更慢)。 http://jsperf.com/array-zero-test/2

【讨论】:

不幸的是,切片似乎比更新节点要慢jsperf.com/array-zero-test 这看起来像是 Mohammad Goudarzi 的解决方案,不过你解释得更好一些。 取决于环境(浏览器)。对于 Windows Server 2008 R2 / 7 64 位上的 Chrome 22.0.1229.96 32 位,它更快。 brute 实际上是最快的解决方案吗?我在首选项中添加了map(),突然对我来说 brute 更快,但在测试 2 中 newcopy 对我来说更快。奇怪。 @cbayram,可能是因为当您在测试 2 中点击“美化”代码时,jspref 为每个数组添加了一个额外的空白:[0, 0, 0, 0, 0,],?查看jsperf.com/array-zero-test/3。 编辑: 现在 brute 在测试 2 中的表现也比 newcopy 好...有趣... @ChrisBlake,取决于环境。我个人会坚持使用 newCopy 作为大多数实现中最快的。 [0, 0, 0, 0, 0,] 应该是 [0, 0, 0, 0, 0],我在之前的测试中错过了。【参考方案2】:

到目前为止,听起来我们有两种可能的选择。

    用零覆盖整个内容。 (您的解决方案)

    记录所有修改过的元素并只重置那些。 record[0].x = 3; record[0].y = 5; 等等 但是,您仍然需要在记录中循环一次。 为了进一步解释,我的意思是每次数组中的元素设置为一个值时,您应该记录该元素在数组中的位置。然后,使用循环,您可以访问每个元素并将其设置为 0。因此,如果您有一个稀疏数组,它会更有效。

根据实现,我可以理解为什么你会想要#2 而不是#1...但是如果你真的有一个足够大的矩阵,你需要担心分析算法,你可能会考虑做某种服务器预处理。

【讨论】:

我并不特别担心数据将由谁处理。我对哪种解决方案总体上最快更感兴趣。 我愿意说我的第二个解决方案将在 O(n) 时间内执行,其中 n 是修改元素的数量。最坏情况下的性能是 n = 整个数组。 这看起来是一个特别好的解决方案,但我不太确定我是否完全理解方法 #2。你能重新解释一下吗? 感谢您的重新解释。但是,访问财产的成本有多高? for (var i=0;i&lt;records.length;i++) vectors[records[i].x][records[i].y]] = 0; 在我的算法课程中,您会说内存访问是 1 个时间单位。因此,要评估您刚刚发布的代码,我会说 var i = 0 是一个分配,1 个单位。 i 【参考方案3】:

查看问题的另一种不同方法是使用线性数组并在更新时根据 x、y 索引计算线性索引。

然后初始化只是一个 for 循环,时间为 O(x+y)

【讨论】:

这不是还是 O(x*y),因为你仍然遍历整个数组——即使它表示为一维数组?【参考方案4】:

我敢说,为所有元素分配相同值的最快方法是调用 Array.map()。

但是,这里有一个问题。请注意,这将在本机实现该方法的浏览器上具有令人难以置信的快速性能,并且在其他浏览器中将具有通常的性能。另请注意,.map() 在某些旧浏览器中不可用,因此您需要使用 Underscore.js 或任何其他提供该方法的库。

【讨论】:

不,即使是原生地图通常也比 for 循环慢

以上是关于重置多维数组的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何重新索引多维数组的所有子数组元素?

JS----使用归递获取多维数组中的某列值

多维数组

Python:创建多维零数组的多维数组

多维数组作为方法的参数

数组的使用 多维数组