C++ 有效地将列/行插入存储在row/col主向量中的矩阵中

C++ 有效地将列/行插入存储在row/col主向量中的矩阵中,c++,matrix,insert,C++,Matrix,Insert,有效地将行或列插入到存储在行中的矩阵并不困难- 或col主(分别)向量。将行插入列主向量或将列插入行主向量的问题稍微有趣一些 例如,给定一个2x3矩阵,该矩阵存储在向量的主行中: 1 2 3 <=> 1 2 3 4 5 6 4 5 6 [将行插入col主向量类似。] C++中的示例设置: auto m = 2; // #rows auto n = 3; // #cols // row-major vector auto x = std::vector<doubl

有效地将行或列插入到存储在行中的矩阵并不困难- 或col主(分别)向量。将行插入列主向量或将列插入行主向量的问题稍微有趣一些

例如,给定一个2x3矩阵,该矩阵存储在向量的主行中:

1 2 3    <=>    1 2 3 4 5 6 
4 5 6
[将行插入col主向量类似。]

C++中的示例设置:

auto m = 2; // #rows
auto n = 3; // #cols
// row-major vector
auto x = std::vector<double>{1,2,3,4,5,6};

auto const colIndex = 1;
auto const col = std::vector<double>{7,8};
// insert column {7,8} into the 2nd position
// =>{1,7,2,3,4,8,5,6}
这比选项1快得多,但需要额外的空间用于矩阵副本和迭代所有元素

是否有其他方法可以在速度/空间或两者方面超越上述方法


ideone上的示例代码:

此版本可能要快得多,尤其是在规模上,并且可以作为进一步微观优化的基础(如果[且仅当]确实需要):

从这里开始,在使用模板使其通用化方面还有进一步的改进空间


从这里,你可能会发现,这个算法有可能应用于矩阵以外的领域。真正发生的是,你正在“压缩”两个向量,加上一个跳转和一个偏移量(即,从元素
i
开始,在每个
n
第个元素之后,将
B
中的一个元素插入
a

<。然而,非平凡()中的就地转置。请参见此处的实现,因此我将使用类似(完全未测试(tm))的内容

x.resize(x.size()+col.size());
对于(已处理大小=0;已处理<列大小();++已处理){
//移动第n行的元素(从末尾开始)
//到他们的新位置
自动开始=x.end()-(已处理+1)*行大小;
自动结束=开始+行大小;
自动中间=结束-(列大小()-已处理);
标准::旋转(开始、中间、结束);
//将其中一个默认值项替换为新值
x[x.size()-rowSize*(1+已处理)]=列[col.size()-processed-1];
}
这个想法是你从
[1,2,3,4,5,6]
&添加
[a,b,c]

调整大小:
[1,2,3,4,5,6,x,x,x]

第一个循环移位:
[1,2,3,4,x,x,x,5,6]

第一回路替换
[1,2,3,4,x,x,c,5,6]

二回路移位
[1,2,x,x,3,4,c,5,6]

等等

因为std::rotate是线性的,每个项目只移动一次;这也应该是线性的


这与您的选项#1不同,因为每次插入时,您都必须在插入后移动所有内容;这意味着最后的x元素被移位了col.size()次。

出于好奇,您是否尝试了第一个选项,但首先调用它,以便只需重新分配一次?仍然会有重复换档(可以优化)。我正在构思一个答案来解决这部分问题。@TypeIA说得好-谢谢。我试过了——即使是100k*10k大小的矩阵,改进也可以忽略不计。但更好的是,你是对的,我来编辑。你为什么从向量的前面开始移位?从后面开始,然后只需移动一次,它就会变成一个单独的for循环,而不是一个嵌套的for循环一般来说,根据需要对它执行的操作来确定要使用哪个容器更为合理,而不是先选择一个容器,然后找出如何以最有效的方式完成需要做的事情。如果需要在矩阵中同时插入有效的行和列,那么一维向量就不是合适的容器。很难想出如何有效地实现这一点。要做到这一点,需要一个相当复杂、非线性的容器。@SamVarshavchik谢谢-真实的故事。但是,此插入功能只是许多操作中的一个,而不是最重要的操作。还有其他需要连续内存的线性代数例程,所以数组/向量几乎是这里唯一的选择,嗯,很有趣。。我会考虑这一点,并对其进行测试。我会让你知道的。谢谢我做了一些快速测试,似乎您的方法对于较小的数据要稍微快一点,对于较大的数据要慢一点。例如,对于100k*10k矩阵,我的问题中的选项2需要约25秒,而您的选项2需要39秒(几次运行的平均值)。也许编译器优化和类似的事情也可以在这里发挥作用。我会做更多的测试并公布结果。不管怎么说,你的解决方案很有趣,即使是b/c,也没有额外的空间。谢谢,这是一种有效的方法,但我不认为插入2*转置比上述任何解决方案都好
auto m = 2; // #rows
auto n = 3; // #cols
// row-major vector
auto x = std::vector<double>{1,2,3,4,5,6};

auto const colIndex = 1;
auto const col = std::vector<double>{7,8};
// insert column {7,8} into the 2nd position
// =>{1,7,2,3,4,8,5,6}
//option 1: insert in-place
x.reserve(m*(n+1));
for(auto i = 0; i < col.size(); i++)
   x.insert(begin(x) + colIndex + i * (n + 1), col[i]);
// option 2: temp vec and swap
{
    auto tmp = std::vector<double>(m*(n+1));

    for(auto i = 0; i < m; i++)
    {
        for(auto j = 0; j < colIndex; j++)
            tmp[j + i * (n + 1)] = x[j + i * n];

        tmp[colIndex + i * (n + 1)] = col[i];

        for(auto j = colIndex + 1; j < n + 1; j++)
            tmp[j + i * (n + 1)] = x[(j - 1) + i * n];
    }

    std::swap(tmp, x);
};
// one-time reallocation of the vector to get space for the new column
x.resize(x.size() + col.size());

// we'll start shifting elements over from the right
double *from = &x[m * n];
const double *src = &col[m];
double *to = from + m;

size_t R = n - colIndex; // number of cols left of the insert
size_t L = colIndex;     // number of cols right of the insert

while (to != &x[0]) {
    for (size_t i = 0; i < R; ++i) *(--to) = *(--from);
    *(--to) = *(--src); // insert value from new column
    for (size_t i = 0; i < L; ++i) *(--to) = *(--from);
}
void insert_column(std::vector<double>& matrix,
    size_t rows, size_t columns, size_t insertBefore,
    const std::vector<double>& column);
x.resize(x.size() + col.size());

for (size_t processed = 0; processed < col.size(); ++processed) {
    // shift the elements for row n (starting at the end) 
    // to their new location
    auto start = x.end()-(processed+1) * rowSize;
    auto end = start + rowSize;
    auto middle = end - (col.size()-processed);
    std::rotate(start, middle, end);

    // replace one of the default value items to be the new value
    x[x.size()- rowSize*(1+processed)] = col[col.size()-processed-1];
}