Javascript 双for循环性能问题
为模糊的标题道歉,我真的不知道该如何描述这个问题 我最近遇到一个例子,我必须循环一组对象来比较多个值,我选择在for循环中使用for循环来比较每个对象和其他对象 虽然这在小型阵列上运行良好,但一旦我的阵列变大一点(比如10000个对象),性能就会成为一个大问题 此数组包含以下类型的对象:Javascript 双for循环性能问题,javascript,arrays,performance,object,for-loop,Javascript,Arrays,Performance,Object,For Loop,为模糊的标题道歉,我真的不知道该如何描述这个问题 我最近遇到一个例子,我必须循环一组对象来比较多个值,我选择在for循环中使用for循环来比较每个对象和其他对象 虽然这在小型阵列上运行良好,但一旦我的阵列变大一点(比如10000个对象),性能就会成为一个大问题 此数组包含以下类型的对象: [{ char: '_', from: 0, to: 2, chrLength: 2 }, { char: '_', from: 0, to: 7, chrLength: 7 }, { char: 'a', f
[{ char: '_', from: 0, to: 2, chrLength: 2 },
{ char: '_', from: 0, to: 7, chrLength: 7 },
{ char: 'a', from: 1, to: 3, chrLength: 2 },
{ char: 'a', from: 1, to: 6, chrLength: 5 },
{ char: '_', from: 2, to: 7, chrLength: 5 },
{ char: 'a', from: 3, to: 6, chrLength: 3 }]
我的想法是,我只能选择from
和to
不与任何其他对象重叠的对象。(from
和to
是另一个数组中的索引)
因此,对于示例阵列,可能的结果是:
[{ char: '_', from: 0, to: 2, chrLength: 2 },
{ char: 'a', from: 1, to: 3, chrLength: 2 },
{ char: 'a', from: 1, to: 6, chrLength: 5 },
{ char: 'a', from: 3, to: 6, chrLength: 3 }]
我的处理方式如下:
var canUse = true,
posibilities = [];
for(i = 0; i < l; i++) {
canUse = true;
for(var j = 0; j < l; j++) {
if((results[i].from < results[j].from && results[i].to > results[j].to)) {
canUse = false;
break;
}
}
if(canUse) posibilities.push(results[i]);
}
var canUse=true,
可能性=[];
对于(i=0;iresults[j].to)){
canUse=false;
打破
}
}
如果(可以使用)可能性推送(结果[i]);
}
看到较大数组的性能非常糟糕,我想知道是否有更好的解决方案来实现这一点?首先在
chrLength
属性上对对象进行排序。查找阻止包含对象的对象时,只需检查至少短两个字符的对象
results.sort(function(x, y){ return x.chrLength - y.chrLength; });
var posibilities = [];
for (var i = 0; i < l; i++) {
var canUse = true, len = results[i].chrLength - 2;
for (var j = 0; results[j].chrLength <= len; j++) {
if((results[i].from < results[j].from && results[i].to > results[j].to)) {
canUse = false;
break;
}
}
if(canUse) posibilities.push(results[i]);
}
这将属性的检查从
和到
属性分为两个阶段,因此完整检查的数量(其中映射[j][k]。计算到
)实际上小于对象的总数
免责声明:当然,您需要验证代码是否正确。我已经检查了结果是否有相同数量的项目,但我没有比较每个项目。对于初学者来说,一旦
canUse
为false
您就不需要继续进行内部循环
您可以添加一个中断代码>或将第二个for循环更改为:
for(var j=0;canUse&(j
您可能会看到一个有用的加速。以下是想法():
您需要某种自平衡树来支持O(logn)的插入和删除操作。为了简单起见,我使用了红黑树李>
您需要使用间隔的中点作为键,即(从+到)/2
假设您已经将k
项“插入”到树中,并且即将插入k+1
。你的步骤是:
如果k+1
覆盖根-忽略它
如果k+1
被根覆盖,请从树中删除根并再次尝试
否则,通过比较k+1
的mid和根的mid,继续向左或向右子树
插入所有内容后,遍历收集每个节点的结果树
利润。。。我已经使用了你的数组的4倍,通过将它与自身合并。我的机器在Chrome下的结果是116ms(冷启动)和64ms(预热后)
代码
函数过程(){
console.log('长度的处理结果:'+l);
控制台。时间(“处理”);
var comparator=函数(a,b){//用于构建树的比较器
返回a.mid-b.mid;
},
isAinB=函数(a,b){//util函数,用于检查a是否在b内部
返回b.froma.to;
},
rbtree=newrbtree(comparator),//构建一个空树
i=结果。长度-1,项目,可能性=[];
函数检查(根,x){//递归检查器
var数据;
如果(!root){//树是空的,或者我们到达了一片叶子
rbtree.插入(x);
返回;
}
data=root.data;
if(isAinB(data,x)){//4
返回;
}
if(isAinB(x,data)){//5
删除(数据);
检查(rbtree.\u根,x);
返回;
}
检查(根[比较器(数据,x)>0?'left':'right'],x);//6
}
对于(;i>=0;i--){
项目=结果[i];
item.mid=(item.from+item.to)/2;//2
选中(rbtree._root,item);//3
}
rbtree.each(功能(项目){//7
可能性。推送(项目);
});
console.timeEnd(“处理”);
console.log(posibility.length);
}
顺便说一句,我用过这个。不确定它是否是最好的:)不应该{char:''u',from:2,to:7,chrLength:5}
在结果中?@LIUFA:不,因为{char:'a',from:3,to:6,chrLength:3}
适合它。@Jashwant不太确定你的意思,你能提供一个小例子吗?@woutr\u,对不起,误解了你的问题。删除了我的评论。正确的数据结构会有所帮助。很好的地方,完全忽略了这一点。谢谢,这似乎确实减少了检查的数量,但对于一些更大的阵列,仍然需要相当多的时间。只是想知道在执行for之前是否有任何方法可以消除项目loop@woutr_be:对于较大的集合,检查的数量如何变化?考虑到你不能得到比O(n)更好的,即至少每项检查一次。你能举一个更大的例子吗?在一个小集合中很难找到任何有用的模式,可以用于省略。当然,例如,这里有一个包含1000个项目的模式,还有一个更极端的模式:(虽然我的最终数组大小是原来的两倍)@woutru\u-be:Nice,这使得测试一些想法更容易。我做了一些改进,并在上面发布了代码。可能会进行更多的优化,但很难对代码中实际发生的情况进行分析,因为它几乎不会使用那么多数据运行。浏览器实际上崩溃了一次。:)伙计们,我觉得你们走错了路。您需要某种数据结构来回答“如果给定间隔
var map = [];
for (var i = 0; i < l; i++) {
var ch = results[i].chrLength;
while (map.length <= ch) map.push([]);
map[ch].push(results[i]);
}
for (var i = 1; i < map.length; i++) {
map[i].sort(function(x, y){ return x.from - y.from; });
}
var posibilities = [];
for (var i = 0; i < l; i++) {
var canUse = true, len = results[i].chrLength - 2, from = results[i].from, to = results[i].to;
for (var j = 1; canUse && j <= len; j++) {
if (map[j][map[j].length - 1].from > from) {
var k;
for (k = 0; map[j][k].from <= from; k++);
for (;k < map[j].length && map[j][k].from < to; k++) {
if (map[j][k].to < to) {
canUse = false;
break;
}
}
}
}
if(canUse) posibilities.push(results[i]);
}
function process() {
console.log('Processing results of length: ' + l);
console.time('Processing');
var comparator = function(a, b) { //Comparator to build a tree
return a.mid - b.mid;
},
isAinB = function(a, b) { //util function to check if a is inside b
return b.from < a.from && b.to > a.to;
},
rbtree = new RBTree(comparator), //Build an empty tree
i = results.length - 1, item, posibilities = [];
function check(root, x) { //Recursive checker
var data;
if(!root) { //Either tree is empty or we've reached a leaf
rbtree.insert(x);
return;
}
data = root.data;
if(isAinB(data, x)) { //4
return;
}
if(isAinB(x, data)) { //5
rbtree.remove(data);
check(rbtree._root, x);
return;
}
check(root[comparator(data, x) > 0 ? 'left' : 'right'], x); //6
}
for(; i >= 0; i--) {
item = results[i];
item.mid = (item.from + item.to)/2; //2
check(rbtree._root, item); //3
}
rbtree.each(function(item) { //7
posibilities.push(item);
});
console.timeEnd('Processing');
console.log(posibilities.length);
}