如何将Armadillo矩阵转换为立方体?

4

我正在尝试重新创建以下Python numpy代码:

num_rows, num_cols = data.shape
N = 4
data = data.reshape(N, num_rows/N, num_cols)

如何在C++中使用Armadillo矩阵和立方体进行操作,以最高效的方式完成。我认为直接使用resize/reshape操作无法将2D矩阵转换为3D立方体。

2个回答

2
使用其中一种高级构造器是构建这样一个立方体最快的方法。它们允许您直接从任意内存部分创建新对象,甚至不需要复制任何数据。这与NumPy进行重塑的方式最为相似,返回原始数据的视图,而不是副本。
您可以像这样使用构造器:
// Assuming a is an arma::mat.
int N = 4;
arma::cube c(a.memptr(), N, a.n_rows / N, a.n_cols, false);

这个操作直接从 a 中获取内存,而不是复制,然后将其用作 c 的数据。当然,这样做速度很快,但也很危险。您需要确保指向的内存在 c 存在期间一直有效。这意味着 c 的生命周期必须严格嵌套在 a 的生命周期内。这可能很难确保,特别是当 ac 都在堆上创建时。您还可以允许 c 复制 a 的数据,方法是省略最后一个参数或将其设置为 true。这比无复制构造函数要慢一些,但比逐个分配来自 a 数据的每个片段要快,因为该构造函数对底层数据进行单个批量 memcpy。所有这些都受到 @ewcz 答案中提出的行 vs. 列主要点的影响。请确保知道重塑时会得到什么,特别是如果您正在使用高级构造函数。

使用构造函数很诱人,但我认为在这种情况下不适用 - data.reshape(N,num_rows / N,num_cols)本质上通过将data的行分成N组来创建N个切片。由于armadillo以列优先的方式存储数据,因此这些切片在内存中不会是连续的,需要进行一些"重新洗牌"。构造函数方法适用于像data.reshape(N, num_rows, num_cols/N)这样的东西,即在"列"空间中拆分矩阵... - ewcz
@tangy 这就是我上一条评论的意思。你需要使用原始数组的转置,因为Armadillo使用列主序,而NumPy使用行主序。但是如果你知道如何切片以获得所需内容,这个构造函数将会更快。 - bnaecker
我确实尝试了那个 - 我转置了矩阵,然后应用了上面的代码 - 它仍然不起作用。事实上,基于一些关于armadillo的其他答案,我早先就尝试过这种方法,包括转置和不转置 - 都没有起作用。 - tangy

1

由于armadillo使用列优先的数据排序方式(与numpy依赖行优先的排序方式不同),因此简单地将矩阵放入一个只有1个切片的立方体中并进行重塑会产生不同的结果(下面是矩阵B)。另一种选择可能是手动构建切片:

#include <iostream>
#include <armadillo>

int main(){

    const arma::uword N = 2;
    const arma::uword num_rows = 4;
    const arma::uword num_cols = 3;

    arma::mat A(num_rows, num_cols, arma::fill::randu);

    std::cout << A;

    arma::cube B(num_rows, num_cols, 1);
    B.slice(0) = A;
    B.reshape(num_rows/N, num_cols, N);
    std::cout << B;

    arma::cube C(num_rows/N, num_cols, N);
    for(arma::uword i = 0; i < N; ++i){
        C.slice(i) = A.rows(i*N, (i+1)*N-1);
    }
    std::cout << C;


    return 0;
}

考虑到这需要付出的努力,我觉得直接使用二维矩阵可能更好。这就是为什么我将我的三维切片操作转换为二维操作的原因。 - tangy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接