web-dev-qa-db-ja.com

望ましい近傍の程度に基づいてユニットを並べ替える方法は? (処理中)

Kostas Terzidis教授の最新の出版物 Permutation Design:Buildingsを読んでいるときに最近遭遇した、建築計画の生成を可能にするアルゴリズムを実装するのに助けが必要でしょう。 、テキストおよびコンテキスト (2014)。

[〜#〜]コンテキスト[〜#〜]

  • グリッドシステム(a)に分割されたサイト(b)を考えます。
  • サイトの制限内に配置されるスペースのリスト(c)と隣接マトリックスを検討して、これらのスペースの配置条件と隣接関係を決定します(d)

enter image description here

テルジディス教授の引用:

「この問題を解決する方法は、すべてのスペースがフィットし、制約が満たされるまで、グリッド内にスペースを確率的に配置することです。」

上の図は、そのような問題とサンプルの解決策を示しています(f)。

ALGORITHM(本に簡単に説明されています)

1 /「各スペースは、望ましい近傍の度合いに従ってソートされた他のすべてのスペースを含むリストに関連付けられています。」

2 /「次に、各スペースの各ユニットがリストから選択され、サイトに適合し、隣接する条件が満たされるまで、サイトにランダムに1つずつ配置されます(失敗した場合、プロセスが繰り返されます)」

ランダムに生成された9つの計画の例:

enter image description here

このアルゴリズムはブルートフォーステクニックに依存していないことを著者が後で説明することを付け加えておきます

[〜#〜]問題[〜#〜]

ご覧のとおり、説明は比較的あいまいで、step 2は(コーディングに関して)かなり不明確です。これまでのところ「パズルのピース」だけです。

  • 「サイト」(選択された整数のリスト)
  • 隣接行列(ネストされたリスト)
  • 「スペース」(リストの辞書)

各ユニット:

  • 直接の近傍を返す関数
  • ソートされた順序でのインデックスを持つその望ましい近傍のリスト
  • 実際の隣人に基づくフィットネススコア

    from random import shuffle
    
    n_col, n_row = 7, 5
    to_skip = [0, 1, 21, 22, 23, 24, 28, 29, 30, 31]
    site = [i for i in range(n_col * n_row) if i not in to_skip]
    fitness, grid = [[None if i in to_skip else [] for i in range(n_col * n_row)] for e in range(2)]
    
    n = 2
    k = (n_col * n_row) - len(to_skip)
    rsize = 50
    
    #Adjacency matrix
    adm = [[0, 6, 1, 5, 2],
           [6, 0, 1, 4, 0],
           [1, 1, 0, 8, 0],
           [5, 4, 8, 0, 3],
           [2, 0, 0, 3, 0]]
    
    
    spaces = {"office1": [0 for i in range(4)], 
              "office2": [1 for i in range(6)], 
              "office3": [2 for i in range(6)],
              "passage": [3 for i in range(7)],
              "entry": [4 for i in range(2)]}
    
    
    def setup():
        global grid
        size(600, 400, P2D)
        rectMode(CENTER)
        strokeWeight(1.4)
    
        #Shuffle the order for the random placing to come
        shuffle(site)
    
        #Place units randomly within the limits of the site
        i = -1   
        for space in spaces.items():
            for unit in space[1]:    
                i+=1
                grid[site[i]] = unit
    
    
        #For each unit of each space... 
        i = -1   
        for space in spaces.items():
            for unit in space[1]:    
                i+=1
    
                #Get the indices of the its DESIRABLE neighbors in sorted order
                ada = adm[unit]
                sorted_indices = sorted(range(len(ada)), key = ada.__getitem__)[::-1]
    
                #Select indices with positive weight (exluding 0-weight indices)
                pindices = [e for e in sorted_indices if ada[e] > 0] 
    
                #Stores its fitness score (sum of the weight of its REAL neighbors)
                fitness[site[i]] = sum([ada[n] for n in getNeighbors(i) if n in pindices])
    
        print 'Fitness Score:', fitness
    
    def draw():
        background(255)
    
        #Grid's background
        fill(170)
        noStroke()
        rect(width/2 - (rsize/2) , height/2 + rsize/2 + n_row , rsize*n_col, rsize*n_row)
    
    
        #Displaying site (grid cells of all selected units) + units placed randomly
        for i, e in enumerate(grid):
            if isinstance(e, list): pass
            Elif e == None: pass
            else:
                fill(50 + (e * 50), 255 - (e * 80), 255 - (e * 50), 180)
                rect(width/2 - (rsize*n_col/2) + (i%n_col * rsize), height/2 + (rsize*n_row/2) + (n_row - ((k+len(to_skip))-(i+1))/n_col * rsize), rsize, rsize)
                fill(0)
                text(e+1, width/2 - (rsize*n_col/2) + (i%n_col * rsize), height/2 + (rsize*n_row/2) + (n_row - ((k+len(to_skip))-(i+1))/n_col * rsize))
    
    
    
    
    def getNeighbors(i):
        neighbors = []
    
        if site[i] > n_col and site[i] < len(grid) - n_col:
            if site[i]%n_col > 0 and site[i]%n_col < n_col - 1:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col]) 
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
    
        if site[i] <= n_col:
            if site[i]%n_col > 0 and site[i]%n_col < n_col - 1:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
    
            if site[i]%n_col == 0:
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
    
            if site[i] == n_col-1:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
    
        if site[i] >= len(grid) - n_col:
            if site[i]%n_col > 0 and site[i]%n_col < n_col - 1:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col])
    
            if site[i]%n_col == 0:
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col])
    
            if site[i]%n_col == n_col-1:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col])
    
        if site[i]%n_col == 0:
            if site[i] > n_col and site[i] < len(grid) - n_col:
                if grid[site[i]+1] != None: neighbors.append(grid[site[i]+1])
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col])
    
        if site[i]%n_col == n_col - 1:
            if site[i] > n_col and site[i] < len(grid) - n_col:
                if grid[site[i]-1] != None: neighbors.append(grid[site[i]-1])
                if grid[site[i]+n_col] != None: neighbors.append(grid[site[i]+n_col])
                if grid[site[i]-n_col] != None: neighbors.append(grid[site[i]-n_col])
    
        return neighbors
    

