【问题标题】:How to create a matrix with user-specified dimension如何创建具有用户指定维度的矩阵
【发布时间】:2017-02-04 10:41:20
【问题描述】:

我需要根据用户的输入存储一些数据。我的程序应该根据我从 cin 获得的 int 值创建不同的数据结构。

例如,值 0 存储标量、1 - 向量、2 - 2dArray、3 - 3dArray、4 - 4d 数组等等...

我的问题是是否可以编写一些代码来做到这一点。

我知道这听起来可能令人困惑,所以我将提供更多示例。

假设用户输入 0 5,那么我的程序应该创建一个 int 变量并将 5 存储在其中。

如果用户输入 1 5,7,6,我的程序应该创建一个向量 = {5,7,6};

如果用户输入 2 2,3 1,2,3,4,5,6 我的程序应该创建一个二维数组 a[2][3] 并将值存储在那里。

如果我知道用户将拥有的最大维度数,我可以找到解决方案,但分配的目标是可以指定任意数量的维度...

请帮忙

【问题讨论】:

  • 嗯,不,除非您对维数定义一些限制,否则这是不可能的,因为它将是无限递归的(n 维数组是 (n-1) 维数组的数组,所以做你想做的事情需要一个无限递归的类型定义,除非你指定一个边界)。您可以做的是使用一维数组(或向量)并管理索引来模拟您寻求的行为。实际上,需要 10 维或更多维的实际应用程序非常少,而且此类数组的内存使用量将很快超过可用内存。
  • 我也在考虑仿真..你能更详细地描述一下吗?你将如何实现它?
  • 分配一个大小等于维度乘积的数组。它具有多维数组应具有的元素数量。计算出一组索引和一维数组中的索引之间的映射。

标签: c++ arrays matrix


【解决方案1】:

如果您放宽一些要求(“根据我从 cin 获得的 int 值创建不同的数据结构”),这是可能的。这只是一个草图,不是完整的答案,但希望它能让你走上正轨。

访问存储空间:

您需要将数字存储在所需类型的单个数组中,并通过索引映射函数包装对它们的访问。 例如,在 2D 中,一个这样的函数是

int indexIn2D(uint rowCol[2], int rowCount) {
  // rowCol[0] - is row index, rowCol[1] is col index
  return rowCol[0]*rowCount + rowCol[1];
}

float* values=new float[numRows*numCols];

// I want the element at (i,j)
float& myIJElement=values[indexIn2D({i,j}, numRows)];

将其转换为 N 维将需要以下内容

// suppose I'm storing a 3D matrix with [3 slices, 4 rows, 5 columns]
// as a 1D array and I want the element at [x,y,z] - what's it would be
// the position of that element in my 1D array?
// - fill coodIndex[3] with {x,y,z}
// - pass nDim=3
// - the dimSignature will be {3 /*slices*/, 4 /*rows*/, 5 /*columns*}
int indexInND(uint coordIndex[], uint numDim, uint[] dimSignature) {
   int ret=coordIndex[0];
   for(uint i=0; i<numDim-; i++) {
      ret=ret*dimSignature[i]+coordIndex[i+1];
   }
   return ret;
}

类似变体的存储类型

好吧,我们已经知道我们会将整个“N-dim 块”存储为目标类型的unidim 数组。所以我们可以利用指针,让我们的“存储”类似于

struct NDimStorage {
  // 0 for int, 1 for float, etc.
  int whichType; // or make it enum
  union {
    int* int_st;
    float* float_st;
  };
  uint numDims;
  uint dimSignature[];
};

std::vector 的后备 fom 变体

类似:

template <typename NumType> class VectNStorage {
   std::vector<NumType> storage;
   std::vector<uint> dimSignature;
protected:
   size_t indexMapping(const std::vector<uint>& indices) {
     size_t ret=indices[0];
     for(uint i=0; i<this->dimSignature.size()-1) {
       ret=ret*this->dimSignature[i]+indices[i+1];
     }
     return ret;
   }
public:
  VectNStorage(const std::vector<uint> dimsDef) : storage(), dimSignature(dimsDef) {
    uint howMany=1;
    for(auto d : this->dimSignature) {
      howMany*=d;
    }
    this->storage.resize(howMany);
    std::fill(this->storage.begin(), this->storage.end(), 0);
  }

  NumType& at(const std::vector<uint>& indices) {
    return this->storage[this->indexMapping(indices)];
  }
}

