[英]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
如果地图上的给定位置的邻居也是该生物群落,则该位置应该更有可能是该生物群落。 因此,如果给定广场的邻居都是伍兹,那么几乎可以保证该广场是伍兹。
所有的生物群系都由类表示( woodsBiome
, desertBiome
, fieldBiome
)。 它们都继承自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 == xMax
和yi == 0
),以便生成一个2D网格,然后可以循环通过它。
我创建了一个列表biomeProbabilities
其中具有代表不同生物群落的不同索引。 在地图中的位置上循环时,我检查所选位置的邻居并根据其生物群系调整biomeProbabilities
的值。
最后,我将numpy.random.choice()
与biomeList
和biomeProbabilities
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噪声函数。 如果特定区域的值高于某个阈值,请使用其他生物群落。 优点是:
我建议:
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.