我必须阅读一个文件,其中存有一个带汽车的矩阵( 1 = BlueCar,2 = RedCar,0 = Empty )。
我需要以这种方式编写一个算法来移动矩阵的汽车:
在阅读文件之前,我不知道矩阵大小,如果它是密集的或稀疏的,那么我必须实现两个数据结构(一个用于密集和一个对于稀疏)和两种算法。
我需要达到可能的最佳时间和空间复杂性。
由于未知的矩阵大小,我认为将数据存储在堆上。
如果矩阵密集,我想使用类似的东西:
short int** M = new short int*[m];
short int* M_data = new short int[m*n];
for(int i=0; i< m; ++i)
{
M[i] = M_data + i * n;
}
通过这种结构,我可以分配一个连续的内存空间,并且使用M[i][j]
访问它也很简单。
现在问题是为稀疏情况选择的结构,我还必须考虑如何以最简单的方式将汽车移动到算法中:例如,当我评估汽车时,如果在下一个位置(向下或向右)有另一辆车或者它是空的,我需要轻易找到。
最初我想要定义继承自一般Car对象的BlueCar和RedCar对象。在这个对象中,我可以保存矩阵坐标,然后将它们放入:
std::vector<BluCar> sparseBlu;
std::vector<RedCar> sparseRed;
否则我可以做类似的事情:
vector< tuple< row, column, value >> sparseMatrix
但是,在下一个位置找到什么的问题仍然存在。
可能这不是最好的方法,所以如何以有效的方式实现稀疏案例呢? (也使用稀疏的独特结构)
答案 0 :(得分:2)
为什么不直接在文件上创建memory mapping? (假设您的数据0,1,2存储在文件中的连续字节(或位)中,并且这些字节的位置也表示汽车的坐标)
这样您就不需要分配额外的内存并读入所有数据,并且可以使用M[i][j]
简单有效地访问数据。
遍历行将是L1缓存友好的。
如果数据非常稀疏,您可以扫描数据一次,并在内存中保留空区域/块的列表(只需要存储初始值和大小),然后您可以跳过(并在需要时调整)在进一步的运行。
使用内存映射时,只有经常访问的页面才会保留在内存中。这意味着一旦扫描了空区域,就只会为频繁访问的非空区域分配内存(所有这些都将由内核自动完成 - 无需自己跟踪它)。
另一个好处是您直接访问操作系统磁盘缓存。因此,无需在内核空间和用户空间之间继续复制和移动数据。
为了进一步优化空间和内存使用,汽车可以在文件中以2位存储。
<强>更新强>:
我将不得不使用openMP和MPI移动汽车...将内存映射 还可以使用并发线程吗?
您当然可以使用多线程,但不确定openMP是否是最佳解决方案,因为如果您同时处理数据的不同部分,您可能需要检查一些重叠区域(即汽车可以移动)从一个街区到另一个街区。)
或者你可以让线程在块的中间部分工作,然后启动其他线程来做边界(红色汽车是一个字节,蓝色汽车是一整行)。
您还需要一个锁定机制来调整稀疏区域的列表。我认为最好的方法是启动单独的线程(当然,取决于数据的大小)。
答案 1 :(得分:1)
在一个类似的任务中,我只是使用了Compressed Row Storage。
压缩行和列(在下一节中)存储格式 是最普遍的:他们完全没有假设 矩阵的稀疏结构,它们不存储任何不必要的东西 元素。另一方面,他们不是很有效率,需要一个 每个标量操作的间接寻址步骤 矩阵向量积或预处理器求解。
您需要更加具体地了解时间和空间复杂性要求。 CSR需要额外的索引步骤才能进行简单的操作,但如果您只是进行简单的矩阵操作,那么这只是一小部分开销。
There's already an existing C++ implementation available online as well.