27

行と列でnby matrixを指定nすると、隣接するすべての値を循環スパイラルで反復処理したいと思います。Mij

これを行うポイントは、M に依存する関数 をテストしてf、 から離れた半径を見つける(i, j)ことfですTrue。したがって、f次のようになります。

def f(x, y):
    """do stuff with x and y, and return a bool"""

次のように呼び出されます。

R = numpy.zeros(M.shape, dtype=numpy.int)
# for (i, j) in M
for (radius, (cx, cy)) in circle_around(i, j):
    if not f(M[i][j], M[cx][cy]):
       R[cx][cy] = radius - 1
       break

circle_around循環スパイラルのインデックス (へのイテレータ) を返す関数はどこにありますか。したがって、 のすべての点についてM、このコードは をf返すその点からの半径を計算して格納しますTrue

を計算するより効率的な方法があればR、私もそれを受け入れます。


アップデート:

回答を提出してくれたすべての人に感謝します。circle_aroundイテレータからの出力をプロットして、イテレータが何をするかを示す短い関数を作成しました。回答を更新するか、新しい回答を投稿する場合は、このコードを使用してソリューションを検証できます。

from matplotlib import pyplot as plt
def plot(g, name):
    plt.axis([-10, 10, -10, 10])
    ax = plt.gca()
    ax.yaxis.grid(color='gray')
    ax.xaxis.grid(color='gray')

    X, Y = [], []
    for i in xrange(100):
        (r, (x, y)) = g.next()
        X.append(x)
        Y.append(y)
        print "%d: radius %d" % (i, r)

    plt.plot(X, Y, 'r-', linewidth=2.0)
    plt.title(name)
    plt.savefig(name + ".png")

結果は次のとおり plot(circle_around(0, 0), "F.J")です。 circle_around by FJ

plot(circle_around(0, 0, 10), "WolframH"): circle_around by WolframH

マグネシウムの提案を次のようにコーディングしました。

def circle_around_magnesium(x, y):
    import math
    theta = 0
    dtheta = math.pi / 32.0
    a, b = (0, 1) # are there better params to use here?
    spiral = lambda theta : a + b*theta
    lastX, lastY = (x, y)
    while True:
        r = spiral(theta)
        X = r * math.cos(theta)
        Y = r * math.sin(theta)
        if round(X) != lastX or round(Y) != lastY:
            lastX, lastY = round(X), round(Y)
            yield (r, (lastX, lastY))
        theta += dtheta

plot(circle_around(0, 0, 10), "magnesium"): circle_around by マグネシウム

ご覧のとおり、私が探しているインターフェイスを満たす結果はどれも、0、0 の周りのすべてのインデックスをカバーする円形のらせんを生成していません。注文。

4

7 に答える 7

13

ポイントの順序は重要ではないと述べたのでarctan2、指定された半径でポイントが現れる角度 ( ) で並べただけです。Nより多くのポイントを獲得するために変更します。

from numpy import *
N = 8

# Find the unique distances
X,Y = meshgrid(arange(N),arange(N))
G = sqrt(X**2+Y**2)
U = unique(G)

# Identify these coordinates
blocks = [[pair for pair in zip(*where(G==idx))] for idx in U if idx<N/2]

# Permute along the different orthogonal directions
directions = array([[1,1],[-1,1],[1,-1],[-1,-1]])

all_R = []
for b in blocks:
    R = set()
    for item in b:
        for x in item*directions:
            R.add(tuple(x))

    R = array(list(R))

    # Sort by angle
    T = array([arctan2(*x) for x in R])
    R = R[argsort(T)]
    all_R.append(R)

# Display the output
from pylab import *
colors = ['r','k','b','y','g']*10
for c,R in zip(colors,all_R):
    X,Y = map(list,zip(*R))

    # Connect last point
    X = X + [X[0],]
    Y = Y + [Y[0],]
    scatter(X,Y,c=c,s=150)
    plot(X,Y,color=c)

axis('equal')
show()

を与えるN=8

ここに画像の説明を入力

その他のポイントN=16(色弱の方はごめんなさい):

