我有一个网格矩形文件,我已读入数组。此网格化文件包含数据值和NODATA值;数据值在数组内部构成一个连续的奇数形状,NODATA值填充其余部分以使网格文件保持矩形。我对数据值执行操作并跳过NODATA值。
我对数据值执行的操作包括检查周围的8个邻居(当前单元格是3x3网格的中心)。我可以处理八个邻居中的任何一个是NODATA值,但是当实际数据值落在第一行或最后一行/列时,我会通过尝试访问不存在的数组值来触发错误。
为了解决这个问题,我考虑了三个选择:
添加一个带有NODATA值的新的第一行和最后一行/列,并相应地调整我的代码 - 我可以遍历内部的'原始'数组并处理新的NODATA值,就像我已经处理过的那些边缘一样不要落在第一行和最后一行/列中。
我可以创建用于处理第一行和最后一行/列中具有数据的单元格的特定进程 - 循环<( for 循环)一个特定的序列/范围)只检查存在的周围细胞,但由于我仍然需要8个相邻值(NODATA /不存在的细胞被赋予与中央细胞相同的值),我将不得不复制空白/ NODATA值二级3x3网格。虽然可能有办法避免二级网格。这个解决方案很烦人,因为我必须将专门的例程编码到所有角落单元(4个不同的用于循环)和第1行或最后一行/列中的任何单元格(另外4个不同的用于循环< / em>的)。对于任何非边缘单元,只需一个 for 循环。
使用基于我的阅读的地图,可以存储原始数组,同时让我搜索阵列外的位置而不会触发错误。在这种情况下,我仍然必须给这些不存在的单元格赋值(等于数组的中心),因此可能也可能不必设置辅助3x3网格;再一次,可能有办法避免二级网格。
解决方案1似乎最简单,解决方案3最聪明,2最烦人。我有什么解决方案吗?或者其中一个解决方案是否应该成为明显的赢家?
答案 0 :(得分:3)
我的建议是用函数替换对数组的所有读访问。例如,arr[i][j]
getarr(i,j)
。这样,您的所有算法代码都或多或少保持不变,您可以轻松地返回NODATA
以获取超出边界的索引。
但我必须承认这只是我的意见。
答案 1 :(得分:2)
我之前必须这样做,最快的解决方案是使用NODATA值扩展区域并迭代内部。这样核心循环很容易让编译器优化。
如果这不是代码中的计算热点,我会选择Serge的方法。
为了最大限度地减少波纹效应,我使用了具有显式行/列步幅的数组结构,如下所示:
class Grid {
private:
shared_ptr<vector<double>> data;
int origin;
int xStride;
int yStride;
public:
Grid(int nx, int ny) :
data( new vector<double>(nx*ny) ),
origin(0),
xStride(1),
yStride(nx) {
}
Grid(int nx, int ny, int padx, int pady) :
data( new vector<double>((nx+2*padx)*(ny+2*pady));
xStride(1),
yStride(nx+2*padx),
origin(nx+3*padx) {
}
double& operator()(int x, int y) {
return (*data)[origin + x*xStride + y*yStride];
}
}
现在你可以做到
Grid g(5,5,1,1);
Grid g2(5,5);
//Initialise
for(int i=0; i<5; ++i) {
for(int j=0; j<5; ++j) {
g(i,j)=i+j;
}
}
// Convolve (note we don't care about going outside the
// range, and our indices are unchanged between the two
// grids.
for(int i=0; i<5; ++i) {
for(int j=0; j<5; ++j) {
g2(i,j)=0;
g2(i,j)+=g(i-1,j);
g2(i,j)+=g(i+1,j);
g2(i,j)+=g(i,j-1);
g2(i,j)+=g(i,j+1);
}
}
除了:这个数据结构对于使用转置和子矩阵非常棒。其中每一项都只是对偏移和步幅值的调整。
答案 2 :(得分:1)
解决方案1是标准解决方案。它充分利用了现代计算机体系结构,其中几个字节的内存没什么大不了的,正确的指令预测可以加速性能。当您以可预测的模式(固定步幅)继续访问内存时,CPU预取程序将成功提前读取。
解决方案2可以节省少量内存,但边缘的特殊处理会导致真正的减速。尽管如此,中间的大块还是来自预取器。
解决方案3太可怕了。映射访问是O(log N)而不是O(1),实际上它可能慢10-20倍。地图的参考地点较差; CPU预取器不会启动。
答案 3 :(得分:0)
1浪费内存与你的整体矩形大小成正比,3 /地图在这里很笨拙,2其实很容易做到:
T d[X][Y] = ...;
for (int x = 0; x < X; ++x)
for (int y = 0; y < Y; ++y) // move over d[x][y] centres
{
T r[3][3] = { { d[i,j], d[i,j], d[i,j] },
d[i,j], d[i,j], d[i,j] },
d[i,j], d[i,j], d[i,j] } };
for (int i = std::min(0, x-1); i < std::max(X-1, x+1); ++i)
for (int j = std::min(0, y-1); j < std::max(Y-1, y+1); ++j)
if (d[i][j] != NoData)
r[i-x][j-y] = d[i][j];
// use r for whatever...
}
请注意,我故意使用signed int
因此x-1
和y-1
不会成为巨大的正数(正如他们所说的那样size_t
)并打破std::min
逻辑...但如果您有理由更喜欢size_t
(例如x == 0 ? 0 : x - 1
),您可以用不同的方式表达。
答案 4 :(得分:0)
如果简单意味着“易读”&#34;我建议您使用重载的[]运算符声明一个类。像常规数组一样使用它,但它会检查处理NODATA的边界。
如果简单就意味着&#34;高性能&#34;并且你有孤立数据的稀疏网格,考虑将链接列表实现到DATA值,并实现直接转换为tge DATA值的最佳运算符。