Kostas Terzidis教授の最新の出版物 Permutation Design:Buildingsを読んでいるときに最近遭遇した、建築計画の生成を可能にするアルゴリズムを実装するのに助けが必要でしょう。 、テキストおよびコンテキスト (2014)。
[〜#〜]コンテキスト[〜#〜]
テルジディス教授の引用:
「この問題を解決する方法は、すべてのスペースがフィットし、制約が満たされるまで、グリッド内にスペースを確率的に配置することです。」
上の図は、そのような問題とサンプルの解決策を示しています(f)。
ALGORITHM(本に簡単に説明されています)
1 /「各スペースは、望ましい近傍の度合いに従ってソートされた他のすべてのスペースを含むリストに関連付けられています。」
2 /「次に、各スペースの各ユニットがリストから選択され、サイトに適合し、隣接する条件が満たされるまで、サイトにランダムに1つずつ配置されます(失敗した場合、プロセスが繰り返されます)」
ランダムに生成された9つの計画の例:
このアルゴリズムはブルートフォーステクニックに依存していないことを著者が後で説明することを付け加えておきます。
[〜#〜]問題[〜#〜]
ご覧のとおり、説明は比較的あいまいで、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
誰かが点をつないで説明してくれると助かります。
[〜#〜]編集[〜#〜]
ご存じの方もいらっしゃると思いますが、アルゴリズムは特定のスペース(ユニットで構成される)が隣接している可能性に基づいています。ロジックは、各ユニットがサイトの制限内でランダムに配置するようにします。
おおよそ、これは次のように変換されます。
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
(隣接確率が低いため)ユニットのかなりの部分が最初の実行に配置されないことを考えると、すべてのユニットをフィットできるランダムな分布が見つかるまで、何度も繰り返す必要があります。
数千回の反復の後、適合が見つかり、すべての隣接要件が満たされます。
ただし、このアルゴリズムでは、提供されている例のように、分割されていない均一なスタックではなく、分割されたグループがどのように生成されるかに注意してください。また、約5000回の反復は、テルジディス氏の本で述べられている274回の反復よりもはるかに多いことも付け加えておきます。
Questions:
この課題を解決するために私が提案するソリューションは、有効なソリューションを記録しながらアルゴリズムを数回繰り返すことに基づいています。ソリューションは一意ではないので、アルゴリズムが複数のソリューションをスローすることを期待しています。それらのそれぞれは、ネイバーの親和性に基づいてスコアを持ちます。
有効なプラント分布を見つけようとする完全な実行に対して、 'attempt'を呼び出します。スクリプトの完全な実行は、N
回の試行で構成されます。
各試行は、2つのランダムな(均一な)選択から始まります。
ポイントとオフィスを定義すると、すべてのオフィスブロックをグリッドに収めようとする「拡張プロセス」が始まります。
新しいブロックはそれぞれ、彼の手順に従って設定されます。
すべてのオフィスブロックが配置された後、別の統一ランダム選択が必要です。次のオフィスが配置されます。
選択したら、各サイトのアフィニティを再度計算し、新しいオフィスの開始点をランダムに(無限に)選択する必要があります。
0
アフィニティオフィスは追加しません。確率係数は0
グリッドのそのポイント。アフィニティ関数の選択は、この問題の繰り返し発生する部分です。隣接するセルファクターの加算または乗算で試すこともできます。
拡張プロセスは、オフィスのすべてのブロックが配置されるまで、再び参加します。
したがって、基本的に、オフィスのピッキングは均一な分布に従い、その後、選択されたオフィスに対して重み付けされた拡張プロセスが発生します。
試行はいつ終了しますか?、以下の場合:
affinity = 0
)次に、その試みは無効であり、完全に新しいランダムな試みに移動して破棄する必要があります。
それ以外の場合、すべてのブロックが適合していれば有効です。
重要なのは、オフィス同士がくっついていることです。これがアルゴリズムの重要なポイントであり、親和性に従ってランダムにすべての新しいオフィスにランダムに適合しようとしますが、それでもランダムなプロセスです。条件が満たされていない(無効な)場合、ランダムプロセスが再び開始され、新しくランダムなグリッドポイントとオフィスが選択されます。
申し訳ありませんが、アルゴリズムはありますが、コードはありません。
注:アフィニティコンピューティングプロセスを改善できるか、いくつかの異なる方法で試すこともできると思います。これは、ソリューションを取得するのに役立つアイデアにすぎません。
それが役に立てば幸い。
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 |
+----------------+
_
この行列では、
m[i][i] = 10
_は、まさにあなたが望むものを意味します。同じオフィスの人々は一緒にいる必要があります。ステップ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
_
そのままにして、場所を入れ替えてください。
N
の場所を持つ行列には、N x (N-1)
の可能なスワップがあり、効率的な方法で実行できます(したがって、ブルートフォースは必要ありません)。それが役に立てば幸い!