Golang中如何遍历二维列表并实现0到n的映射?
Golang中如何遍历二维列表并实现0到n的映射? 假设你有一个矩阵(二维数组),你选择了坐标 (x, y) 处的单元格,并且想要遍历它的8个邻居。遍历邻居的顺序并不重要。
选项1:
for i=-1, 1
for j=-1, 1
m[x + i ][y + j]
这将遍历邻居以及 (x,y) 单元格。
选项2: 定义一个常量二维数组 MASKS {{-1,-1}, {-1,0}, {-1,1},{0, -1}…
for mask in MASKS:
m[x + mask[0]][y + mask[1]]
选项3:<- 这里变得有趣了: 我想创建一个映射(仅使用二进制操作)将数字0到7与选项2中的掩码对应起来。也就是说,我希望我的代码看起来像:
for i = 0, 7:
m[x + io1 - io2][y + io3 - io4]
其中 o1,o2,o3,o4 是使用 & 和 | 的二进制操作。
如何找到这样的映射?这个技巧能否推广到超过8个邻居的算法中?
请注意,我相当确定这是可以做到的,正如我直觉上假设的那样。
更多关于Golang中如何遍历二维列表并实现0到n的映射?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我对此进行了一些尝试,发现很难理解如何将8位排列映射到三组数据,其中两组包含三个成员,一组包含两个成员。具体来说,(0,0)的相邻点:
- 第一组:(1,-1), (1,0), (1,1)
- 第二组:(-1,-1), (-1,0), (-1,1)
- 第三组:(0,-1), (0,1)
我回复只是因为如果有人找到解决方案,我希望收到通知!
更多关于Golang中如何遍历二维列表并实现0到n的映射?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在 Go 语言中,遍历二维数组(矩阵)的 8 个邻居时,可以通过二进制操作将索引 0 到 7 映射到偏移量,从而避免使用预定义的掩码数组。这种方法利用位运算来动态计算邻居坐标的偏移量,适用于 8 个邻居的情况,并且可以推广到其他类似场景,例如 4 个邻居(仅上下左右)或更多方向。
以下是实现代码示例。我们假设矩阵 m 是一个二维整数数组,坐标 (x, y) 是有效的,并且我们处理边界检查以避免越界访问。映射的核心思想是将索引 i(0 到 7)分解为两个二进制位,分别表示行和列的偏移方向(-1、0 或 1),但注意 8 个邻居包括所有组合,除了 (0,0) 自身。
对于 8 个邻居,我们可以将索引 i 视为一个 3 位数字(因为 2^3 = 8),并使用位操作提取行和列的偏移。具体来说:
- 行偏移:使用
(i >> 2) & 1和(i >> 1) & 1的组合来生成 -1、0 或 1。 - 列偏移:使用
(i >> 0) & 1和(i >> 1) & 1的组合。
但更简单的方法是直接使用一个查找表或计算偏移,因为 8 个邻居的偏移是固定的:(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)。我们可以通过索引 i 计算行和列偏移:
- 行偏移 =
(i / 3) - 1,但注意 8 个邻居不是均匀分布的;更好的方式是用位操作模拟。
实际上,一个常见技巧是将索引 i 从 0 到 7 映射到行偏移 dx 和列偏移 dy,其中:
dx = (i / 3) - 1和dy = (i % 3) - 1,但这样会包括 (0,0),需要跳过。- 或者,我们可以定义一个函数,使用位操作直接计算。
以下是使用位操作实现映射的 Go 代码示例。我们通过将索引 i 的二进制位分解来生成偏移,确保不包括 (0,0):
package main
import "fmt"
func main() {
// 示例矩阵,假设为 3x3 整数矩阵
m := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
x, y := 1, 1 // 中心单元格 (1,1),值为 5
// 遍历 0 到 7 的索引,使用位操作映射到邻居偏移
for i := 0; i < 8; i++ {
// 使用位操作计算行偏移 dx 和列偏移 dy
// 将 i 视为 3 位二进制数: b2 b1 b0
// dx = (b2 << 1) | b1 - 1? 更简单的方法:直接计算偏移表,但用位操作模拟
// 实际上,我们可以这样定义:
// dx = (i >> 2) & 1? 不直接,改用以下方法:
// 对于 8 个邻居,偏移可以基于 i 的二进制表示生成
// 但为了简化,这里使用一个标准方法:将 i 映射到预定义的 8 个方向,但用计算代替数组
// 一个可行的映射:使用 i 生成 dx 和 dy,其中 dx 和 dy 各为 -1, 0, 1
// 通过 i 的位组合:令 a = (i >> 2) & 1, b = (i >> 1) & 1, c = i & 1
// 但更直接的方法是使用除法和取模,跳过 (0,0)
dx := (i / 3) - 1
dy := (i % 3) - 1
if dx == 0 && dy == 0 {
continue // 跳过自身单元格
}
nx, ny := x+dx, y+dy
// 检查边界
if nx >= 0 && nx < len(m) && ny >= 0 && ny < len(m[0]) {
fmt.Printf("Neighbor at (%d, %d): %d\n", nx, ny, m[nx][ny])
}
}
}
上述代码使用除法和取模操作来生成偏移,但这不是纯位操作。如果要完全使用位操作(如 & 和 |),我们可以通过索引 i 的二进制位来直接计算 dx 和 dy。例如,将 i 视为一个 3 位数字,位为 b2 b1 b0,然后:
dx = (b2 << 1) | b1 - 1?这可能会产生 -1、0、1、2 等,不精确。
一个更精确的位操作映射是使用查找表,但用计算代替。实际上,对于 8 个邻居,一个标准位操作技巧是:
- 定义
dx = ((i & 4) >> 2) - ((i & 2) >> 1)?这比较复杂。
由于问题要求使用二进制操作(& 和 |)实现映射,并且推广到更多邻居,我们可以设计一个通用函数。但注意,8 个邻居的偏移是固定的,位操作可能不是最直接的方式;通常使用掩码数组更清晰。如果坚持用位操作,这里是一个示例,通过位组合生成偏移:
package main
import "fmt"
func main() {
m := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
x, y := 1, 1
for i := 0; i < 8; i++ {
// 使用位操作计算 dx 和 dy
// 将 i 的位分解:令 a = (i >> 2) & 1, b = (i >> 1) & 1, c = i & 1
// 然后定义 dx = a + b - 1? 不通用。改用以下方法:
// 实际上,一个简单映射:dx = (i >> 1) & 1? 不工作。
// 更可靠的方法:使用预计算,但用位操作模拟方向。
// 由于 8 个邻居的偏移是 (-1,-1) 到 (1,1),不包括 (0,0),我们可以用:
dx := (i>>1)&1 - (i>>2)&1 // 示例计算,可能不准确
dy := i&1 - (i>>1)&1 // 类似
// 但上述计算可能不覆盖所有情况;实际上,这需要更复杂的位操作。
// 一个可行的纯位操作映射(针对 8 邻居):
// 定义 dx 和 dy 基于 i 的位:令 high = i >> 2, low = i & 3
// 然后 dx = (high & 1) - ((high >> 1) & 1)? 不直接。
// 由于复杂性,通常推荐使用掩码数组。但为满足要求,这里是一个近似:
// 使用 i 生成 dx 和 dy,例如:
dx := ((i & 4) >> 2) - ((i & 2) >> 1)
dy := ((i & 2) >> 1) - (i & 1)
// 调整以覆盖 -1,0,1
// 注意:这只是一个示例,可能不精确;实际中需要测试所有 i。
nx, ny := x+dx, y+dy
if nx >= 0 && nx < len(m) && ny >= 0 && ny < len(m[0]) {
fmt.Printf("Neighbor at (%d, %d): %d\n", nx, ny, m[nx][ny])
}
}
}
注意:上述位操作示例可能不精确,因为 8 个邻居的映射需要确保每个 i 对应唯一的偏移对,且覆盖所有 8 个方向。在实际应用中,使用预定义的掩码数组(如选项2)更可靠和易读。但如果目标是纯位操作,可能需要更复杂的位掩码和条件调整。
对于推广到超过 8 个邻居(例如 24 个邻居在 3D 网格),类似位操作可能更复杂,通常使用循环或查找表更实用。位操作的优势在于性能,但可能牺牲可读性。在 Go 中,建议根据具体需求选择方法。

