多线性映射M的元素存储在长度为N的一维数组中,其中由S:[Int] = [p,q,r,...]
定义的形状S为q*p*r*... = N
。 Shape的大小可变,在编译时不知道。
我试图解决的问题是使用整数数组访问地图元素的一般方法,其中单个值是Shape S中的坐标,例如:M[1,3,2], M[2,3,3,3]
等...这是一个问题不同于地图元素的简单枚举。
一种方法是使用M[i,j,k]
并实现下标方法。不幸的是,这种方法硬编码了地图的形状,并且算法不再是通用的。
假设有一个实用程序函数,它从派生自地图的Shape的元组返回一个元素索引,以便:
func index(_ indexes:[Int]) -> Int {....}
func elementAt(indexes:[Int]) -> Element {
return elements_of_the_map[self.index(indexes)]
}
M.elementAt(indexes:[i,j,k]) or M.elementAt(indexes:[i,j,k,l,m])
始终有效。所以此时的问题是构建数组[i,j,k,...]
问题:是否有算法有效枚举这些索引?嵌套循环不起作用,因为在编译时不知道循环的数量,并且递归函数似乎增加了很多复杂性(特别是跟踪以前的索引)。
我正在考虑算法'a la'base-x计数,即向右上方索引添加一个单位,如果计数超过地图形状的元素数,则向左移动一个单位。
答案 0 :(得分:1)
这里是代码,它是原始的,但应该有效。我的想法是从右到左递增,从形状约束[2,3,3]中[1,2,1]移动说到[1,2,2]。
func add_one_unit(shape:[Int],indexes:[Int]) -> [Int]? {
//Addition is right to left, so we have to reverse the arrays. Shape Arrays are usually very small, so it's fast.
let uu = Array(indexes.reversed()); //Array to add one index to.
let shape_reversed = Array(shape.dimensions.reversed()); //Shape array.
var vv:[Int] = [];
var move_next:Bool = true;
for i in 0..<uu.count {
if move_next {
if uu[i] < shape_reversed[i] - 1 { //Shape constraint is OK.
vv.append(uu[i] + 1)
move_next = false;
} else {
vv.append(0) //Shape constraint is reached.
move_next = true;//we'll flip the next index.
}
} else {
vv.append(uu[i]) //Nothing to change.
}
}
return ( vv.reduce(true, { $0&&($1 == 0) }) ) ? nil : Array(vv.reversed()); //Returns nil once we reached the Zero Vector.
}
哪个给出了
add_one_unit(shape:[2,3,3],indexes:[0,0,0]) -> [0,0,1]
add_one_unit(shape:[2,3,3],indexes:[1,2,2]) -> [0,0,0]/nil
完成此操作后,此函数可用于枚举任何形状的多线性映射([i,j,k,...]到唯一索引的映射,例如矩阵到索引映射是必要的,并且取决于在你的实现上),或从任何特定的向量切片地图。
答案 1 :(得分:1)
同样的想法,但代码较少:
func addOneUnit(shape: [Int], indexes: [Int]) -> [Int]? {
var next = indexes
for i in shape.indices.reversed() {
next[i] += 1
if next[i] < shape[i] {
return next
}
next[i] = 0
}
return nil
}