填充填充算法选择具有最少邻居数的像素

时间:2013-11-06 13:54:45

标签: javascript performance algorithm flood-fill

我正在使用plate tectonics simulator来使用改变用途的flood fill algorithm来检测大陆。算法大致相同。唯一的区别是它适用于顶点而不是像素。

我一直在努力改善这种行为的质量 - 我想看看当它忽略具有两个邻居或更少的大陆地壳的像素/顶点时它的表现如何。是否存在支持此功能的泛洪填充算法的现有变体?

我的(有点简化)代码如下:

var group = [];
var stack = [initialVertex];
while(stack.length > 0){
    var next = stack.pop();
    if (group.indexOf(next) != -1 && isContinental(next)){
        group.push(next);
        stack = stack.concat(getNeighbors(next));
    }
}
return group 

2 个答案:

答案 0 :(得分:1)

最后并不太难。我需要做的是将isContinental检查移动到相邻的顶点。我不能说这是否是最有效的方法,但确实有效:

var group = [];
var stack = [initialVertex];
while(stack.length > 0){
    var next = stack.pop();
    if (group.indexOf(next) != -1){
        var neighbors = getNeighbors(next).filter(isContinental)
        if (neighbors.length > 3){
            group.push(next);
            stack = stack.concat(neighbors);
        }
    }
}
return group;

答案 1 :(得分:1)

从理论上讲,在集合上执行的算法中跳过元素应该减少该算法所花费的时间。速度增加是否显着取决于您的数据集以及检查每个元素所花费的时间。如果有很多情况会应用跳过条件,那么说算法效率会提高似乎是合理的。

Let C = the time taken to execute such a skipping algorithm
    S = the total number of skipped elements
    s = the time taken to check if an element should be skipped
    E = the total number of elements
    e = the time taken to process an element

C = (E - S) * e + E * s

如果(C < E * e),则该算法对于该特定数据集比对没有跳过条件更有效。下图演示了检查元素花费处理元素10%的方案。随着越来越多的元素被跳过,函数的成本自然会降低。

graph of C


考虑到您使用填充算法的情况,由于问题的图形性质和可变的初始坐标,可能很难提前知道是否跳过某些元素会有所帮助。一方面,通过跳过该顶点可以从该过程中消除与一个顶点相邻的整个元素链,另一方面,检查的成本可能超过优点。一些测试将按照您的特定数据集进行。

如果您的简化代码准确反映了实际代码,那么我想指出几个问题。

  1. 的indexOf

    使用indexOf检查扩展数组中是否存在元素比检查哈希映射或关联数组中元素的存在要慢得多。这样做的原因是,随着阵列在操作过程中扩展,检查元素是否在该阵列中变得越来越昂贵。哈希映射往往要快得多,代价是一些内存。您可以通过使用与每个元素的某些独特特征(例如ID)相关联的简单{}对象来执行此类操作。 (同样,您的原始示例代码中存在拼写错误。indexOf在找不到元素时返回-1,并将结果与​​!=进行比较==。)

  2. 的concat

    使用concat将一个数组连接到另一个数组实际上会产生一个全新的数组。当您继续使用数组连接数组时,会产生大量不必要的垃圾。保留原始堆栈阵列并将其推上去是非常有效的。


  3. 我已经设置了一个 jsperf demo ,演示了一些可以对您的算法进行的更改,关于基本效率,例如使用关联地图,不使用Array.concat,忽略恰好是顶点本身的顶点的邻居,当然还有跳过元素。与任何优化问题一样,您应个人资料 优先以查找您的程序大部分时间花在哪里以及基准更改在代码中。我希望这对你有所帮助,祝你好运!

    演示中使用的最重要代码的副本如下:

    function hasSufficientContinentalNeighbors(vert) {
        var i = vert.neighbors.length, n = 0;
    
        if (i <= 2) { return false; }
    
        while (i--) {
            if (isContinental(vert.neighbors[i])) {
                // Return true at nearest opportunity.
                if (++n === 3) { return true; }
            }
        }
    
        return false;
    }
    
    // Skip (w/o redundancies)
    var group = [], grouped = {};
    var stack = [initialVertex];
    var next, nearby, i;
    while (stack.length > 0) {
        next = stack.pop();
        if (grouped[next.id] === undefined && isContinental(next) && hasSufficientContinentalNeighbors(next)) {
            group.push(next);
            grouped[next.id] = true;
            nearby = getNeighbors(next);
            i = nearby.length;
            while (i--) {
                if (nearby[i] !== next) { stack.push(nearby[i]); }
            }
        }
    }