enter image description here

誰かが点をつないで説明してくれると助かります。

  • 望ましい近傍の程度に基づいてユニットを並べ替える方法?

[〜#〜]編集[〜#〜]

ご存じの方もいらっしゃると思いますが、アルゴリズムは特定のスペース(ユニットで構成される)が隣接している可能性に基づいています。ロジックは、各ユニットがサイトの制限内でランダムに配置するようにします。

  • 直接隣人(上、下、左、右)を事前にチェックします
  • 2つ以上の近傍がある場合、適合度スコアを計算します。 (=これらの2+近傍の重みの合計)
  • 隣接確率が高い場合、最後にそのユニットを配置します

おおよそ、これは次のように変換されます。

    i = -1   
    for space in spaces.items():
        for unit in space[1]:    
            i+=1

            #Get the indices of the its DESIRABLE neighbors (from the adjacency matrix 'adm') in sorted order
            weights = adm[unit]
            sorted_indices = sorted(range(len(weights)), key = weights.__getitem__)[::-1]

            #Select indices with positive weight (exluding 0-weight indices)
            pindices = [e for e in sorted_indices if weights[e] > 0] 


            #If random grid cell is empty
            if not grid[site[i]]:

                #List of neighbors
                neighbors = [n for n in getNeighbors(i) if isinstance(n, int)]

                #If no neighbors -> place unit
                if len(neighbors) == 0:
                    grid[site[i]] = unit 

                #If at least 1 of the neighbors == unit: -> place unit (facilitate grouping)
                if len(neighbors) > 0 and unit in neighbors:
                    grid[site[i]] = unit  

                #If 2 or 3 neighbors, compute fitness score and place unit if probability is high
                if len(neighbors) >= 2 and len(neighbors) < 4:

                    fscore = sum([weights[n] for n in neighbors if n in pindices]) #cumulative weight of its ACTUAL neighbors
                    count = [1 for t in range(10) if random(sum(weights)) < fscore] #add 1 if fscore higher than a number taken at random between 0 and the cumulative weight of its DESIRABLE neighbors

                    if len(count) > 5: 
                        grid[site[i]] = unit

                #If 4 neighbors and high probability, 1 of them must belong to the same space
                if len(neighbors) > 3:

                    fscore = sum([weights[n] for n in neighbors if n in pindices]) #cumulative weight of its ACTUAL neighbors                    
                    count = [1 for t in range(10) if random(sum(weights)) < fscore] #add 1 if fscore higher than a number taken at random between 0 and the cumulative weight of its DESIRABLE neighbors

                    if len(count) > 5 and unit in neighbors:                       
                        grid[site[i]] = unit


            #if random grid cell not empty -> pass
            else: pass

(隣接確率が低いため)ユニットのかなりの部分が最初の実行に配置されないことを考えると、すべてのユニットをフィットできるランダムな分布が見つかるまで、何度も繰り返す必要があります。

enter image description here

数千回の反復の後、適合が見つかり、すべての隣接要件が満たされます。

ただし、このアルゴリズムでは、提供されている例のように、分割されていない均一なスタックではなく、分割されたグループがどのように生成されるかに注意してください。また、約5000回の反復は、テルジディス氏の本で述べられている274回の反復よりもはるかに多いことも付け加えておきます。

Questions:

  • このアルゴリズムへのアプローチ方法に何か問題がありますか?
  • いいえの場合、どの暗黙の条件が欠落していますか?
31
solub

この課題を解決するために私が提案するソリューションは、有効なソリューションを記録しながらアルゴリズムを数回繰り返すことに基づいています。ソリューションは一意ではないので、アルゴリズムが複数のソリューションをスローすることを期待しています。それらのそれぞれは、ネイバーの親和性に基づいてスコアを持ちます。

有効なプラント分布を見つけようとする完全な実行に対して、 'attempt'を呼び出します。スクリプトの完全な実行は、N回の試行で構成されます。

各試行は、2つのランダムな(均一な)選択から始まります。

  • グリッドの開始点
  • 開始オフィス

ポイントとオフィスを定義すると、すべてのオフィスブロックをグリッドに収めようとする「拡張プロセス」が始まります。

新しいブロックはそれぞれ、彼の手順に従って設定されます。

  • 第一。オフィスに隣接する各セルの親和性を計算します。
  • 2番目。 1つのサイトをランダムに選択します。選択肢はアフィニティによって重み付けする必要があります。

すべてのオフィスブロックが配置された後、別の統一ランダム選択が必要です。次のオフィスが配置されます。

選択したら、各サイトのアフィニティを再度計算し、新しいオフィスの開始点をランダムに(無限に)選択する必要があります。

0アフィニティオフィスは追加しません。確率係数は0グリッドのそのポイント。アフィニティ関数の選択は、この問題の繰り返し発生する部分です。隣接するセルファクターの加算または乗算で試すこともできます。

拡張プロセスは、オフィスのすべてのブロックが配置されるまで、再び参加します。

したがって、基本的に、オフィスのピッキングは均一な分布に従い、その後、選択されたオフィスに対して重み付けされた拡張プロセスが発生します。

試行はいつ終了しますか?、以下の場合:

  • グリッドに新しいオフィスを配置する意味がありません(すべてaffinity = 0
  • すべての親和性の重みが0に等しいため、Officeを拡張できません

次に、その試みは無効であり、完全に新しいランダムな試みに移動して破棄する必要があります。

それ以外の場合、すべてのブロックが適合していれば有効です。

重要なのは、オフィス同士がくっついていることです。これがアルゴリズムの重要なポイントであり、親和性に従ってランダムにすべての新しいオフィスにランダムに適合しようとしますが、それでもランダムなプロセスです。条件が満たされていない(無効な)場合、ランダムプロセスが再び開始され、新しくランダムなグリッドポイントとオフィスが選択されます。

enter image description here

申し訳ありませんが、アルゴリズムはありますが、コードはありません。

注:アフィニティコンピューティングプロセスを改善できるか、いくつかの異なる方法で試すこともできると思います。これは、ソリューションを取得するのに役立つアイデアにすぎません。

それが役に立てば幸い。

3
Cheche

Kostas Terzidis教授は優れたコンピュータ理論研究者になると思いますが、彼のアルゴリズムの説明はまったく役に立ちません。

まず、隣接行列は意味がありません。あなたが言った質問のコメントで:

「その値が高いほど、2つのスペースが隣接している可能性が高くなります。」

しかし、_m[i][i] = 0_、つまり、同じ「オフィス」の人々は隣人として他のオフィスを好むことを意味します。それはあなたが期待するものとは正反対ですよね?代わりにこのマトリックスを使用することをお勧めします:

_With 1 <= i, j <= 5:

              +----------------+
              | 10  6  1  5  2 |
              |    10  1  4  0 |
    m[i][j] = |       10  8  0 |  
              |          10  3 |
              |             10 |
              +----------------+
_

この行列では、

  • 最高値は10です。したがって、_m[i][i] = 10_は、まさにあなたが望むものを意味します。同じオフィスの人々は一緒にいる必要があります。
  • 最も低い値は0です(まったく連絡をとるべきではない人々)。

アルゴリズム

ステップ1:すべての場所をランダムに配置し始めます

(1を基にした行列のインデックス付けには申し訳ありませんが、隣接行列との一貫性があります。)

_With 1 <= x <= 5 and 1 <= y <= 7:

            +---------------------+
            | -  -  1  2  1  4  3 |
            | 1  2  4  5  1  4  3 |
  p[x][y] = | 2  4  2  4  3  2  4 |
            | -  -  -  -  3  2  4 |
            | -  -  -  -  5  3  3 |
            +---------------------+
_

ステップ2:ソリューションのスコアリング

all Places _p[x][y]_の場合、隣接行列を使用してスコアを計算します。たとえば、最初の場所_1_には_2_と_4_が隣接しているため、スコアは11です。

_score(p[1][3]) = m[1][2] + m[1][4] = 11
_

すべての個々のスコアの合計はソリューションスコアになります。

ステップ3:場所を交換して現在のソリューションを調整します

場所のペア_p[x1][y1], p[x2][y2]_ごとに、それらを入れ替えてソリューションを再度評価します。スコアが優れている場合は、新しいソリューションを保持します。いずれの場合も、順列が解を改善できないまでステップ3を繰り返します。

たとえば、_p[1][4]_を_p[2][1]_と交換すると、次のようになります。

_            +---------------------+
            | -  -  1  1  1  4  3 |
            | 2  2  4  5  1  4  3 |
  p[x][y] = | 2  4  2  4  3  2  4 |
            | -  -  -  -  3  2  4 |
            | -  -  -  -  5  3  3 |
            +---------------------+
_

より良いスコアのソリューションが見つかります:

スワップ前

_score(p[1][3]) = m[1][2] + m[1][4] = 11
score(p[2][1]) = m[1][2] + m[1][2] = 12
_

スワップ後

_score(p[1][3]) = m[1][1] + m[1][4] = 15
score(p[2][1]) = m[2][2] + m[2][2] = 20
_

そのままにして、場所を入れ替えてください。

いくつかのメモ

  • 反復のある時点で2か所を入れ替えることができず、スコアが高くなるため、アルゴリズムは常にファイナライズされます。
  • Nの場所を持つ行列には、N x (N-1)の可能なスワップがあり、効率的な方法で実行できます(したがって、ブルートフォースは必要ありません)。

それが役に立てば幸い!

0
Cartucho