优化形状检测算法

时间:2016-09-14 12:01:34

标签: javascript

我有0和1的矩阵,我需要从该矩阵中提取“形状”。形状由1个连接在8个方向组成。在最终结果中,我获得了2d形状的数组。每个形状阵列都包含原始矩阵中的单元索引。我写了脚本并且它有效,除了它与更大的矩阵崩溃。我在控制台Uncaught RangeError: Maximum call stack size exceeded中收到错误我需要使用大型矩阵,如1000 x 1000甚至更多。

这是我的代码:

var matrixCols = 150;
var matrixRows = 150;
var matrix = [];
var shapes = [];
var checkedCels = [];


function createMatrix() {
	for(var i = 0; i < matrixRows; i++) {
  	var row = [];
		for(var j = 0; j < matrixRows; j++) {
    	var value = Math.round(Math.random());
      row.push(value);
    	matrix.push(value);
    }
    console.log(JSON.stringify(row));
  }	
}

function getShapes() {
	for(var i = 0; i < matrix.length; i++) {
  	if(checkedCels.indexOf(i) === -1 && matrix[i] === 1) {
    	shapes.push(formShape(i));
    }
  }
  
  console.log('Total shapes:', shapes.length);
  console.log(shapes);
}

function formShape(startIndex) {
	return getNeighbours(startIndex);
}

function getNeighbours(index) {
	if(checkedCels.indexOf(index) > -1) {
  	return [];
  }
  var cels = [index];
  checkedCels.push(index);
  
  var nwIndex = index - matrixCols - 1;
  var nwCel = matrix[nwIndex];
  if(typeof nwCel !== 'undefined' && nwCel === 1 && index % matrixCols > 0) {
  	cels = cels.concat(getNeighbours(nwIndex));
  }
  
  var nIndex = index - matrixCols;
  var nCel = matrix[nIndex];
  if(typeof nCel !== 'undefined' && nCel === 1) {
  	cels = cels.concat(getNeighbours(nIndex));
  }
  
  var neIndex = index - matrixCols + 1;
  var neCel = matrix[neIndex];
  if(typeof neCel !== 'undefined' && neCel === 1 && index % matrixCols < (matrixCols - 1)) {
  	cels = cels.concat(getNeighbours(neIndex));
  }
  
  var wIndex = index - 1;
  var wCel = matrix[wIndex];
  if(typeof wCel !== 'undefined' && wCel === 1 && index % matrixCols > 0) {
  	cels = cels.concat(getNeighbours(wIndex));
  }
  
  var eIndex = index + 1;
  var eCel = matrix[eIndex];
  if(typeof eCel !== 'undefined' && eCel === 1 && index % matrixCols < (matrixCols - 1)) {
  	cels = cels.concat(getNeighbours(eIndex));
  }
  
  var swIndex = index + matrixCols - 1;
  var swCel = matrix[swIndex];
  if(typeof swCel !== 'undefined' && swCel === 1 && index % matrixCols > 0) {
  	cels = cels.concat(getNeighbours(swIndex));
  }
  
  var sIndex = index + matrixCols;
  var sCel = matrix[sIndex];
  if(typeof sCel !== 'undefined' && sCel === 1) {
  	cels = cels.concat(getNeighbours(sIndex));
  }
  
  var seIndex = index + matrixCols + 1;
  var seCel = matrix[seIndex];
  if(typeof seCel !== 'undefined' && seCel === 1 && index % matrixCols < (matrixCols - 1)) {
  	cels = cels.concat(getNeighbours(seIndex));
  }
  
  return cels;
}

createMatrix();
getShapes();

我如何优化它?

1 个答案:

答案 0 :(得分:3)

您可以通过维护要检查的单元格列表(最初只包含getNeighbours)并在每次迭代时避免startIndex函数中的递归:

  • 从列表中取一个项目
  • 检查是否正常(未选中,该索引处的矩阵值为1)
  • 将其邻居添加到列表中以检查
  • 重复

当没有更多单元格要检查时,此循环停止。

通过不使用递归,程序将不会耗尽堆栈空间,并且算法将能够处理更大的矩阵(只要当前要检查的列表不超过可用内存)。

除此之外,还有另一种可能的优化:checkedCells目前是一个数组,要查看某个单元格是否已被分析,请使用.indexOf()(这是一个O( n)操作)。将checkedCells更改为保持已分析单元格的索引作为键的对象将此查找减少为O(1)。

根据以上两点修改您的代码:

&#13;
&#13;
console.clear();

var matrixCols = 7;
var matrixRows = 7;
var matrix = [];
var shapes = [];
var checkedCels = {};


function createMatrix() {
  for (var i = 0; i < matrixRows; i++) {
    var row = [];
    for (var j = 0; j < matrixRows; j++) {
      var value = Math.round(Math.random());
      // value = Math.random() > 0.75 ? 1 : 0; // to change the probability of a cell being "set"
      row.push(value);
      matrix.push(value);
    }
    console.log(row.map(function(x) {
      return " X" [x]
    }).join(''));
  }
}

function getShapes() {
  for (var i = 0; i < matrix.length; i++) {
    if (!checkedCels[i] && matrix[i] === 1) {
      shapes.push(formShape(i));
    }
  }

  console.log('Total shapes:', shapes.length);
  console.log(shapes);
}

function formShape(startIndex) {
  var cels = [];
  var toCheck = [startIndex];

  while (toCheck.length) {
    var index = toCheck.pop();

    if (checkedCels[index]) {
      continue;
    }

    if (matrix[index] !== 1) {
      continue;
    }

    cels.push(index);
    checkedCels[index] = 1;

    var neighbours = [];

    if (index % matrixCols > 0) {
      neighbours.push(index - matrixCols - 1); // NW
      neighbours.push(index - 1); // W
      neighbours.push(index + matrixCols - 1); // SW
    }
    if (index % matrixCols < (matrixCols - 1)) {
      neighbours.push(index - matrixCols + 1); // NE
      neighbours.push(index + 1); // E
      neighbours.push(index + matrixCols + 1); // SE
    }
    neighbours.push(index - matrixCols); // N
    neighbours.push(index + matrixCols); // S

    neighbours.forEach(function(n) {
      if (typeof matrix[n] !== 'undefined') {
        toCheck.push(n);
      }
    });
  }

  return cels;
}


createMatrix();
getShapes();
&#13;
&#13;
&#13;

为了便于阅读,我将矩阵大小限制在7x7,但上面的代码应在几秒钟内解决1000x1000矩阵。