迭代2d数组的布尔值,只留下最大的连续" 2D blob of the"

时间:2018-02-13 15:11:07

标签: php arrays algorithm 2d cellular-automata

好的,所以这个问题有点笨拙,但我希望这会让事情变得清晰。

我有这个样本2d数组。

$array = array(
array(1, 0, 0, 0, 1, 0, 0, 1),
array(0, 0, 1, 1, 1, 1, 0, 1),
array(0, 1, 1, 0, 1, 0, 0, 0),
array(0, 1, 1, 0, 0, 0, 1, 0),
array(1, 0, 0, 0, 1, 1, 1, 1),
array(0, 1, 1, 0, 1, 0, 1, 0),
array(0, 0, 0, 0, 0, 0, 0, 1)
);

当按行迭代(并用\ n终止每一行),然后对于按列迭代的每一行,它将回显如下:(░░= 0,▓▓= 1)

    ▓▓░░░░░░▓▓░░░░▓▓
    ░░░░▓▓▓▓▓▓▓▓░░▓▓
    ░░▓▓▓▓░░▓▓░░░░░░
    ░░▓▓▓▓░░░░░░▓▓░░
    ▓▓░░░░░░▓▓▓▓▓▓▓▓
    ░░▓▓▓▓░░▓▓░░▓▓░░
    ░░░░░░░░░░░░░░▓▓

但我想做的是"分析"数组只留下1个连续的形状(最多的#34;单元格"),在这个例子中,结果将是:

    ░░░░░░░░▓▓░░░░░░
    ░░░░▓▓▓▓▓▓▓▓░░░░
    ░░▓▓▓▓░░▓▓░░░░░░
    ░░▓▓▓▓░░░░░░░░░░
    ▓▓░░░░░░░░░░░░░░
    ░░▓▓▓▓░░░░░░░░░░
    ░░░░░░░░░░░░░░░░

我最初的做法是:

  1. 为每个▓▓单元格分配一个唯一的编号(无论是完全随机的,还是当前的迭代编号):

    01      02    03
        04050607  08
      0910  11      
      1213      14  
    15      16171819
      2021  22  23  
                  24
    
  2. 多次迭代数组:每次迭代,每个▓▓单元格在其邻居中占据最大的唯一数字。循环将无限期地继续,直到在当前状态和先前状态之间没有检测到变化。在最后一次迭代之后,结果将是:

    01      21    08
        21212121  08
      2121  21      
      2121      24  
    21      24242424
      2121  24  24  
                  24
    

    现在,这一切都归结为计算最多发生的价值。然后,再次迭代,将所有值不是最受欢迎的单元格转换为0,从而得到所需的结果。

  3. 但是,对于这样一个简单的任务,我觉得这是一个非常迂回,计算量很大的方法,必须有更好的方法。任何想法都会非常感激,欢呼!

    奖励积分:将所有blob划分为一个2D数组数组,按照单元格数排序,这样我们就可以用最小的blob做一些事情

3 个答案:

答案 0 :(得分:1)

我只能想到一些小改进:

  1. 保留非空字段的链接列表。在步骤2中,您不需要触摸n²矩阵元素,只需触摸链表中的元素即可。根据矩阵的稀疏程度,这可能会少得多。

  2. 您只需要比较右,右下,左下和下方向。否则,已从前一行/列检查其他方向。我的意思是:当我比右邻居更大时,我已经可以改变右邻居的号码了。 (同样适用于向下和向右)。这是比赛数量的一半。

答案 1 :(得分:1)

总是好玩,这些问题。之前完成了,所以我在这里转储我的代码,也许你可以使用其中的一些代码。这基本上遵循每个形状,通过观察细胞及其周围的8个细胞,如果它们连接到连接细胞,再看一下等等......

<?php 
$shape_nr=1;
$ln_max=count($array);
$cl_max=count($array[0]);
$done=[];

//LOOP ALL CELLS, GIVE 1's unique number
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
    if($array[$ln][$cl]===0)continue;
    $array[$ln][$cl] = ++$shape_nr;
}}

//DETECT SHAPES
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
    if($array[$ln][$cl]===0)continue;

    $shape_nr=$array[$ln][$cl];
    if(in_array($shape_nr,$done))continue;

    look_around($ln,$cl,$ln_max,$cl_max,$shape_nr,$array);
    //SET SHAPE_NR to DONE, no need to look at that number again
    $done[]=$shape_nr;
}}  

//LOOP THE ARRAY and COUNT SHAPENUMBERS
$res=array();
for($ln=0;$ln<$ln_max;++$ln){
for($cl=0;$cl<$cl_max;++$cl){
    if($array[$ln][$cl]===0)continue;
    if(!isset($res[$array[$ln][$cl]]))$res[$array[$ln][$cl]]=1;
    else $res[$array[$ln][$cl]]++;
}}

//get largest shape
$max = max($res);
$shape_value_max = array_search ($max, $res);

//get smallest shape
$min = min($res);
$shape_value_min = array_search ($min, $res);

// recursive function: detect connecting cells  
function look_around($ln,$cl,$ln_max,$cl_max,$nr,&$array){
    //create mini array
    $mini=mini($ln,$cl,$ln_max,$cl_max);
    if($mini===false)return false;

    //loop surrounding cells
    foreach($mini as $v){
        if($array[$v[0]][$v[1]]===0){continue;}
        if($array[$v[0]][$v[1]]!==$nr){
            // set shape_nr of connecting cell
            $array[$v[0]][$v[1]]=$nr;

            // follow the shape
            look_around($v[0],$v[1],$ln_max,$cl_max,$nr,$array);
            }
        }
    return $nr;
    }

// CREATE ARRAY WITH THE 9 SURROUNDING CELLS
function mini($ln,$cl,$ln_max,$cl_max){
    $look=[];   
    $mini=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];
    foreach($mini as $v){
        if( $ln + $v[0] >= 0 &&
            $ln + $v[0] < $ln_max &&
            $cl + $v[1] >= 0 &&
            $cl + $v[1] < $cl_max
            ){
            $look[]=[$ln + $v[0], $cl + $v[1]];
            }
        }

    if(count($look)===0){return false;}
    return $look;
    }

Here's a fiddle

答案 2 :(得分:0)

如果您的阵列大小不是很大并且内存不会成为问题,那么递归解决方案可能会更快。我在这里找到了一个c ++算法: https://www.geeksforgeeks.org/find-length-largest-region-boolean-matrix/