将行或列有效地插入存储在行中的矩阵并不困难 - 或col-major(分别)向量。将行插入col-major矢量或列到行主矢量中的问题稍微有点了。
例如,给定2x3矩阵存储在向量中的行主要:
1 2 3 <=> 1 2 3 4 5 6
4 5 6
以及在原始矩阵中第1列之后插入的列7 8
,我们得到:
1 7 2 3 <=> 1 7 2 3 4 8 5 6
4 8 5 6
[在col-major向量中插入一行是类似的。]
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}
在C ++中可以有多种方法来实现这种算法,但是我们正在寻找大型矩阵和多个插入的效率和可扩展性。
我能想到的第一个显而易见的选择是使用std::vector<double>::insert
将新元素插入正确的位置:
//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]);
,即使对于中等数据大小也是有效但非常慢,因为每次迭代都会调整大小并进行转换。
另一个更直接的选择是创建另一个向量,填充范围[0,colIndex),colIndex,(colIndex,n+1]
中的所有列,并将其与原始向量交换:
// 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);
};
比选项1更快,但需要额外的空间用于矩阵复制并迭代所有元素。
有没有其他方法可以达到这个目标,在速度/空间或两者中都能超越上述目标?
关于ideone的示例代码:https://ideone.com/iXrPfF
答案 0 :(得分:3)
这个版本可能会更快,特别是在规模上,并且可以作为进一步微优化的基础(如果[且仅当]真的有必要):
// 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);
}
这并不需要任何临时分配,除了可能的循环微优化之外,它可能和它一样快。为了理解它是如何工作的,我们可以从观察到原始矩阵的右下角元素在源向量中向右移动m
个元素开始。从最后一个元素向后工作,在某个时刻插入插入的列向量中的值,并且源向量中的后续元素现在仅向右移动m - 1
个元素。使用该逻辑,我们只需构建一个在源阵列上从右到左工作的3相循环。循环迭代m
次,每行一次。循环的三个阶段,对应于它的三行代码,是:
在变量命名方面也有很大的改进空间,算法当然应该用适当的输入参数封装在自己的函数中。一个可能的签名是:
void insert_column(std::vector<double>& matrix,
size_t rows, size_t columns, size_t insertBefore,
const std::vector<double>& column);
从这里开始,还有进一步的改进空间,使用模板使其成为通用的。
从那里,您可能会发现该算法可能超出矩阵。真正发生的是你拉链&#34;拉链&#34;两个向量连同跳过和偏移(即从元素i
开始,在每B
个元素之后将A
中的元素插入n
答案 1 :(得分:0)
所以我想要的是(完全未经测试的(tm))
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];
}
你的想法就是你
[1,2,3,4,5,6]
&amp;添加[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()次。
答案 2 :(得分:-1)
替代解决方案可以转置,然后再插入和转置。但是,就地转置是非平凡的(https://en.wikipedia.org/wiki/In-place_matrix_transposition)。请参阅此处的实施https://stackoverflow.com/a/9320349