【发布时间】:2020-12-10 17:04:53
【问题描述】:
我想将 3x3 网格中的元素与随机邻居交换。但当然有些元素有 2 个邻居,有些 3 和 1 个元素有 4 个邻居。因为我想选择一个随机邻居,所以我必须知道每个元素有多少个邻居。
我很确定我可以用很多 if else 查询来解决这个问题,但这对于这个简单的问题来说太难看了。但我对如何更优雅地做到这一点没有不同的想法。
【问题讨论】:
标签: python arrays numpy matrix
我想将 3x3 网格中的元素与随机邻居交换。但当然有些元素有 2 个邻居,有些 3 和 1 个元素有 4 个邻居。因为我想选择一个随机邻居,所以我必须知道每个元素有多少个邻居。
我很确定我可以用很多 if else 查询来解决这个问题,但这对于这个简单的问题来说太难看了。但我对如何更优雅地做到这一点没有不同的想法。
【问题讨论】:
标签: python arrays numpy matrix
这里有两种可能的解决方案。
在这两种情况下,都假设单元格的位置是网格中以 0 为索引的坐标(行、列)的元组。
不需要循环,并且有 1/n 的概率可以在 n 个可能的邻居中找到任何邻居。
import random
import itertools
import numpy as np
# All the possible moves are combination of 2 numbers among 3 (-1, 0 and 1)...
all_moves = list(itertools.product([-1,0,1], repeat=2))
#... except (0,0) which is keeping the current position.
all_moves.remove((0,0))
def random_neighbour(current_pos):
# Adding them to your current position gives you all virtual neighbours
choices = np.array(current_pos) + np.array(all_moves)
# ... but we must remove the impossible positions
choices = choices[((choices>=0)&(choices<=2)).all(axis=1)]
# Now we can choose randomly among the remaining possibilities:
return random.choice(choices)
这也确保我们获得相同的概率来获得任何邻居到一个单元格。在您的脚本启动时,我们可以初始化一个字典,为每个单元格(键)提供所有邻居(值)。
import random
neighbours_dict = {
(p // 3, p % 3): [(p // 3 + x_inc-1, p % 3 + y_inc - 1)
for x_inc in range(3) if 1 <= p // 3 + x_inc <= 3
for y_inc in range(3) if 1 <= p % 3 + y_inc <= 3 and not y_inc == x_inc == 1]
for p in range(9)}
然后我们可以简单地给它当前位置的索引。请注意,位置必须是元组,因为列表不可散列且不能构成索引。所以我们将进行强制转换以确保我们不会收到 TypeError。
random.choice(neighbours_dict[tuple(current_pos)])
当然,我们也可以手动初始化字典以满足我们的特定需求,像这样:
neighbours_dict = {(0, 0): [(0, 1), (1, 0), (1, 1)],
(1, 0): [(0, 0), (0, 1), (1, 1), (2, 0), (2, 1)],
(2, 0): [(1, 0), (1, 1), (2, 1)],
(0, 1): [(0, 0), (0, 2), (1, 0), (1, 1), (1, 2)],
(1, 1): [(0, 0), (0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1), (2, 2)],
(2, 1): [(1, 0), (1, 1), (1, 2), (2, 0), (2, 2)],
(0, 2): [(0, 1), (1, 1), (1, 2)],
(1, 2): [(0, 1), (0, 2), (1, 1), (2, 1), (2, 2)],
(2, 2): [(1, 1), (1, 2), (2, 1)]}
或者我们可以概括字典理解,使其兼容任何网格形状和大小:
grid_width, grid_height = 3, 3
neighbours_dict = {
(p // grid_width, p % grid_width):
[(p // grid_width + y_inc - 1, p % grid_width + x_inc - 1)
for y_inc in range(3) if 1 <= p // grid_width + y_inc <= grid_height
for x_inc in range(3) if 1 <= p % grid_width + x_inc <= grid_width
and not y_inc == x_inc == 1]
for p in range(grid_width * grid_height) }
对我来说,后者是其中最优雅的解决方案。
【讨论】:
将 3x3 网格嵌入到 5x5 网格中。在边界中,有一些永远不会出现在 3x3 网格中的值,比如 -1。
-1 -1 -1 -1 -1
-1 -1
-1 Your Grid-1
-1 -1
-1 -1 -1 -1 -1
现在,每个内部网格单元都有 4 个邻居。对于这些单元格,请进行拒绝采样。那就是不断地从附近循环一个样本,直到你抽取一个不是 -1 的样本。
【讨论】: