web-dev-qa-db-ja.com

2次元配列での近傍の検索

2次元配列の要素の近傍(つまり、要素の周りの8つの要素)を見つける簡単な方法はありますか?次のように、さまざまな組み合わせでインデックスを減算および追加するだけでは不十分です。

array[i-1][i]
array[i-1][i-1]
array[i][i-1]
array[i+1][i]

... 等々。

25
Emil Svansø

(疑似コード)

row_limit = count(array);
if(row_limit > 0){
  column_limit = count(array[0]);
  for(x = max(0, i-1); x <= min(i+1, row_limit); x++){
    for(y = max(0, j-1); y <= min(j+1, column_limit); y++){
      if(x != i || y != j){
        print array[x][y];
      }
    }
  }
}

もちろん、元のハードコードされたソリューションとほぼ同じ行数が必要ですが、これを使用すると、 "近隣"をできる限り拡張できます(2〜3セル以上離れます)。

32
Seb

ベンは彼のアプローチについては正しいと思いますが、局所性を改善するために、私はそれを並べ替えるかもしれません。

array[i-1][j-1]
array[i-1][j]
array[i-1][j+1]

array[i][j-1]
array[i][j+1]

array[i+1][j-1]
array[i+1][j]
array[i+1][j+1]

境界チェックの問題を回避する1つのトリックは、配列の次元を必要以上に大きくすることです。だから、このような小さなマトリックス

3 1 4
1 5 9
2 6 5

実際には

0 0 0 0 0
0 3 1 4 0
0 1 5 9 0
0 2 6 5 0
0 0 0 0 0 

次に、合計するときに、両方の次元で1から3に添字を付けることができます。上記の配列参照は有効であることが保証され、最終的な合計には影響しません。例としてcとゼロベースの添え字を想定しています

13
EvilTeach

@sebの元の擬似コードからのJavascriptの実際の例を次に示します。

function findingNeighbors(myArray, i, j) {
  var rowLimit = myArray.length-1;
  var columnLimit = myArray[0].length-1;

  for(var x = Math.max(0, i-1); x <= Math.min(i+1, rowLimit); x++) {
    for(var y = Math.max(0, j-1); y <= Math.min(j+1, columnLimit); y++) {
      if(x !== i || y !== j) {
        console.log(myArray[x][y]);
      }
    }
  }
}
5
Ryan Alberts

あなたの言語がこれをサポートしている場合、@ SebaGRの代替:

var deltas = { {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1},
               {x=-1, y=0},               {x=1, y=0},
               {x=-1, y=1},  {x=0, y=1},  {x=1, y=1} };
foreach (var delta in deltas)
{
    if (x+delta.x < 0 || x + delta.x >= array.GetLength(0) ||
        y+delta.y < 0 || y + delta.y >= array.GetLength(1))
        continue;

    Console.WriteLine("{0}", array[x + delta.x, y + delta.y]);
}

読みやすさのわずかな利点、デルタを静的に割り当てることができる場合のパフォーマンスの可能性。

5
FryGuy

array[i][j]には隣人がいます

array[i-1][j]
array[i][j-1]
array[i-1][j-1]
array[i+1][j]
array[i][j+1]
array[i+1][j+1]
array[i+1][j-1]
array[i-1][j+1]

それがおそらく最も速く/最も簡単な方法は、すべての可能なネイバーをリストすることです。ただし、インデックスの範囲外チェックを行うようにしてください。

一部の言語ではこれを行うためのショートカット方法が提供されている可能性がありますが、私は知りません。

3
Ben S

これは、python3 +での@Sebの回答の実装であり、簡潔であり、ジェネレーターを使用して最大のパフォーマンスを実現します。

def neighbours(pos, matrix):
    rows = len(matrix)
    cols = len(matrix[0]) if rows else 0
    for i in range(max(0, pos[0] - 1), min(rows, pos[0] + 2)):
        for j in range(max(0, pos[1] - 1), min(cols, pos[1] + 2)):
            if (i, j) != pos:
                yield matrix[i][j]
3

Pythonでの便利なメソッドは次のとおりです。

def neighbors(array,pos):
    n = []
     string = "array[pos.y+%s][pos.x+%s]"
    for i in range(-1,2):
        for j in range(-1,2):
            n.append(eval(string % (i,j)))
    return n

Posが2D Pointオブジェクトであり、arrayが2D配列であると仮定します。

2
AdrianC

グリッド(ベクター2Dまたは1次元...ここでは問題ありません)
XおよびY、要素の座標(または、参照によってベクトル要素を渡すだけ...)

int neighbour(const Grid & g, const size_t & x, const size_t & y) {
    for (int i = -1; i < 2; ++i)
        for (int j = -1; j < 2; ++j)
            if (x + i >= 0 && x + i < g.row && y + j >= 0 && y + j < g.col)
                //Do some stuff
    return 0;
}
1
Tierno

要素の周りの行列には要素が8つしかないため、配列を使用してさまざまなインデックス値を格納できます。たとえば、

  int iarr[8] = {-1,-1,-1,0,0,+1,+1,+1};
  int jarr[8] = {-1,0,+1,-1,+1,-1,0,+1};
  for(int i = 0 ; i < 8 ; i++)
   {
    if(arr[x-iarr[i]][y-jarr[i]] == 1)
    {
     //statements
    }
   }  
  /* x and y are the position of elements from where you want to reach out its neighbour */

両方の配列に含まれる値は8つだけなので、スペースは問題にならない可能性があります。

