繁体   English   中英

确保np.random.choice()的列表内容总和为1

[英]Ensure that contents of list sums up to 1 for np.random.choice()

上下文

在Python 3.5中,我正在制作一个函数来生成具有不同生物群系的地图-一个二维列表,其中第一层代表Y轴的线,项目代表X轴的项。

例:

 [ ["A1", "B1", "C1"], ["A2", "B2", "C2"], ["A3", "B3", "C3"] ] 

显示为:

 A1 B1 C1 A2 B2 C2 A3 B3 C3 

目标

如果地图上的给定位置的邻居也是该生物群落,则该位置应该更有可能是该生物群落。 因此,如果给定广场的邻居都是伍兹,那么几乎可以保证该广场是伍兹。


我的代码(到目前为止)

所有的生物群系都由类表示( woodsBiomedesertBiomefieldBiome )。 它们都继承自baseBiome ,后者baseBiome用于填充网格。

我的代码采用函数形式。 它以最大的X和Y坐标为参数。 这里是:

def generateMap(xMax, yMax):
    areaMap = []  # this will be the final result of a 2d list

    # first, fill the map with nothing to establish a blank grid
    xSampleData = []  # this will be cloned on the X axis for every Y-line
    for i in range(0, xMax):
        biomeInstance = baseBiome()
        xSampleData.append(biomeInstance)  # fill it with baseBiome for now, we will generate biomes later
    for i in range(0, yMax):
        areaMap.append(xSampleData)

    # now we generate biomes
    yCounter = yMax  # because of the way the larger program works. keeps track of the y-coordinate we're on
    for yi in areaMap:  # this increments for every Y-line
        xCounter = 0  # we use this to keep track of the x coordinate we're on
        for xi in yi:  # for every x position in the Y-line
            biomeList = [woodsBiome(), desertBiome(), fieldBiome()]
            biomeProbabilities = [0.0, 0.0, 0.0]
            # biggest bodge I have ever written
            if areaMap[yi-1][xi-1].isinstance(woodsBiome):
                biomeProbabilities[0] += 0.2
            if areaMap[yi+1][xi+1].isinstance(woodsBiome):
                biomeProbabilities[0] += 0.2
            if areaMap[yi-1][xi+1].isinstance(woodsBiome):
                biomeProbabilities[0] += 0.2
            if areaMap[yi+1][xi-1].isinstance(woodsBiome):
                biomeProbabilities[0] += 0.2
            if areaMap[yi-1][xi-1].isinstance(desertBiome):
                biomeProbabilities[1] += 0.2
            if areaMap[yi+1][xi+1].isinstance(desertBiome):
                biomeProbabilities[1] += 0.2
            if areaMap[yi-1][xi+1].isinstance(desertBiome):
                biomeProbabilities[1] += 0.2
            if areaMap[yi+1][xi-1].isinstance(desertBiome):
                biomeProbabilities[1] += 0.2
            if areaMap[yi-1][xi-1].isinstance(fieldBiome):
                biomeProbabilities[2] += 0.2
            if areaMap[yi+1][xi+1].isinstance(fieldBiome):
                biomeProbabilities[2] += 0.2
            if areaMap[yi-1][xi+1].isinstance(fieldBiome):
                biomeProbabilities[2] += 0.2
            if areaMap[yi+1][xi-1].isinstance(fieldBiome):
                biomeProbabilities[2] += 0.2
            choice = numpy.random.choice(biomeList, 4, p=biomeProbabilities)
            areaMap[yi][xi] = choice

    return areaMap

说明:

如您所见,我从一个空列表开始。 我将baseBiome作为占位符添加到其中(最多xi == xMaxyi == 0 ),以便生成一个2D网格,然后可以循环通过它。

我创建了一个列表biomeProbabilities其中具有代表不同生物群落的不同索引。 在地图中的位置上循环时,我检查所选位置的邻居并根据其生物群系调整biomeProbabilities的值。

最后,我将numpy.random.choice()biomeListbiomeProbabilities biomeList使用,使用每个项目的给定概率从biomeList进行选择。


我的问题

如何确定biomeProbabilities中每个项目的总和等于1(以便numpy.random.choice将允许随机选择概率)? 我看到两种合乎逻辑的解决方案:

a)分配新的概率,以使排名最高的生物群系获得0.8 ,然后是第二个0.4和第三个0.2

b)对每个加或减相等的金额,直到总和== 1

哪个选项(如果有)会更好,我将如何实施?

另外,有没有一种更好的方法来获得结果而不求助于我在这里使用的无尽if语句?

这听起来像是解决问题的复杂方法。 您将很难以这种方式工作,因为您将自己限制在单向前通过。

做到这一点的一种方法是选择一个随机位置以启动生物群系,然后以较高的概率(例如0.9)将其“扩展”到邻近的斑块。

(请注意,示例10行中存在代码错误-您必须复制内部列表)

import random
import sys


W = 78
H = 40

BIOMES = [
    ('#', 0.5, 5),
    ('.', 0.5, 5),
]

area_map = []

# Make empty map
inner_list = []
for i in range(W):
    inner_list.append(' ')
for i in range(H):
    area_map.append(list(inner_list))

def neighbors(x, y):
    if x > 0:
        yield x - 1, y
    if y > 0:
        yield x, y - 1
    if y < H - 1:
        yield x, y + 1
    if x < W - 1:
        yield x + 1, y

for biome, proba, locations in BIOMES:
    for _ in range(locations):
        # Random starting location
        x = int(random.uniform(0, W))
        y = int(random.uniform(0, H))

        # Remember the locations to be handled next
        open_locations = [(x, y)]
        while open_locations:
            x, y = open_locations.pop(0)

            # Probability to stop
            if random.random() >= proba:
                continue

            # Propagate to neighbors, adding them to the list to be handled next
            for x, y in neighbors(x, y):
                if area_map[y][x] == biome:
                    continue
                area_map[y][x] = biome
                open_locations.append((x, y))

for y in range(H):
    for x in range(W):
        sys.stdout.write(area_map[y][x])
    sys.stdout.write('\n')

结果截图

当然,一种更好的方法(通常用于此类任务(例如在Minecraft中))是使用Perlin噪声函数。 如果特定区域的值高于某个阈值,请使用其他生物群落。 优点是:

  • 惰性生成:您无需提前生成整个区域图,而实际上需要知道某个区域时,就可以确定该区域中的生物群系类型
  • 看起来更真实
  • Perlin为您提供实际值作为输出,因此您可以将其用于更多事物,例如地形高度,或用于混合多个生物群落(或者可以将其用于“湿润”,沙漠为0-20%,沙漠为20-60%)草,60-80%的沼泽,80-100%的水)
  • 您可以叠加多个“大小”的噪声,例如,只需将它们相乘即可为您提供每个生物群系的详细信息

我建议:

biomeProbabilities = biomeProbabilities / biomeProbabilities.sum()

为了让您无尽的if语句,我建议使用预分配的方向数组,例如:

directions = [(-1, -1), (0, -1), (1, -1),
              (-1,  0),          (1,  0),
              (-1,  1), (0,  1), (1,  1)]

并使用它进行迭代,例如:

for tile_x, tile_y in tiles:
    for x, y in direction:
        neighbor = map[tile_x + x][tile_y + y]

@remram很好地回答了您可能会或可能不会用于生成地形的算法,因此我不会涉及这一主题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM