如何根据所需邻域的程度对单元进行重新排序?(进行中)

solub:

我需要帮助来实现允许生成建筑计划的算法,最近在阅读Kostas Terzidis教授的最新出版物《置换设计:建筑,文本和上下文》(2014)时,我偶然发现了该算法

语境

  • 考虑一个划分为网格系统(a)的站点(b)。
  • 我们还考虑要在站点限制内放置的空间列表(c)和邻接矩阵,以确定这些空间的放置条件和相邻关系(d)

在此处输入图片说明

引用Terzidis教授的话:

"A way of solving this problem is to stochastically place spaces within the grid until all spaces are fit and the constraints are satisfied"

The figure above shows such a problem and a sample solution (f).

ALGORITHM (as briefly described in the book)

1/ "Each space is associated with a list that contains all other spaces sorted according to their degree of desirable neighborhood."

2/ "Then each unit of each space is selected from the list and then one-by-one placed randomly in the site until they fit in the site and the neighboring conditions are met. (If it fails then the process is repeated)"

Example of nine randomly generated plans:

在此处输入图片说明

I should add that the author explains later that this algorithm doesn't rely on brute force techniques.

PROBLEMS

As you can see, the explanation is relatively vague and step 2 is rather unclear (in terms of coding). All I have so far are "pieces of a puzzle":

  • a "site" (list of selected integers)
  • an adjacency matrix (nestled lists)
  • "spaces" (dictionnary of lists)

for each unit:

  • a function that returns its direct neighbors
  • a list of its desirable neighbors with their indices in sorted order
  • a fitness score based on its actual neighbors

    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 would really appreciate if someone could help connect the dots and explain me:

  • how to re-order the units based on their degree of desirable neighborhood ?

EDIT

As some of you have noticed the algorithm is based on the likelihood that certain spaces (composed of units) are adjacent. The logic would have it then that for each unit to place randomly within the limits of the site:

  • we check its direct neighbors (up, down, left right) beforehand
  • compute a fitness score if at least 2 neighbors. (=sum of the weights of these 2+ neighbors)
  • and finally place that unit if the adjacency probability is high

Roughly, it would translate into this:

    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

Given that a significant part of the units won't be placed on the first run (because of low adjacency probability), we need to iterate over and over until a random distribution where all units can be fitted is found.

在此处输入图片说明

After a few thousand iterations a fit is found and all the neighboring requirements are met.

Notice however how this algorithm produces separated groups instead of non-divided and uniform stacks like in the example provided. I should also add that nearly 5000 iterations is a lot more than the 274 iterations mentioned by Mr. Terzidis in his book.

Questions:

  • Is there something wrong with the way I'm approaching this algorithm ?
  • If no then what implicit condition am I missing ?
Cheche :

The solution I propose to solve this challenge is based on repeating the algorithm several times while recordig valid solutions. As solution is not unique, I expect the algorithm to throw more than 1 solution. Each of them will have a score based on neighbours affinity.

I'll call an 'attempt' to a complete run trying to find a valid plant distribution. Full script run will consist in N attempts.

Each attempt starts with 2 random (uniform) choices:

  • Starting point in grid
  • Starting office

Once defined a point and an office, it comes an 'expansion process' trying to fit all the office blocks into the grid.

Each new block is set according to his procedure:

  • 1st. Compute affinity for each adjacent cell to the office.
  • 2nd. Randomly select one site. Choices should be weighted by the affinity.

After every office block is placed, another uniform random choice is needed: next office to be placed.

Once picked, you should compute again affinitty for each site, and randomly (weigthed) select the starting point for the new office.

0 affinity offices don't add. Probability factor should be 0 for that point in the grid. Affinity function selection is an iteresting part of this problem. You could try with the addition or even the multiplication of adjacent cells factor.

Expansion process takes part again until every block of the office is placed.

So basically, office picking follows a uniform distribution and, after that, the weighted expansion process happens for selected office.

When does an attempt end?, If:

  • There's no point in grid to place a new office (all have affinity = 0)
  • Office can't expand because all affinity weights equal 0

Then the attempt is not valid and should be descarded moving to a fully new random attempt.

Otherwise, if all blocks are fit: it's valid.

关键是办公室应该团结在一起。这是算法的关键点,该算法会根据亲和力随机尝试适应每个新办公室,但这仍然是一个随机过程。如果不满足条件(无效),则随机过程会再次开始,选择一个新的随机网格点和办公室。

在此处输入图片说明

抱歉,这里只有一个算法,但没有代码。

注意:我确信相似性计算过程可以得到改善,甚至可以尝试使用一些不同的方法。这只是帮助您获得解决方案的想法。

希望能帮助到你。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何避免在JTable中对单个列进行重新排序?

如何在进行中反转切片?

如何对MySQL表中的记录/行进行重新排序

如何根据图像中的蛇形对坐标进行排序?

如何根据屏幕尺寸(引导)对在导航栏中水平对齐的<ul>元素进行重新排序

Javascript:如何检查异步操作是否仍在进行中/正在进行中?

如何根据Julia中的键对词典进行排序?

如何根据列中的值对数据框的行进行重新排序

如何根据另一个活动的可排序列表对列表进行重新排序?

如何根据“。”对arraylist进行排序?

如何在Bootstrap中对垂直列进行重新排序?

如何根据在jtable中动态添加的列对行进行排序?

如何对从A到Z的列中的单元格进行排序?

如何根据内容在Python中对列表进行重新排序

如何在Excel中对通道中的单元进行排序?

如何防止HAML对class属性中的单词进行重新排序?

如何在MYSQL的SELECT查询中对行进行重新排序?

如何对char数组中的位置进行重新排序

如何在Notepad ++中对管道定界的列进行重新排序?

如何根据igraph layout_in_circle中的程度对顶点进行排序

如何在Bootstrap 3中对行进行重新排序

如何在Realm Swift中对UITableView单元格进行重新排序?

如何根据日期对 RecyclerView 中的项目进行排序

如何根据日期对wordpress中的帖子进行分组和排序?

如何根据躲避变量的绝对差异程度对因子重新排序

如何根据每行中的非空单元格对范围进行排序?

如何根据Javascript中对象内的字母对象进行排序

如何根据 Python 中的日期时间对字典列表进行排序

如何根据多个因素对 Scala 中的“映射”值进行排序?