1
Shubh Tripathi

JSサンプル:

    function findingNeighbors(myArray, i, j){
        return myArray.reduce(function(a, b, c){
            if(Math.max(0, i-1) <= c && c <= Math.min(i+1, myArray.length-1)){
                a = a.concat(
                    b.reduce(function(d, e, f){
                    if(f == j && c == i)
                        return d;
                    if(Math.max(0, j-1) <= f && f <= Math.min(j+1, myArray.length-1))
                        d.Push(e)
                    return d;
                    },[])
                );
            }
            return a;
        },[]);
    }
1
can_er

ここにC#のコードがあります:

public Cell[,] MeetNeigbours(Cell[,] Grid)
    {
        for (int X = 0; X < Grid.GetLength(0); X++)
        {
            for (int Y = 0; Y < Grid.GetLength(1); Y++)
            {
                int NeighbourCount = 0;
                for (int i = -1; i < 2; i++)
                {
                    for (int j = -1; j < 2; j++)
                    {
                        if (CellExists(Grid, (X + i)), (Y + j) && (i != 0 && j != 0))
                        {
                            Grid[X, Y].Neighbours[NeighbourCount] = Grid[(X + i), (Y + j)];
                        }
                        if(!(i == 0 && j == 0))
                        {
                            NeighbourCount++;
                        }
                    }
                }
            }
        }
        return Grid;
    }

    public bool CellExists(Cell[,] Grid, int X, int Y)
    {
        bool returnValue = false;
        if (X >= 0 && Y >= 0)
        {
            if (X < Grid.GetLength(0) && Y < Grid.GetLength(1))
            {
                returnValue = true;
            }
        }

        return returnValue;
    }

「Cell」クラスは次のようになります。

public class Cell
{
    public Cell()
    {
        Neighbours = new Cell[8];
    }

    /// <summary>
    /// 0 3 5
    /// 1 X 6
    /// 2 4 7
    /// </summary>
    public Cell[] Neighbours;
}
0
TDM

私が通常取るアプローチは、このブログの下部に記載されています: https://royvanrijn.com/blog/2019/01/longest-path/

ルートをハードコーディングしたり、2つのループをネストしたりする代わりに、8つの「ルート」に単一の整数ループを使用し、(i%3)-1および(i/3)-1を使用します。画像付きのブログをチェックしてください。

それほど深くネストされておらず、簡単に記述できます。多くのコードは必要ありません。

0
Roy van Rijn

多くはあなたのデータが何であるかに依存します。たとえば、2D配列が論理行列の場合、行を整数に変換し、ビットごとの演算を使用して必要な行を見つけることができます。

より汎用的なソリューションについては、SebaGRのソリューションのように、インデックス付けに悩まされていると思います。

0
Sarah Mei

行と列は行と列の総数です

CellIndex構造体またはクラスを定義します。または、インデックスの代わりに実際の値を返すこともできます。

public List<CellIndex> GetNeighbors(int rowIndex, int colIndex)
{
var rowIndexes = (new int[] { rowIndex - 1, rowIndex, rowIndex + 1 }).Where(n => n >= 0 && n < Rows);

var colIndexes = (new int[] { colIndex - 1, colIndex, colIndex + 1 }).Where(n => n >= 0 && n < Cols);

return (from row in rowIndexes from col in colIndexes where row != rowIndex || col != colIndex select new CellIndex { Row = row, Col = col }).ToList();
}
0
Krishna

これは最近のプロジェクトで私にとって本当に役に立ちました。Swiftでの@Sebの疑似コード実装は次のとおりです。これは、2次元配列が正方形であることを前提としています。

func adjacentIndexPaths(to indexPath: IndexPath) -> [IndexPath] {
var neighboringSquareIndexes: [IndexPath] = []

// gridSquareCount is the size of the 2D array. For example, in an 8 x 8 [[Array]], gridSquareCount is 8
let maxIndex = gridSquareCount - 1
var neighborRowIndex = max(0, indexPath.section - 1)
var neighborColumnIndex = max(0, indexPath.row - 1)

while neighborRowIndex <= min(indexPath.section + 1, maxIndex) {
    while neighborColumnIndex <= min(indexPath.row + 1, maxIndex) {
        if neighborRowIndex != indexPath.section || neighborColumnIndex != indexPath.row {
            neighboringSquareIndexes.append(IndexPath(row: neighborColumnIndex, section: neighborRowIndex))
        }
        neighborColumnIndex += 1
    }
    neighborRowIndex += 1
    neighborColumnIndex = max(0, indexPath.row - 1)
}

return neighboringSquareIndexes }
0
ArielSD
private ArrayList<Element> getNeighbors(Element p) {
    ArrayList<Element> n = new ArrayList<Element>();

    for (int dr = -1; dr <= +1; dr++) {
        for (int dc = -1; dc <= +1; dc++) {
            int r = p.row + dr;
            int c = p.col + dc;

            if ((r >= 0) && (r < ROWS) && (c >= 0) && (c < COLS)) {
                // skip p
                if ((dr != 0) || (dc != 0))
                    n.add(new Element(r, c));
            }               
        }
    }

    return n;
}
0
Vijay M

リスト内包表記のネストされたforループは少し見苦しいですが、これは短いです:

def neighbours(m, i, j):
    return [m[x][y] for x in [i-1,i,i+1] for y in [j-1,j,j+1] if x in range(0,len(m)) and y in range(0,len(m[x])) and (x,y) != (i,j)]
0
hansaplast