ここに画像の説明を入力

これは明らかに円に近づき、半径が大きくなる順にすべてのグリッドポイントに当たります。

ここに画像の説明を入力

于 2012-03-06T21:04:29.867 に答える
10

距離を増やしながらポイントを生成する1つの方法は、ポイントを簡単なパーツに分割てから、パーツの結果をマージすることです。マージを行う必要があることはかなり明白です。簡単な部分です。固定xの場合、ポイント(x、y)はyの値のみを見て順序付けできるためです。itertools.merge

以下は、そのアルゴリズムの(単純な)実装です。ユークリッド距離の2乗が使用され、中心点が含まれていることに注意してください。最も重要なのは、xが含まれるポイント(x、y)のみrange(x_end)が考慮されることですが、これはユースケース(上記の表記法のどこx_endにあるか)には問題ないと思います。n

from heapq import merge
from itertools import count

def distance_column(x0, x, y0):
    dist_x = (x - x0) ** 2
    yield dist_x, (x, y0)
    for dy in count(1):
        dist = dist_x + dy ** 2
        yield dist, (x, y0 + dy)
        yield dist, (x, y0 - dy)

def circle_around(x0, y0, end_x):
    for dist_point in merge(*(distance_column(x0, x, y0) for x in range(end_x))):
        yield dist_point

編集:テストコード:

def show(circle):
    d = dict((p, i) for i, (dist, p) in enumerate(circle))
    max_x = max(p[0] for p in d) + 1
    max_y = max(p[1] for p in d) + 1
    return "\n".join(" ".join("%3d" % d[x, y] if (x, y) in d else "   " for x in range(max_x + 1)) for y in range(max_y + 1))

import itertools
print(show(itertools.islice(circle_around(5, 5, 11), 101)))

テストの結果(ポイントは、生成された順序で番号が付けられますcircle_around):

             92  84  75  86  94                
     98  73  64  52  47  54  66  77 100        
     71  58  40  32  27  34  42  60  79        
 90  62  38  22  16  11  18  24  44  68  96    
 82  50  30  14   6   3   8  20  36  56  88    
 69  45  25   9   1   0   4  12  28  48  80    
 81  49  29  13   5   2   7  19  35  55  87    
 89  61  37  21  15  10  17  23  43  67  95    
     70  57  39  31  26  33  41  59  78        
     97  72  63  51  46  53  65  76  99        
             91  83  74  85  93                

編集2:本当に負の値が必要な場合は、関数でiに置き換えます。range(end_x)range(-end_x, end_x)cirlce_around

于 2012-02-12T23:59:44.087 に答える
2

のループベースの実装は次のcircle_around()とおりです。

def circle_around(x, y):
    r = 1
    i, j = x-1, y-1
    while True:
        while i < x+r:
            i += 1
            yield r, (i, j)
        while j < y+r:
            j += 1
            yield r, (i, j)
        while i > x-r:
            i -= 1
            yield r, (i, j)
        while j > y-r:
            j -= 1
            yield r, (i, j)
        r += 1
        j -= 1
        yield r, (i, j)
于 2012-01-23T22:35:47.907 に答える
0

あなたが何をしようとしているのか完全にはわかりませんが、次のように始めます。

def xxx():
    for row in M[i-R:i+R+1]:
        for val in row[j-R:j+r+1]:
            yield val

スパイラルにどのくらいの順序が必要かわかりませんが、それは重要ですか? Rの昇順にする必要がありますか?または、特定の方位角から時計回りに開始しますか?

マンハッタンの R の距離測定値は? ユークリッド?他の何か?

于 2012-02-12T16:10:03.567 に答える
0

私がすることは、アルキメデスのらせんの方程式を使用することです。

r(theta) = a + b*theta

を使用して、極座標 (r,theta) を (x,y) に変換します。

x = r*cos(theta)
y = r*sin(theta)

cosそして図書館sinにいます。math次に、結果の x と y を整数に丸めます。配列の最終インデックスを取得するために、開始インデックスによって x と y を後でオフセットできます。