【讨论】:

  • 1.如何将二维数组映射到一维向量?把这些行一个接一个地放好。 2. 你是如何在 nDim 中做到这一点的?好吧,取最后两个暗淡并将它们“线性化”,如 1 所示,您将得到一个 nDim-1 数组。重复此操作,直到完成一个一维数组。
  • 我可以用向量做上面的事情,这样我就不用担心内存分配了吗?
  • 如果您使用向量,您将无法使用NDimStorage 类似变体的结构。但是索引映射函数仍然有效。
  • @kozouu "我可以用向量做上面的事情吗" 在那里,作为一个草图(即使编译也没有尝试:把它当作伪代码 C++ 风格)。有意义吗?
  • 让我想一想:D
【解决方案2】:
  1. 您知道如何读取用户输入吗?如果没有,请先弄清楚如何在 C++ 中执行此操作。
  2. 从用户输入中读取一个变量,它将告诉您要创建的结构的类型。 (提示,使用int
  3. 使用适当的检查来查看用户输入的内容(提示,使用 if() {} else if() {} 等。
  4. 根据初始类型,构造适当的结构,提示创建所有类型,例如struct Scalar{}struct Vector{} 等。
  5. 在您创建的每个结构中调用read() 方法以读取用户输入的其余部分。 (您将需要使用某种形式的机制来拆分用户输入,提示:字符串标记化)
  6. 拒绝您不支持的类型。

【讨论】:

    【解决方案3】:

    您可以对所有类型的用户输入使用纯 std::vector。您唯一需要的是一些函数 f(x),它将 k 大小的索引向量映射到普通数组中的索引。

    例如:x - k 大小的索引向量,s - k 大小的维度大小向量

    k = 1 -> f1(x,s) = x1;
    k = 2 -> f2(x,s) = x2*s2 + x1;
    k = 3 -> f3(x,s) = x3*s3*s2 + x2*s2 + x1 = x3*s3*s2 + f2(x2,x1,s2,s1);
    k = 4 -> f4(x,s) = x4*s4*s3*s2 + f3(x3,x2,x1,s3,s2,s1);
    

    最后一个坐标的乘数是 (k - 1) 维对象的体积。 此外,我们认为,坐标的编号从 0 开始。

    这看起来像递归函数,很容易实现。您只需要计算 (k - 1) 维对象的移位,从数组中弹出坐标并再次从自身调用函数。

    【讨论】:

    • 这看起来可行,但是你说的 k 大小的索引向量和 k 大小的维度大小向量是什么意思?你能更详细地描述一下递归吗?
    • @kozouu 如果你的数据结构有k 维度,那么你需要k 索引来寻址这个结构的元素这是k 大小的索引,使用向量来表示是很自然的k 索引。与数据结构的大小相同。您需要确定每个维度的大小,它给出k 大小。它们也可以存储为向量。
    • @SemyonBurov 不需要递归(除了智能实现正在发生的事情)。索引映射的公式类似于计算多项式的值。
    • @AdrianColomichi 是的,这可以实现为循环,但使用递归代码可能看起来更好,更容易理解。我们几乎可以保证堆栈不会溢出 - 普通数组的内存会更快耗尽,所以这是个人喜好问题,而不是性能和安全问题。
    • @SemyonBurov - :D 啊,是的,"everything is better with bluetooth" ;) 性能方面 - 对于少数维度,这可能会被使用,也许它不会刮得太多,但要保持考虑到“在矩阵中定位”将是最频繁的操作,它可能会导致性能的显着变化(我不是说“显着”,只是显着)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    • 2021-05-12
    相关资源
    最近更新 更多