如何旋转一个向量的向量

11

我正在寻找一种优雅的方法来旋转一个向量的向量,最好使用STL算法或boost。示例数据如下:

vector<vector<int> > vm;
vector<int> v;
v.push_back(1);
v.push_back(2);
vm.push_back(v);
v.clear();
v.push_back(3);
v.push_back(4);
vm.push_back(v);
v.clear();
v.push_back(5);
v.push_back(6);
vm.push_back(v);

1   2
3   4
5   6

我想获得像这样的整数向量的向量

1   3   5
2   4   6

它总是2乘N到N乘2吗? - Flexo
5个回答

9

我想最简单的解决方案就是编写一个简单的transpose函数,在其中使用两个循环:

std::vector<std::vector<int> > transpose(const std::vector<std::vector<int> > data) {
    // this assumes that all inner vectors have the same size and
    // allocates space for the complete result in advance
    std::vector<std::vector<int> > result(data[0].size(),
                                          std::vector<int>(data.size()));
    for (std::vector<int>::size_type i = 0; i < data[0].size(); i++) 
        for (std::vector<int>::size_type j = 0; j < data.size(); j++) {
            result[i][j] = data[j][i];
        }
    return result;
}

返回值应该很容易被优化掉。我认为使用任何标准函数都不会使其更简单或更有效,但我可能错了。

另一种方法是将所有数据存储在一个平坦的 vector 中,然后使用 i*row_length + j 或类似的方式计算元素 i, j 的位置。这样,转置不涉及数据的复制,而只需改变索引的计算。


1
唯一的问题是你实际上需要移动数据。对于小数组来说还好,但对于大数组来说并不是一个好主意。你已经将一个O(1)的问题转化为了一个O(n)的问题。 - Martin York
@Martin:我同意。OP说他想要一个向量的转置向量,而唯一实现这一点的方法就是复制数据。个人而言,我更喜欢像你提出的包装器方法。 - Björn Pollex
这很好。我也会将“ints”转换为模板参数。 - Trevor Hickey

7

使用Boost MultiArray,看起来我需要在编译时知道我的结构的大小。除非没有其他选择,否则这不是很好。 - user754425
@user:你需要知道维度的数量,而不需要知道大小。 - Benjamin Lindley
我又添加了一个建议。但在C++中,它永远不会优雅! - YXD
第二个链接似乎已经失效了。 - Benjamin Lindley

3
我会介绍一个包装器,将行转换为列,列转换为行。这将避免您复制所有数据。
#include <iostream>
#include <vector>

template<typename T>
class TwoDPivotWrapper
{
    public:
        // These two typedef's were done with std::vector
        // in mind. But with a small amount of effort I am
        // sure they can be generalized. They are solely to define
        // value_type (the data stored in the 2-D array).
        typedef typename T::value_type          OneDType;
        typedef typename OneDType::value_type   value_type;

        // A constructor that wraps a 2-D structure.
        TwoDPivotWrapper(T& o)
            : object(o)
        {}

        // A helper class used to store the row after the first array accesses.
        class Row
        {
            friend class TwoDPivotWrapper;
            Row(TwoDPivotWrapper& w, size_t r)
                : wrapper(w)
                , row(r)
            {}

            TwoDPivotWrapper&    wrapper;  
            size_t               row;

            public:
                value_type operator[](size_t col)
                {                    
                    return wrapper.get(row,col);
                }
        };

        // The operator [] returns a Row object that overloads
        // the operator[] for the next dimension.
        Row operator[](size_t row)              {return Row(*this, row);}

        // Generic get function used to access elements.
        // Notice we swap the row/col around when accessing
        // the underlying object.
        value_type get(size_t row, size_t col)  {return object[col][row];}

    private:
        T&  object;
};

典型的使用方法如下:

int main()
{
    typedef std::vector<std::vector<int> >      TwoDVector;

    TwoDVector  data(3,std::vector<int>(2,0));

    data[0][0]  = 1; data[0][1]  = 2;
    data[1][0]  = 3; data[1][1]  = 4;
    data[2][0]  = 5; data[2][1]  = 6;

    TwoDPivotWrapper<TwoDVector>               wrapper(data);
    std::cout << wrapper[0][0] << wrapper[0][1] << wrapper[0][2] << "\n";
    std::cout << wrapper[1][0] << wrapper[1][1] << wrapper[1][2] << "\n";
}

1
如果你要在C++中进行大量的线性代数计算,那么你应该看看Boost.uBlas
#include <boost/numeric/ublas/matrix.hpp>

template <class M>
void printMatrix(const M& m)
{
    for (size_t i=0; i<m.size1(); ++i)
    {
        for (size_t j=0; j<m.size2(); ++j)
            std::cout << m(i,j) << " ";
        std::cout << std::endl;
    }
}

int main()
{
    namespace ublas = boost::numeric::ublas;
    typedef ublas::matrix<double> Matrix;
    Matrix A(3, 2);

    // Fill matrix A with incrementing values
    double counter = 0.0;
    for (size_t i=0; i<A.size1(); ++i)
        for (size_t j=0; j<A.size2(); ++j)
            A(i,j) = ++counter;

    printMatrix(A);
    std::cout << std::endl;

    // Transpose A and store it in B
    Matrix B = ublas::trans(A);
    printMatrix(B);

    return 0;
}

0

一定要使用向量的向量吗?

如果不是,那么您可能可以在普通的行*列元素向量上实现一个包装器。

类似于:

class Vector
{
public:
    int operator[]( int index )
    {
        return 1;
    }
    friend class Wrapper;
private:
    Vector( std::vector<int> & vector, int index, int numElements );

    std::vector<int> v_;
    int index_;
    int numElements_;
};

class Wrapper
{
public:
    Vector operator[](int index)
    {
        return Vector( v_, index, numColumns_ );
    }

    Wrapper( int numRows, int numColumns);
    void setNumRows( int numRows );
    void setNumColumns( int numColumns );
private:
    std::vector<int> v_;
    int numRows_;
    int numColumns_;
};

以及在主函数中:

Wrapper w;

int i = w[1][1];

编辑:

向量类将表示行或列数据。它需要作为参数传递线性化矩阵、矩阵几何(行数和列数)以及它所代表的行/列。

请用纸笔尝试以下操作: 1. 以矩阵形式写出3x4矩阵的元素

11 12 13 14 21 22 23 24 31 32 33 34

  1. 将它们以线性化方式写出:

11 12 13 14 21 22 23 24 31 32 33 34

一个元素在矩阵形式中的索引和它在线性化形式中的索引之间存在简单的关系,这取决于矩阵几何。

  1. 如果您想让向量“包含”元素21 22 23 24(或12 22 32),那么您需要在operator [](int index)中实现一些逻辑,以确定从Vector[index]获取的元素在底层向量中的索引。

假设您的向量需要引用矩阵的第二列(即12 22 32)。 那么: - 向量中的第一项(即Vector [1])是线性化矩阵中的第二个元素 - 向量中的第二项22是基础向量中的第6个元素 - 向量中的第三项32是v_的第10个元素

现在,用铅笔和纸做同样的事情,针对矩阵的第三列(元素13 23 33)。 看看您是否发现第二列的基础向量中的索引(分别为2、6和10)与您找到的第三列的索引之间存在任何相似之处。


你的解决方案看起来比马丁的简单,但我不确定如何使用它。你能展示一下如何迭代枢轴以检索行吗? - user754425

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