在矩阵中找到1和0的岛


29

给定二维矩阵0和1s。找出1和0的岛数,其中邻居仅在水平和垂直方向。

Given input:

1 1 1 0
1 1 1 0

output = 1 1
Number of 1s island = 1

xxx-
xxx-

Number of 0s island = 1 

---x
---x

------------------------------

Given input:

0 0 0 0
1 1 1 1
0 0 0 0
1 1 1 1

output = 2 2
Number of 1s island = 2

----
xxxx  <-- an island of 1s
----
xxxx  <-- another island of 1s

Number of 0s island = 2

xxxx  <-- an island
----
xxxx  <-- another island
----

------------------------------

Given input:

1 0 0
0 0 0
0 0 1
output = 2 1
Number for 1's island = 2:

x--  <-- an island of 1s
---
--x  <-- an island of 1s

Number of 0's island = 1:

-xx  \
xxx   > 1 big island of 0s
xx-  / 


------------------------------

Given input:

1 1 0
1 0 0
output = 1 1
Number for 1's island =1 and number of 0's island = 1

------------------------------

Given input:

1 1
1 1
output = 1 0
Number for 1's island =1 and number of 0's island = 0

11
您应该添加一个测试用例[[1,0];[0,1]],以确保不包括对角线连接
Sanchises

8
我建议只要指定顺序,输出就可以采用任何顺序-不添加任何值来强制执行命令
streetster

8
欢迎光临本站!
Arnauld

1
质疑中应澄清评论中回答的内容。更具体地说,如果您确实希望我们在0之前返回1,则应明确说明。
Arnauld

4
建议的测试用例:11111 / 10001 / 10101 / 10001 / 111112 1
Kevin Cruijssen,

Answers:


16

APL(Dyalog Unicode)29 28字节SBCS

-1感谢@Adám

{≢∪∨.∧⍨⍣≡2>+/↑|∘.-⍨⍸⍵}¨⊂,~∘⊂

在线尝试!

⊂,~∘⊂ 矩阵及其取反

