最近,我正在做一些关于C ++指针的事情,当我想要使用包含索引的1维数组访问多维数组中的元素时,我得到了这个问题。
假设我有一个数组arr
,它是一个四维数组,所有元素都设置为0
,arr[1][2][3][4]
除外1
,数组{{ 1}}包含idx
每个维度中的索引,我可以使用arr
或arr[idx[0]][idx[1]][idx[2]][idx[3]]
来访问此元素。
当*(*(*(*(arr + idx[0]) + idx[1]) + idx[2]) + idx[3])
很大时会出现这个问题,这会不太好,所以我想知道是否有更好的方法来处理多维访问?
n
输出
#include <bits/stdc++.h>
using namespace std;
#define N 10
int main()
{
int arr[N][N][N][N] = {0};
int idx[4] = {1, 2, 3, 4};
arr[1][2][3][4] = 1;
cout<<"Expected: "<<arr[1][2][3][4]<<" at "<<&arr[1][2][3][4]<<endl;
cout<<"Got with ****: ";
cout<<*(*(*(*(arr + idx[0]) + idx[1]) + idx[2]) + idx[3])<<endl;
return 0;
}
答案 0 :(得分:0)
我找到了解决这个问题的方法。
我们的想法是使用void *
指针,我们知道每个存储单元都保存存储单元的值或地址,因此我们可以直接计算目标到基址的偏移量。
在这种情况下,我们使用void *p = arr
来获取n-d数组的基地址,然后循环遍历数组idx
,以计算偏移量。
对于arr[10][10][10][10]
,arr[0]
和arr[1]
之间的偏移量为10 * 10 * 10 * sizeof(int)
,因为arr
为4-d,arr[0]
和{{ 1}}是3-d,因此arr[1]
和10 * 10 * 10 = 1000
之间有arr[0]
个元素,之后我们应该知道两个arr[1]
个相邻地址之间的偏移量为{ {1}},所以我们应该乘以void *
得到正确的偏移量,根据这个,我们最终得到我们想要访问的内存单元的确切地址。
最后,我们必须将1 byte
指针转换为sizeof(int)
指针并访问该地址以获取正确的int值,这就是它!
使用void *
(不太好)
int *
<强>输出强>
void *
注意: 编译时会出现警告,但我选择忽略它。
#include <bits/stdc++.h>
using namespace std;
#define N 10
int main()
{
int arr[N][N][N][N] = {0};
int idx[4] = {1, 2, 3, 4};
arr[1][2][3][4] = 1;
cout<<"Expected: "<<arr[1][2][3][4]<<" at "<<&arr[1][2][3][4]<<endl;
cout<<"Got with ****: ";
cout<<*(*(*(*(arr + idx[0]) + idx[1]) + idx[2]) + idx[3])<<endl;
void *p = arr;
for(int i = 0; i < 4; i++)
p += idx[i] * int(pow(10, 3-i)) * sizeof(int);
cout<<"Got with void *:";
cout<<*((int*)p)<<" at "<<p<<endl;
return 0;
}
使用Expected: 1 at 0x7fff5e3a3f18
Got with ****: 1
Got with void *:1 at 0x7fff5e3a3f18
代替test.cpp: In function 'int main()':
test.cpp:23:53: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
p += idx[i] * int(pow(10, 3-i)) * sizeof(int);
(更好)
由于我们希望逐字节操作指针,因此最好使用char *
来替换void *
。
char *
<强>输出强>
void *
使用#include <bits/stdc++.h>
using namespace std;
#define N 10
int main()
{
int arr[N][N][N][N] = {0};
int idx[4] = {1, 2, 3, 4};
arr[1][2][3][4] = 1;
cout<<"Expected: "<<arr[1][2][3][4]<<" at "<<&arr[1][2][3][4]<<endl;
char *p = (char *)arr;
for(int i = 0; i < 4; i++)
p += idx[i] * int(pow(10, 3-i)) * sizeof(int);
cout<<"Got with char *:";
cout<<*((int*)p)<<" at "<<(void *)p<<endl;
return 0;
}
(在此特定情况下)
我被告知,对于算术中使用的Expected: 1 at 0x7fff4ffd7f18
Got with char *:1 at 0x7fff4ffd7f18
来说,这不是一个好习惯,最好使用int *
,因此我将void *
转换为int *
指针并替换arr
。
int *
输出
pow
答案 1 :(得分:0)
构造算法用于索引多维数组的方式将根据所选语言而有所不同;你用C和C ++标记了这个问题。我会坚持使用后者,因为我的回答与C ++有关。一段时间以来,我一直在研究类似但不同的东西,所以当我构建一个多用途多维矩阵类模板时,这成为一个有趣的问题。
我发现有关更高水平的多维向量和矩阵的内容是,在理解更高维度的本质时,3的顺序重复地创造了奇迹。在考虑算法软件实现方面之前,请在几何视角中考虑这一点。
从数学上讲,让我们考虑0的最低维度,第一个形状是0维对象。这恰好是该点可以具有无限量的坐标位置属性的任意点。诸如p0(0),p1(1),p2(2,2),p3(3,3,3),... pn(n,n,... n)之类的点,其中每个对象都指向具有已定义维度属性数的特定区域设置。这意味着没有诸如长度,宽度或高度之类的线性距离,并且相反地,在任何方向或尺寸上没有任何方向或尺寸的大小,其中该形状或物体没有限定幅度并不限定任何面积,体积或更高的体积尺寸。同样利用这些0维点,不会意识到方向,这也意味着没有定义幅度的旋转角度。另一件需要考虑的事情是任何一个点也是零向量。另一个有助于理解这一点的是通过使用代数多项式,使得线性的f(x) = mx+b
是一维方程,形状(在本例中是一条线)或图形,f(x) = x^2
是二维的, f(x) = x^3
是三维的,f(x) = x^4
是四维的,依此类推,直至f(x) = x^n
,其中这将是N维。在关联两个不同的点以至少为您提供至少1个具有指定方向的线段或矢量之前,不能定义长度或幅度,方向或旋转角度,面积,体积等。一旦你有一个隐含的方向,你就有了斜率。
当看数学运算时,最简单的是加法,它只不过是一个线性平移,一旦你引入加法,你还会引入所有其他运算,如减法,乘法,除法,幂和基;一旦你有乘法和除法,你就可以定义旋转,旋转角度,面积,体积,变化率,斜率(也是正切函数),从而定义几何和三角函数,然后它们也会导致积分和导数。是的,我们都有数学课,但我认为这对于理解如何构建一个数量级与另一个数量级的关系很重要,这将有助于我们一旦你知道如何轻松地完成更高维度的订单构建它。一旦你能够理解,即使你的更高级别的操作只不过是加法和减法的扩展,你将开始知道它们的连续操作本质上仍然是线性的,只是它们扩展到多个维度。
早期我说过3的顺序重复地创造了奇迹,所以让我解释一下我的意思。因为我们从3D的角度每天感知事物;我们只能看到3个彼此正交的不同矢量,为您提供我们自然的三维空间,如左和右。对,前进&amp;向后给你水平轴和平面和Up&amp;向下给你垂直轴和平面。我们无法想象任何更高的东西,因此我们无法想象x^4, x^5, x^6 etc...
的顺序,但它们确实存在。如果我们开始查看数学多项式的图形,我们就可以开始看到奇数和偶数函数之间的模式,其中x^4, x^6, x^8
是相似的,它们只不过是x^2
的扩展和{x^5, x^7 & x^9
的函数。 1}}只不过是x^3
的扩展。所以我认为前几个维度是正常的:零 - 点,第一 - 线性,第二 - 区域和第三 - 体积,对于第四维和更高维,我称它们全部为体积。
因此,如果您看到我使用Volume,那么它直接与第三维度相关,如果我参考Volumetric,它与任何高于第3的Dimension相关。现在让我们考虑一个矩阵,这样你就可以在常规代数中看到公共矩阵由MxN
定义。这是一个具有M * N
个元素的二维平面矩阵,该矩阵的面积也为M * N
。让我们扩展到更高维度矩阵,例如MxNxO
这是一个带有M * N * O
元素的3D矩阵,现在有M * N * O
卷。因此,当您想象这时,将MxN
2D部分视为书籍的页面,O
组件表示书籍或盒子的每个页面。这些矩阵的元素可以是任何东西,从简单值到应用操作,到方程式,方程组,集合,或者只是存储容器中的任意对象。所以现在当我们有一个第四阶的矩阵,如MxNxOxP
时,这现在有第四维方面,但最简单的可视化方法是,这将是一个1维数组或向量,其中所有的P
元素将是MxNxO
卷的3D矩阵。当您的矩阵为MxNxOxPxQ
时,您的二维区域矩阵为PxQ
,其中每个元素都是MxNxO
体积矩阵。如果你有一个MxNxOxPxQxR
,你现在又有了一个第六维矩阵,这次你有一个3D体积矩阵,其中每个PxQxR
元素实际上都是MxNxO
的三维矩阵。一旦你越来越高,这种模式会重复并再次融合。因此,任意矩阵如何表现的顺序是这些维度重复:1D是线性向量或矩阵,2D是区域或平面矩阵,3D是体积矩阵,任何更高的东西重复这个过程压缩前一步的卷,因此术语体积矩阵。看一下这张表:
// Order of Magnitude And groupings
-----------------------------------
Linear Area Volume
x^1 x^2 x^3
x^4 x^5 x^6
x^7 x^8 x^9
x^10 x^11 x^12
... ... ...
----------------------------------
现在只需要使用一点微积分来知道哪个数量级可以指向更高维度的哪个维度。一旦你知道一个特定的维度,就可以很容易地采用多个导数给你一个线性表达式;然后遍历空间,然后整合到多个衍生物的相同顺序以给出结果。这应该通过首先忽略高维度的最不重要的较低维度来消除大量的中间工作。如果您正在处理具有12个维度的东西,您可以假设定义第一组体积的前3个维度被紧密包装成另一个3D体积矩阵的元素,然后再次认为体积矩阵的2阶本身就是一个元素另一个3D体积矩阵。因此,我们有一个重复的模式,现在只需要应用它来构建一个算法,一旦你有一个算法;用任何可编程语言实现这些方法应该很容易。因此,您可能必须使用3个案例切换来确定使用哪种算法方法,了解矩阵或nd数组的整体维度,其中一个处理线性顺序,另一个处理区域,最后处理卷,如果它们是第4个+那么整个过程本质上就是递归的。