使用C++矩阵创建自定义索引

3
我希望能够像操作 R 语言的数据框一样,在 C++ 中处理双维数组(矩阵)。我的意思是,我希望能够指定矩阵的索引值。
例如,以下是一个普通的 C++ 整数矩阵:
  0 1 2 3 4 ...
0 1 0 1 0 .
1 3 . . .
2 8 . .
3 . .
4 .
.
.
.

我想指定矩阵中的索引,例如:

  5 7 8 13 24 ...
0 1 0 1 0 .
1 3 . . .
2 8 . .
6 . .
8 .
.
.
.

任何建议将不胜感激。

那么以前的 Martix[0][0] = 1 现在可以通过任意索引来访问,例如 Matrix[0][5] - perreal
你需要定义一个自定义的矩阵类。在C++中,数组是由连续整数索引的连续内存块的原语。 - StoryTeller - Unslander Monica
1
你在这里考虑的索引方案完全不清楚。 - Matt Phillips
2
如果您没有使用过R和C++,这可能不太清楚!我认为他想能够使用自定义索引来访问矩阵。所以类似于,使用matrix["6"]["5]或matrix ["6","5"]来访问第4行第1列的单元格。 - TooTone
如果像TooTone所描述的那样,您需要一个索引xlat中介,这应该不难实现(我想在R中也至少需要这样一种东西(这是我完全不了解的语言)。 - WhozCraig
3个回答

3
如果您想要交换矩阵的列或行,可以使用一些间接方法:
 indexTable[0][0] = 0; // map row index 0 to 0
 indexTable[1][0] = 5; // map column index 0 to 5

并像这样使用它:

 value = matrix[indexTable[0][RowIndex]][indexTable[1][ColumnIndex];

或者你可以编写一个类来处理这种间接引用。

0
在R中,data.frame本质上只是一种对列的list的精美包装器,而list则是与C++中的std::map1相当接近(而不是像其名称所示的那样接近std::list),这在逻辑上是不合理的。
换句话说,您可以使用类似于此的typedef来近似data.frame
typedef std::map<int, std::vector<int>> data_frame;

...但是R类实际上更加强大,因为它们在某种程度上是通用的,支持数据框中的不同类型,检查所有行是否具有相同的长度,并允许对列和行进行命名访问。最后,我们不要忘记R支持使用数据框、漂亮地打印它们以及高效地加载和保存它们。

根据您的需要,当然没有必要在C++中复制所有这些,但将结构封装在类中并提供适当的接口来访问它肯定是有益的。


1 实际上,这是一个std::unordered_map。需要使用C++11。


0
我会创建一个类,其中包括以下功能:
  • 将任意指定的索引转换为从0开始递增的索引
  • 封装一个1D数组,以便您可以使用转换后的索引将其作为2D数组访问
下面是一个可行的示例:
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#include <cassert>

using namespace std;

class DataFrame
{
  vector<int> data;

public:
  typedef vector<ssize_t> idx_t;
private:
  idx_t rowIdx;
  idx_t colIdx;

public:
  DataFrame(const idx_t &rowIdx, const idx_t &colIdx)
    : data(rowIdx.size() * colIdx.size())
    , rowIdx(rowIdx)
    , colIdx(colIdx)
  {
    assert(is_sorted(rowIdx.begin(), rowIdx.end()));
    assert(is_sorted(colIdx.begin(), colIdx.end()));
  }

  int& operator()(int i, int j)
  {
    idx_t::iterator itI, itJ;

    itI = lower_bound(rowIdx.begin(), rowIdx.end(), i);
    if(rowIdx.end() == itI || i != *itI) throw out_of_range("could not find specified row");
    itJ = lower_bound(colIdx.begin(), colIdx.end(), j);
    if(colIdx.end() == itJ || j != *itJ) throw out_of_range("could not find specified col");

    return data[distance(rowIdx.begin(), itI)*colIdx.size() +
      distance(colIdx.begin(), itJ)];
  }

  vector<int> & getData() { return data; }
};


int main()
{
  DataFrame::idx_t rI, cI;
  rI.push_back(3);
  rI.push_back(5);

  cI.push_back(2);
  cI.push_back(3);
  cI.push_back(10);

  DataFrame df(rI, cI);

  df(3,2) = 1;
  df(3,3) = 2;
  df(3,10) = 3;
  df(5,2) = 4;
  df(5,3) = 5;
  df(5,10) = 6;

  ostream_iterator<int> out_it(cout, ", ");
  copy(df.getData().begin(), df.getData().end(), out_it);
  cout << endl;

  return 0;
}

每行/列的任意索引都在一个向量中指定。为了保持一些性能,代码要求索引单调递增。(如果你使用的是 C++11,则在 ctor 中检查;如果你不使用 C++11,则没有 is_sorted 函数。此外,该代码不验证任意索引的唯一性。)

当你访问数据时,它只会在每个索引向量上进行二分搜索,以找到与任意索引匹配的位置,并将该位置用作对底层数据的相应索引。有一个简单的方法将 2D 索引转换成 1D 索引。

如果你需要担心这个问题,可以检查我的索引错误检查是否正确。

我会让你在const访问器、各种构造函数等方面添加更多的健壮性/功能。如果你想将其推广到除2维之外的数组,我建议你创建一个类,仅用于将任意索引转换为基于0的索引,这将消除一些代码重复。还有其他方法可以将任意索引转换为基于0的索引,例如使用像其他人建议的map。在这种情况下,存在一些问题,例如map的创建者必须确保如果有10列,则[0,10)中的每个索引都恰好出现一次作为映射中的值。


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