{ 为他们每个人做

⍸⍵ 1s坐标对列表

+/↑|∘.-⍨ 曼哈顿距离矩阵

2> 邻居矩阵

∨.∧⍨⍣≡ 传递闭包

≢∪ 唯一行数


真的很聪明。您能否详细说明为什么保证最后一行有效—即,为什么唯一行等同于答案。另外,“传递闭包”是否像J一样^:_
约拿

1
@Jonah看到聊天
NGN

16

J,57个字节

,&([:(0#@-.~~.@,)](*@[*[:>./((,-)#:i.3)|.!.0])^:_ i.@$)-.

在线尝试!

这是其中的想法非常简单(我认为很有趣)的那些例子之一,但是执行起来却有一些机械上的冗长性,这掩盖了简单性……例如,将原始矩阵在所有方向上以0 fill填充都是冗长的((,-)#:i.3) |.!.0

这种机械上的冗长可能会进一步打高尔夫球,明天晚上我可能会尝试,但是现在我要发布它的症结所在。

说我们的输入是:

0 0 0 0
1 1 1 1
0 0 0 0
1 1 1 1

我们从大小相同的唯一整数矩阵开始:

 0  1  2  3
 4  5  6  7
 8  9 10 11
12 13 14 15

然后,对于每个单元,我们找到其所有邻居的最大值,然后乘以输入掩码:

 0  0  0  0
 8  9 10 11
 0  0  0  0
13 14 15 15

我们重复此过程,直到矩阵停止更改为止:

 0  0  0  0
11 11 11 11
 0  0  0  0
15 15 15 15

然后计算唯一的非零元素的数量。这告诉我们1岛的数量。

我们对“ 1减去输入”应用相同的过程,以获得0岛数。


3
这看起来很像一个“填充”机制,确实很整洁。
Matthieu M.

7

JavaScript(ES7), 138 ...  107106 字节

返回一个数组[ones, zeros]

f=(m,X,Y,V=.5,c=[0,0])=>m.map((r,y)=>r.map((v,x)=>V-v|(x-X)**2+(y-Y)**2>1||f(m,x,y,v,r[c[v^1]++,x]=2)))&&c

在线尝试!

怎么样?

我们使用一个递归函数。在初始通话期间,我们寻找01。每当我们发现这样的起点,我们增加相应岛计数器(c[0]c[1]),并输入相位递归到洪水填充类似的相邻小区的区域2的。

为了节省字节,根迭代和递归迭代使用完全相同的代码,但是其行为略有不同。

在第一次迭代中:

  • V=0.5V-v0v=0v=1个
  • XY(xX)2+(yY)2(x,y)

在递归迭代中:

  • c2c[v ^ 1]++C

已评论

f = (                 // f is a recursive function taking:
  m,                  //   m[]  = input binary matrix
  X, Y,               //   X, Y = coordinates of the previous cell, initially undefined
  V = .5,             //   V    = value of the previous cell, initially set to 0.5
                      //          so that the integer part of V - v is 0 for v = 0 or 1
  c = [0, 0]          //   c[]  = array of counters of 1's and 0's islands
) =>                  //          (or an integer when called recursively)
  m.map((r, y) =>     // for each row r[] at position y in m[]:
    r.map((v, x) =>   //   for each value v at position x in r[]:
      V - v |         //     abort if |V - v| ≥ 1
      (x - X) ** 2 +  //     or X and Y are defined and the quadrance between
      (y - Y) ** 2    //     (X, Y) and (x, y)
      > 1 ||          //     is greater than 1
      f(              //     otherwise, do a recursive call to f:
        m,            //       leave m[] unchanged
        x, y,         //       pass the new coordinates
        v,            //       pass the new reference value
        r[c[v ^ 1]++, //       increment c[v ^ 1] (ineffective if c is an integer)
          x           //       and set the current cell ...
        ] = 2         //       ... to 2
      )               //     end of recursive call
    )                 //   end of inner map()
  ) && c              // end of outer map(); return c

此代码不适用于大型矩阵(如100 * 100),由于堆栈溢出,只能使用1或0。
KB欢乐

3
@KBjoy除非在挑战中另有明确说明,否则我们的默认规则是,只要基础算法在理论上对任何输入有效,我们就不会在乎实现限制。(这是有关此内容的元文章,但某个地方可能有一个更相关的文章。)
Arnauld

7

MATL14 12字节

,G@-K&1ZIugs

在线尝试!验证所有测试用例

说明

,        % Do twice
  G      %   Push input
  @      %   Push iteration index: first 0, then 1
  -      %   Subtract. This converts 0 and 1 into -1 and 0 in the second iteration 
  K      %   Push 4
  &1ZI   %   Label connected components of matrix using 4-connectedness. Zeros in the
         %   matrix are background. This replaces the nonzeros by 1, 2, 3, ..., where 
         %   each number defines a connected component
  u      %   Unique values. This gives [0; 1; 2; ..., L], where L is the number of
         %   connected components.
  g      %   Convert nonzeros to 1
  s      %   Sum. This gives L, to be output
         % End (implicit).
         % Display stack (implicit)

6

K(ngn / k)60 55 51 50 46字节

{#?{|/'x*\:x}/2>+/x*x:x-\:'x:(0,#*x)\&,/x}'~:\

在线尝试!

~:\ 一对输入及其取反(字面意思:取反迭代收敛)

{ }' 每个

,/x 压扁arg

&1在哪里?-索引列表

(0,#*x)\ divmod width(input)获取ys和xs的两个单独的列表

x-\:'x: 每轴距离∆x和∆y

x*x: 摆正他们

+/ 加上∆x²和∆y²

2> 邻居矩阵

{|/'x*\:x}/ 传递闭包

#? 计算唯一行


看到您的回答后,我很高兴我没有尝试在K中解决这个问题:)
streetster

2
@streetster哈哈,谢谢!那不是我想要的效果:)我实际上是想鼓励人们学习k和其中的任何一种方言
ngn

6

Wolfram语言(Mathematica)64 62字节

Max@MorphologicalComponents[#,CornerNeighbors->1<0]&/@{#,1-#}&

在线尝试!

感谢attinat:我们可以写1<0而不是False保存两个字节。

非高尔夫版本:

F[M_] := {Max[MorphologicalComponents[M,   CornerNeighbors -> False]], 
          Max[MorphologicalComponents[1-M, CornerNeighbors -> False]]}

当然,有一个Mathematica内置函数,MorphologicalComponents它接受一个数组(或图像)并返回相同的值,每个形态连接的岛的像素都被岛索引替换。取Max这个结果的得出孤岛的数量(背景零保留为零,孤岛索引从1开始)。我们需要分别对数组(给出1个岛的数量)和减去数组(给出0个岛的数量)分别进行此操作。为了确保对角线邻居不算作邻居,CornerNeighbors->False需要提供选项。


-2个字节,因为不等式的优先级高于Rule
attinat

5

Python 3中,144个 127字节

该解决方案使用cv2的出色图像处理能力。尽管cv不太好用,超长且可读的方法名,但它击败了其他Python答案!

打高尔夫球:

import cv2,numpy as n
f=lambda b:n.amax(cv2.connectedComponents(b*255,0,4)[1])
def g(a):b=n.array(a,n.uint8);print(f(1-b),f(b))

展开:

import cv2
import numpy as np

# Finds the number of connected 1 regions 
def get_components(binary_map):
    _, labels = cv2.connectedComponents(binary_map*255, connectivity=4) # default connectivity is 8
    # labels is a 2d array of the binary map but with 0, 1, 2, etc. marking the connected regions
    components = np.amax(labels)
    return components

# Takes a 2d array of 0s and 1s and returns the number of connected regions
def solve(array): 
    binary_map = np.array(input_map, dtype=np.uint8)
    black_regions = get_components(1 - binary_map) # 0s
    white_regions = get_components(binary_map) # 1s
    return (black_regions, white_regions)

我对Python不太熟悉,但是为什么需要显式的参数名称?不仅是4代替,connectivity=4而且n.uint8dtype=n.uint8不可能的吗?
凯文·克鲁伊森

@KevinCruijssen,如果跳过可选参数,则需要参数名称。看一下文档,实际上我不必跳过,这为我节省了很多字节。谢谢!
丹尼尔

好的,我以为是这样,但是当我看文档时,我只能找到一个cv2.connectedComponents方法,所以我很困惑,认为可能有其他原因需要参数名称。如我所说,我对Python不太熟悉。我从中学到的就是这里的CCGC。;)但是使用变量名来跳过其他可选参数是有意义的。
凯文·克鲁伊森

1
非常好!我在这里找到了一个在线编译器,其中包括cv2模块。
吉斯

5

J46 44 43字节

-1个字节感谢@miles

,&#&~.&([:+./ .*~^:_:2>1#.[:|@-"1/~4$.$.)-.

在线尝试!

测试和@jonah答案中,& -.包装被盗

,& -. 对于输入及其取反,请执行以下操作:

4$.$. 1s的(y,x)坐标为n×2矩阵

1#.[:|@-"1/~ 曼哈顿距离:abs(∆x)+ abs(∆y)

2> 邻居矩阵

[:+./ .*~^:_: 传递闭包

#&~.&( ) 唯一行数


1
您可以编写长度和唯一性来保存另一个字节,即,&#&~.避免上限[:
英里

@miles谢谢
NGN

3

视网膜0.8.2,155字节

s`1(.*)
;$1a
}+`(?<=(.)*)(1|;)(.*¶(?<-1>.)*(?(1)$))?(?!\2)[1;]
;$3;
s`0(.*)
:$1b
}+`(?<=(.)*)(0|:)(.*¶(?<-1>.)*(?(1)$))?(?!\2)[0:]
:$3:
\W+(a*)(b*)
$.1 $.2

在线尝试!链接包括测试用例。说明:

s`1(.*)
;$1a

如果有一个1;请将其更改为并将a附加a到输入的末尾,以免干扰。

}+`(?<=(.)*)(1|;)(.*¶(?<-1>.)*(?(1)$))?(?!\2)[1;]
;$3;

1s中的所有相邻s 填充为Flood ;

}

重复直到的所有孤岛1都变成;s。

s`0(.*)
:$1b

如果存在,请将其0更改为:并将a附加b到输入的末尾,以免干扰。

+`(?<=(.)*)(0|:)(.*¶(?<-1>.)*(?(1)$))?(?!\2)[0:]
:$3:

0s中的所有相邻s 填充为Flood :

}

重复直到的所有孤岛0都变成:s。

\W+(a*)(b*)
$.1 $.2

分别计算1s和0s 的孤岛数。


3

Haskell中228个 227 225 224字节

import Data.List
z=zipWith
a!b=div(max(a*a)(a*b))a
l x=z(!)(z(!)x(0:x))$tail x++[0]
s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]

在线尝试!

说明:

此解决方案的想法如下:用每个单元格中唯一的值初始化矩阵,正值为1,负值为0。然后重复比较每个像元与其相邻像元,如果该像元具有相同的符号,但其绝对值较大的数字,则用该像元的数字替换该像元的数字。一旦达到固定点,就为1区域数计算不同的正数,为区域数计算不同的负数0

在代码中:

s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]

可以分为预处理(为单元分配编号),迭代和后处理(对单元计数)

预处理

预处理部分是功能

z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]

它使用z的缩写zipWith剃掉几个字节。我们在这里所做的是压缩二维数组,在行上具有整数索引,在列上具有奇数整数索引。我们这样做是因为我们可以(i,j)使用公式从一对整数中构建唯一的整数(2^i)*(2j+1)。如果仅为生成奇数整数j,则可以跳过计算2*j+1,节省三个字节。

有了唯一的数字,我们现在只需要根据矩阵中的值乘以一个符号,就可以得到 2*x-1

迭代

迭代是通过

(until=<<((==)=<<))((.)>>=id$transpose.map l)

由于输入是列表列表的形式,因此我们对每一行执行邻居比较,对矩阵进行转置,对每一行再次执行比较(由于转置而导致了以前的列),然后再次进行转置。完成这些步骤之一的代码是

((.)>>=id$transpose.map l)

l比较函数(在下面详细说明)在哪里,并transpose.map l执行比较和换位步骤的一半。(.)>>=id执行两次它的参数,这是点的无点形式,\f -> f.f在这种情况下由于运算符优先级规则而缩短了一个字节。

l在上一行中定义为l x=z(!)(z(!)x(0:x))$tail x++[0]。此代码(!)通过x依次将列表0:x与右移列表和左移列表压缩在一起,首先对每个单元格执行比较运算符(请参见下文),首先是其左邻居,然后是其右邻居tail x++[0]。我们使用零来填充移位列表,因为它们永远不会出现在预处理矩阵中。

a!b在此行上方定义为a!b=div(max(a*a)(a*b))a。我们在这里要做的是以下区分大小写:

  • 如果sgn(a) = -sgn(b),我们在矩阵中有两个相对的区域,不希望将它们统一,则a保持不变
  • 如果是sgn(b) = 0,我们有拐角处b的空白处,因此a保持不变
  • 如果为sgn(a) = sgn(b),我们希望将这两个区域统一起来,并采用具有更大绝对值的区域(为方便起见)。

请注意,sgn(a)永远不能0。我们使用给定的公式完成此操作。如果留下的痕迹ab不同,a*b小于或等于零,而a*a总是大于零,所以我们选择它作为最大和鸿沟与a回去a。否则,max(a*a)(a*b)为is abs(a)*max(abs(a),(abs(b)),并将其除以a,我们得到sgn(a)*max(abs(a),abs(b)),它是绝对值较大的数字。

为了使函数迭代((.)>>=id$transpose.map l)直到达到固定点,我们使用(until=<<((==)=<<)),该函数取自这个stackoverflow答案

后期处理

对于后处理,我们使用

(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)

这只是步骤的集合。

(>>=id)将列表列表压缩为单个列表, nub去除双精度, (\x->length.($x).filter<$>[(>0),(<0)])将列表划分为一对列表,一个为正数,一个为负数,并计算其长度。


2

爪哇10,359个 355 281 280 261 246字节

int[][]M;m->{int c[]={0,0},i=m.length,j,t;for(M=m;i-->0;)for(j=m[i].length;j-->0;)if((t=M[i][j])<2)c[t^1]+=f(t,i,j);return c;}int f(int v,int x,int y){try{if(M[x][y]==v){M[x][y]|=2;f(v,x+1,y);f(v,x,y+1);f(v,x-1,y);f(v,x,y-1);}}finally{return 1;}}

-74字节感谢@NahuelFouilleul

在线尝试。

说明:

int[][]M;              // Integer-matrix on class-level, uninitialized

m->{                   // Method with integer-matrix parameter and integer-array return-type
  int c[]={0,0}        //  Counters for the islands of 1s/0s, starting both at 0
      i=m.length,      //  Index of the rows
      j,               //  Index of the columns
      t;               //  Temp-value to decrease the byte-count
  for(M=m;             //  Set the class-level matrix to the input-matrix
      i-->0;)          //  Loop over the rows
    for(j=m[i].length;j-->0)
                       //   Inner loop over the columns
      if((t=M[i][j])   //    Set the temp value `t` to the value of the current cell
         <2)           //    And if this value is a 0 or 1:
        c[t^1]+=       //     Increase the corresponding counter by:
          f(t,i,j);    //      Call the recursive flood-fill method with value `t`
                       //      Which always returns 1 to increase the counter
  return c;}           //  After the nested loops: return the counters-array as result

// Recursive method with value and cell-coordinate as parameters,
// This method will flood-fill the matrix, where 0 becomes 2 and 1 becomes 3
int f(int v,int x,int y){
  try{if(M[x][y]==v){  //   If the cell contains the given value:
    M[x][y]|=2;        //    Fill the cell with 0→2 or 1→3 depending on the value
    f(v,x+1,y);        //    Do a recursive call downwards
    f(v,x,y+1);        //    Do a recursive call towards the right
    f(v,x-1,y);        //    Do a recursive call upwards
    f(v,x,y-1);}       //    Do a recursive call towards the left
  }finally{return 1;}} //  Ignore any ArrayIndexOutOfBoundsExceptions with a finally-return,
                       //  which is shorter than manual checks
                       //  And return 1 to increase the counter

1
-74字节,删除克隆并使用|=2:0-> 2和1-> 3,但是>0更改为==1
Nahuel Fouilleul

抱歉,我必须删除测试,以便tio链接适合评论
Nahuel Fouilleul

@NahuelFouilleul谢谢,聪明的使用|=2!而且我仍然可以通过首先检查来<2代替==1-1字节使用0(因此它们被更改为2,然后使用<2进行检查1(更改为3))
Kevin Cruijssen

2

Python 3,167字节

def f(m):
 n=[0,0];i=-2
 for r in m:
  j=0;i+=1
  for c in r:n[c^1]+=1-((i>=0)*(m[i][j]==c)*(1+({*r[:j]}=={c})*({*m[i][:j]}=={c^1}))or(j>0)*(r[j-1]==c));j+=1
 print(n)

在线尝试!


Python 2,168字节

def f(m):
 n=[0,0];i=-2
 for r in m:
	j=0;i+=1
	for c in r:n[c^1]+=1-((i>=0)*(m[i][j]==c)*(1+(set(r[:j])=={c})*(set(m[i][:j])=={c^1}))or(j>0)*(r[j-1]==c));j+=1
 print n

在线尝试!

-2个字节,感谢Kevin Cruijssen

+2字节格式修复

说明

计数器保持0和1。对于矩阵中的每个条目,执行以下操作:

  • 将当前值增加1
  • 如果相同的值直接位于上方或左侧,则减少1

对于左对齐的情况,这会导致误报,例如

0 0 1
1 1 1

要么

0 1
1 1

如果出现这种情况,计数器将减少1。

返回值为 [#1, #0]


1
恐怕第二条评论中提到的操作顺序应该是[#1, #0]。强制执行此操作毫无意义,但现在是这样。无论如何,您可以将{not c}转到{c^1},并通过类似的方法更改n[c]+=为解决我提到的n[c^1]+=问题。不错的答案,但我+1。:)
凯文·克鲁伊森

啊,你是对的。谢谢!
吉特

1

Perl 5(-0777p),110个字节

可以改进,使用正则表达式替换13,然后替换02

/
/;$m="(.{@-})?";sub f{($a,$b,$c)=@_;1while s/$b$m\K$a|$a(?=$m$b)/$b/s||s/$a/$b/&&++$c;$c}$_=f(1,3).$".f(0,2)

蒂奥


1

果冻44 36字节

ŒJfⱮ+€¥Ø.,UŻ¤œịƇþ,¬$¹ƇfƇⱮ`ẎQ$€QƲÐL€Ẉ

在线尝试!

单子链接接受整数列表作为其参数,并按该顺序返回1和0个岛的数量的列表。

说明

步骤1

生成所有矩阵索引的列表,每个矩阵索引的右边(除非在右侧)和向下(除非在底部)具有其邻居的索引

ŒJ            | Multi-dimensional indices (e.g. [1,1],[1,2],[1,3],[2,1],[2,2],[2,3])
      ¥       | Following as as a dyad:
  fⱮ          | - Filter the indices by each of:
    +€      ¤ |   - The indices added to the following
       Ø.     |     - 0,1
         ,U   |     - Paired with itself reversed [0,1],[1,0]
           Ż  |     - Prepended with zero 0,[0,1],[1,0]

第2步

通过输入中包含1还是0拆分这些索引。返回一个索引列表,其邻居为1s,另一个为0s。

  Ƈþ   | Filter each member of the output of stage 1 using the following criteria:
œị   $ | - Corresponding value for the multi-dimensional indices in each of the following as a monad:
   ,¬  |   - The input paired with its inverse

第三步

合并具有共同成员和输出计数的成员的列表

           ƲÐL€  | For each of the outputs from stage 2, do the following as a monad and repeat until no changes
¹Ƈ               | - Filter out empty lists (only needed on first pass through but included here to save a byte)         
  fƇⱮ`           | - Take each list of indices and filter the list of indices for those containing a match for any of them
        $€       | - For each resulting list of lists:
      Ẏ          |   - Tighten (concatenate top level of lists)
       Q         |   - Uniquify
          Q      | - Uniquify
               Ẉ | Finally output the lengths of the final lists

1

T-SQL 2008,178个字节

输入是一个表变量。

x和y是坐标

v是值0和1(也可以处理其他数值)

本示例中使用的测试数据:

100
000
001
DECLARE @ table(x int, y int, v int)

INSERT @ values
(1,1,1),(1,2,0),(1,3,0),
(2,1,0),(2,2,0),(2,3,0),
(3,1,0),(3,2,0),(3,3,1)
SELECT*,y-x*99r INTO # FROM @
WHILE @@rowcount>0UPDATE #
SET r=b.r
FROM #,# b
WHERE abs(#.x-b.x)+abs(#.y-b.y)=1and #.v=b.v and #.r>b.r
SELECT v,count(distinct r)FROM #
GROUP BY v

在线尝试


1

R194172字节

function(m,u=!1:2){for(i in 1:2){w=which(m==i-1,T)
N=1:nrow(w)
A=!!N
for(s in N){u[i]=u[i]+A[s]
while(any(s)){A[s]=F
s=c(N[as.matrix(dist(w))[s[1],]==1&A],s[-1])}}}
rev(u)}

在线尝试!

从等于1(或零)的矩阵的每个像元开始执行深度优先搜索。

  • -2个字节,感谢@Giuseppe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.