我需要帮助来实现允许生成建筑计划的算法,最近在阅读Kostas Terzidis教授的最新出版物Permutation Design: Buildings, Texts and Contexts(2014年)时偶然发现了该算法。
上下文
引用Terzidis教授:
“解决此问题的一种方法是在网格内随机放置空间,直到所有空间都适合并且满足约束条件为止”。
上图显示了这样的问题和示例解决方案(f)。
算法(如书中所述)
1 /“每个空间都与一个列表相关联,该列表包含所有其他空间,这些其他空间均根据其所需的邻域程度进行排序。”
2 /“然后从列表中选择每个空间的每个单元,然后将其一个接一个地随机放置在站点中,直到它们适合站点并满足相邻条件为止。(如果失败,则该过程为重复)”
九个随机生成的计划的示例:
我还要补充一点,作者稍后会解释该算法不依赖于蛮力技术。
问题
如您所见,解释相对模糊,步骤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次迭代比Terzidis先生在书中提到的274次迭代要多得多。
问题:
答案 0 :(得分:3)
我建议解决此挑战的解决方案是基于在记录有效解决方案的同时重复几次算法。由于解决方案不是唯一的,所以我希望算法抛出多个解决方案。他们每个人都有一个基于邻居亲和力的分数。
我将调用“ 尝试”进行完整的尝试,以查找有效的工厂分布。完整的脚本运行将包含N
次尝试。
每次尝试均从2个随机(统一)选择开始:
一旦定义了一个点和一个办公室,就会出现一个“ 扩展过程”,试图将所有办公室块放到网格中。
每个新块均根据其过程进行设置:
在放置每个办公大楼之后,还需要另一个统一的随机选择:要放置下一个办公室。
选择后,您应该重新计算每个站点的亲和力,并随机(错误地)选择新办公室的起点。
0
亲和办公室未添加。概率因数应为0
网格中的那个点。亲和力功能选择非常麻烦 这个问题的一部分。您可以尝试添加或什至 邻近细胞倍增因子。
扩展过程将再次参与,直到放置办公室的每个区域。
因此,基本上,办公室的选择遵循统一的分配方式,然后,选定办公室的加权扩展过程就会发生。
尝试何时结束?, 如果:
affinity = 0
)然后,该尝试无效,应将其丢弃,转向全新的随机尝试。
否则,如果所有块都适合:这是有效的。
重点是办公室应该团结在一起。这是算法的关键点,该算法会根据亲和力随机尝试适应每个新办公室,但这仍然是一个随机过程。如果不满足条件(无效),则随机过程会再次开始,选择一个新的随机网格点和办公室。
对不起,这里只有一个算法,但是没有代码。
注意:我敢肯定亲和力计算过程可以得到改善,甚至您可以尝试使用其他方法。这只是一个帮助您获得解决方案的想法。
希望有帮助。
答案 1 :(得分:0)
我相信Kostas Terzidis教授将是一位出色的计算机理论研究人员,但是他的算法解释完全没有帮助。
首先,邻接矩阵没有意义。在问题评论中,您说过:
“该值越高,两个空格相邻的可能性就越大”
但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步:对解决方案进行评分
对于所有个地方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)
个可能的交换,并且可以以有效的方式完成(因此,不需要蛮力)。希望有帮助!