ただし、f が true を返す最初の半径を見つけることにのみ関心がある場合は、次の擬似コードを実行する方が有益だと思います。

for (i,j) in matrix:
    radius = sqrt( (i-i0)^2 + (j-j0)^2) // (i0,j0) is the "center" of your spiral
    radiuslist.append([radius, (i,j)])
sort(radiuslist) // sort by the first entry in each element, which is the radius
// This will give you a list of each element of the array, sorted by the
// "distance" from it to (i0,j0)
for (rad,indices) in enumerate(radiuslist):
    if f(matrix[indices]):
        // you found the first one, do whatever you want
于 2012-02-13T03:52:41.900 に答える
0

うーん、これは私が今まで思いついた中で最高です。しかし、多分それはあなたを助けるでしょう。実際には循環イテレータではないため、テスト関数を引数として受け入れる必要がありました。

問題:

  • 配列外のポイントをスキップするように最適化されていません
  • 正方形イテレータを引き続き使用しますが、最も近い点を見つけます
  • 私はnumpyを使用していないので、リストのリスト用に作成されています。変更する必要がある2つのポイントがコメントされています
  • 読みやすいように、正方形イテレータを長い形式のままにしました。それはもっと乾燥している可能性があります

これがコードです。あなたの質問に対する重要な解決策は、最上位の「spiral_search」関数です。これは、最も近い点が確実に見つかるように、正方形のらせんイテレータの上に追加のロジックを追加します。

from math import sqrt

#constants
X = 0
Y = 1

def spiral_search(array, focus, test):
    """
    Search for the closest point to focus that satisfies test.
    test interface: test(point, focus, array)
    points structure: [x,y] (list, not tuple)
    returns tuple of best point [x,y] and the euclidean distance from focus
    """
    #stop if focus not in array
    if not _point_is_in_array(focus, array): raise IndexError("Focus must be within the array.")
    #starting closest radius and best point
    stop_radius = None
    best_point = None 
    for point in _square_spiral(array, focus):
        #cheap stop condition: when current point is outside the stop radius
        #(don't calculate outside axis where more expensive)
        if (stop_radius) and (point[Y] == 0) and (abs(point[X] - focus[X]) >= stop_radius):
            break #current best point is already as good or better so done
        #otherwise continue testing for closer solutions
        if test(point, focus, array):
            distance = _distance(focus, point)
            if (stop_radius == None) or (distance < stop_radius):
                stop_radius = distance
                best_point = point
    return best_point, stop_radius

def _square_spiral(array, focus):
    yield focus
    size = len(array) * len(array[0]) #doesn't work for numpy
    count = 1
    r_square = 0
    offset = [0,0]
    rotation = 'clockwise'
    while count < size:
        r_square += 1
        #left
        dimension = X
        direction = -1
        for point in _travel_dimension(array, focus, offset, dimension, direction, r_square):
            yield point
            count += 1
        #up
        dimension = Y
        direction = 1
        for point in _travel_dimension(array, focus, offset, dimension, direction, r_square):
            yield point
            count += 1
        #right
        dimension = X
        direction = 1
        for point in _travel_dimension(array, focus, offset, dimension, direction, r_square):
            yield point
            count += 1
        #down
        dimension = Y
        direction = -1
        for point in _travel_dimension(array, focus, offset, dimension, direction, r_square):
            yield point
            count += 1

def _travel_dimension(array, focus, offset, dimension, direction, r_square):
    for value in range(offset[dimension] + direction, direction*(1+r_square), direction):
        offset[dimension] = value
        point = _offset_to_point(offset, focus)
        if _point_is_in_array(point, array):
            yield point

def _distance(focus, point):
    x2 = (point[X] - focus[X])**2
    y2 = (point[Y] - focus[Y])**2
    return sqrt(x2 + y2)

def _offset_to_point(offset, focus):
    return [offset[X] + focus[X], offset[Y] + focus[Y]]

def _point_is_in_array(point, array):
    if (0 <= point[X] < len(array)) and (0 <= point[Y] < len(array[0])): #doesn't work for numpy
        return True
    else:
        return False
于 2012-02-12T13:07:03.513 に答える