【问题标题】:Apply memmove function to a 3d array将 memmove 函数应用于 3d 数组
【发布时间】:2015-08-13 15:30:38
【问题描述】:

我正在尝试使用 for 循环在 c++ 中实现 fftshift 函数(来自 MATLAB),这非常耗时。这是我的代码:

const int a = 3;
    const int b = 4;
    const int c = 5;
    int i, j, k;
    int aa = a / 2;
    int bb = b / 2;
    int cc = c / 2;

    double ***te, ***tempa;
    te = new double **[a];
    tempa = new double **[a];
    for (i = 0; i < a; i++)
    {
        te[i] = new double *[b];
        tempa[i] = new double *[b];
        for (j = 0; j < b; j++)
        {
            te[i][j] = new double [c];
            tempa[i][j] = new double [c];
            for (k = 0; k < c; k++)
            {
                te[i][j][k] = i + j+k;
            }

        }
    }
    /*for the row*/
    if (c % 2 == 1)
    {
        for (i = 0; i < a; i++)
        {
            for (j = 0; j < b; j++)
            {
                for (k = 0; k < cc; k++)
                {
                    tempa[i][j][k] = te[i][j][k + cc + 1];
                    tempa[i][j][k + cc] = te[i][j][k];
                    tempa[i][j][c - 1] = te[i][j][cc];
                }
            }
        }
    }
    else
    {
        for (i = 0; i < a; i++)
        {
            for (j = 0; j < b; j++)
            {
                for (k = 0; k < cc; k++)
                {
                    tempa[i][j][k] = te[i][j][k + cc];
                    tempa[i][j][k + cc] = te[i][j][k];
                }
            }
        }
    }

    for (i = 0; i < a; i++)
    {
        for (j = 0; j < b; j++)
        {
            for (k = 0; k < c; k++)
            {
                te[i][j][k] = tempa[i][j][k];
            }
        }
    }

    /*for the column*/
    if (b % 2 == 1)
    {
        for (i = 0; i < a; i++)
        {
            for (j = 0; j < bb; j++)
            {
                for (k = 0; k < c; k++)
                {
                    tempa[i][j][k] = te[i][j + bb + 1][k];
                    tempa[i][j + bb][k] = te[i][j][k];
                    tempa[i][b - 1][k] = te[i][bb][k];
                }
            }
        }
    }
    else
    {
        for (i = 0; i < a; i++)
        {
            for (j = 0; j < bb; j++)
            {
                for (k = 0; k < c; k++)
                {
                    tempa[i][j][k] = te[i][j + bb][k];
                    tempa[i][j + bb][k] = te[i][j][k];
                }
            }
        }
    }

    for (i = 0; i < a; i++)
    {
        for (j = 0; j < b; j++)
        {
            for (k = 0; k < c; k++)
            {
                te[i][j][k] = tempa[i][j][k];
            }
        }
    }

    /*for the third dimension*/
    if (a % 2 == 1)
    {

        for ( i = 0; i < aa; i++)
        {
            for (j = 0; j < b; j++)
            {
                for ( k = 0; k < c; k++)
                {
                    tempa[i][j][k] = te[i + aa + 1][j][k];
                    tempa[i + aa][j][k] = te[i][j][k];
                    tempa[a - 1][j][k] = te[aa][j][k];
                }
            }
        }
    }
    else
    {
        for (i = 0; i < aa; i++)
        {
            for ( j = 0; j < b; j++)
            {
                for ( k = 0; k < c; k++)
                {
                    tempa[i][j][k] = te[i + aa][j][k];
                    tempa[i + aa][j][k] = te[i][j][k];

                }
            }
        }
    }

    for (i = 0; i < a; i++)
    {
        for (j = 0; j < b; j++)
        {
            for (k = 0; k < c; k++)
            {
                cout << te[i][j][k] << ' ';
            }
            cout << endl;
        }
        cout << "\n";
    }
    cout << "and then" << endl;
    for (i = 0; i < a; i++)
    {
        for (j = 0; j < b; j++)
        {
            for (k = 0; k < c; k++)
            {
                cout << tempa[i][j][k] << ' ';
            }
            cout << endl;
        }
        cout << "\n";
    }

现在我想用memmove重写它以提高运行效率。 对于第 3 维,我使用:

    memmove(tempa, te + aa, sizeof(double)*(a - aa));
    memmove(tempa + aa+1, te, sizeof(double)* aa);

此代码适用于 1d 和 2d 数组,但不适用于 3d 数组。另外,我不知道如何使用memmove 移动列和行元素。任何人都可以帮助我解决所有这些问题吗?非常感谢!!

现在我修改了如下代码:

double ***te, ***tempa1,***tempa2, ***tempa3;

    te = new double **[a];
    tempa1 = new double **[a];
    tempa2 = new double **[a];
    tempa3 = new double **[a];
    for (i = 0; i < a; i++)
    {
        te[i] = new double *[b];
        tempa1[i] = new double *[b];
        tempa2[i] = new double *[b];
        tempa3[i] = new double *[b];
        for (j = 0; j < b; j++)
        {
            te[i][j] = new double [c];
            tempa1[i][j] = new double [c];
            tempa2[i][j] = new double [c];
            tempa3[i][j] = new double [c];
            for (k = 0; k < c; k++)
            {
                te[i][j][k] = i + j+k;
            }

        }
    }

    /*for the third dimension*/
    memmove(tempa1, te + (a-aa), sizeof(double**)*aa);
    memmove(tempa1 + aa, te, sizeof(double**)* (a-aa));
    //memmove(te, tempa, sizeof(double)*a);
    /*for the row*/
    for (i = 0; i < a; i++)
    {
        memmove(tempa2[i], tempa1[i] + (b - bb), sizeof(double*)*bb);
        memmove(tempa2[i] + bb, tempa1[i], sizeof(double*)*(b - bb));
    }

    /*for the column*/
    for (j = 0; i < a; i++)
    {
        for (k = 0; j < b; j++)
        {
            memmove(tempa3[i][j], tempa2[i][j] + (c - cc), sizeof(double)*cc);
            memmove(tempa3[i][j] + cc, tempa2[i][j], sizeof(double)*(c-cc));
        }
    }

但问题是我定义了太多新的动态数组,而且 tempa3 的结果也不正确。谁能给点建议?

【问题讨论】:

  • 数组指针并不完全是多维数组。自己制作一个真正的连续多维数组并不难,或者您可以使用库。使用指向指针的数组来完成您想要实现的目标并不是解决问题的有效方法。使用嵌套的std::vector 仍然只是一个指针数组。
  • 你没有在最后一个 for 循环中使用好的变量名:for (j = 0; i &lt; a; i++) 应该是 for (i = 0; i &lt; a; i++)for (k = 0; j &lt; b; j++) 应该是 for (j = 0; j &lt; b; j++)。顺便说一句,你知道你可以在 for 循环语句中声明变量(如for(int i = 0 ; i &lt; 42 ; ++i))对吗?至于“太多的动态数组”,要么重用它们,要么使用我回答中的解决方案。

标签: c++ multidimensional-array memmove


【解决方案1】:

我相信你想要这样的东西:

memmove(tempa, te + (a - aa), sizeof(double**) * aa);
memmove(tempa + aa, te, sizeof(double**) * (a - aa));

memmove(tempa, te + aa, sizeof(double**) * (a - aa));
memmove(tempa + (a - aa), te, sizeof(double**) * aa);

取决于您是否要交换前半部分“向上或向下舍入”(我假设您希望它向上舍入,那么它是第一个版本)。


虽然我不太喜欢你的代码设计:

首先,避免动态分配并尽可能使用std::vectorstd::array。 您可能会争辩说它会阻止您安全地使用memmove 而不是swap 用于第一个维度(嗯,它应该可以工作,但我不是 100% 确定它不是实现定义的)但我不认为那将大大提高效率。 此外,如果你想要一个 N 维数组,我通常更喜欢避免“链接指针”(尽管使用你的算法,你实际上可以使用这种结构,所以它还不错)。
例如,如果您坚持使用new 动态分配数组,则可以使用类似的方法来减少内存使用量(尽管差异可能可以忽略不计;它也可能稍微快一点,但同样可能可以忽略不计):

#include <cstddef>
#include <iostream>

typedef std::size_t index_t;

constexpr index_t width = 3;
constexpr index_t height = 4;
constexpr index_t depth = 5;

// the cells (i, j, k) and (i, j, k+1) are adjacent in memory
// the rows (i, j, _) and (i, j+1, _) are adjacent in memory
// the "slices" (i, _, _) and (i+1, _, _) are adjacent in memory
constexpr index_t cell_index(index_t i, index_t j, index_t k) {
  return (i * height + j) * depth + k;
}

int main() {
  int* array = new int[width * height * depth]();
  for( index_t i = 0 ; i < width ; ++i )
    for( index_t j = 0 ; j < height ; ++j )
      for( index_t k = 0 ; k < depth ; ++k ) {
        // do something on the cell (i, j, k)
        array[cell_index(i, j, k)] = i + j + k;
        std::cout << array[cell_index(i, j, k)] << ' ';
      }
  std::cout << '\n';
  // alternatively you can do this:
  //*
  for( index_t index = 0 ; index < width * height * depth ; ++index) {
    index_t i = index / (height * depth);
    index_t j = (index / depth) % height;
    index_t k = index % depth;
    array[index] = i + j + k;
    std::cout << array[index] << ' ';
  }
  std::cout << '\n';
  //*/
  delete[] array;
}

不同之处在于内存中的组织。在这里,您有一个 60*sizeof(int) 字节(通常为 240 或 480 字节)的大块,而使用您的方法,您将拥有: - 1 个 3*sizeof(int**) 字节块 - 3 个 4*sizeof(int*) 字节的块 - 12 个 5*sizeof(int) 字节的块 (在 64 位架构上增加了 120 个字节,每个单元访问有两个额外的间接,以及用于分配/释放所有内存的更多代码) 当然,你不能再做 array[i][j][k] 了,但仍然......

vectors 也是如此(您可以创建 std::vector&lt;std::vector&lt;std::vector&lt;int&gt;&gt;&gt;std::vector&lt;int&gt;

还有太多的代码重复:您的算法基本上将表的两半交换了 3 次(每个维度一次),但是您将相同的东西重写了 3 次,但有一些不同。

还有太多的内存分配/复制(您的算法有效并且可以通过简单地交换指针来交换整行/切片来利用指针数组的结构,在这种特定情况下,您可以利用这个数据结构来避免你的算法的复制......但你没有)

您应该选择更明确的变量名称,这会有所帮助。例如使用widthheightdepth 而不是abc

例如,这是一个带有向量的实现(虽然我不知道 matlab 的 fftshift 函数,但根据你的代码和这个page,我认为它基本上是“交换角”):

(另外,使用 -std=c++11 编译)

#include <cstddef>
#include <iostream>
#include <vector>
#include <algorithm>

typedef std::size_t index_t;
typedef double element_t;
typedef std::vector<element_t> row_t;
typedef std::vector<row_t> slice_t;
typedef std::vector<slice_t> array_3d_t;

// for one dimension
// you might overload this for a std::vector<double>& and use memmove
// as you originally wanted to do here
template<class T>
void fftshift_dimension(std::vector<T>& row)
{
  using std::swap;
  const index_t size = row.size();
  if(size <= 1)
    return;
  const index_t halved_size = size / 2;
  // swap the two halves
  for(index_t i = 0, j = size - halved_size ; i < halved_size ; ++i, ++j)
    swap(row[i], row[j]);
  // if the size is odd, rotate the right part
  if(size % 2)
  {
    swap(row[halved_size], row[size - 1]);
    const index_t n = size - 2;
    for(index_t i = halved_size ; i < n ; ++i)
      swap(row[i], row[i + 1]);
  }
}

// base case
template<class T>
void fftshift(std::vector<T>& array) {
  fftshift_dimension(array);
}

// reduce the problem for a dimension N+1 to a dimension N
template<class T>
void fftshift(std::vector<std::vector<T>>& array) {
  fftshift_dimension(array);
  for(auto& slice : array)
    fftshift(slice);
}

// overloads operator<< to print a 3-dimensional array
std::ostream& operator<<(std::ostream& output, const array_3d_t& input) {
  const index_t width = input.size();
  for(index_t i = 0; i < width ; i++)
  {
    const index_t height = input[i].size();
    for(index_t j = 0; j < height ; j++)
    {
      const index_t depth = input[i][j].size();
      for(index_t k = 0; k < depth; k++)
        output << input[i][j][k] << ' ';
      output << '\n';
    }
    output << '\n';
  }
  return output;
}

int main()
{
  constexpr index_t width = 3;
  constexpr index_t height = 4;
  constexpr index_t depth = 5;

  array_3d_t input(width, slice_t(height, row_t(depth)));

  // initialization
  for(index_t i = 0 ; i < width ; ++i)
    for(index_t j = 0 ; j < height ; ++j)
      for(index_t k = 0 ; k < depth ; ++k)
        input[i][j][k] = i + j + k;
  std::cout << input;

  // in place fftshift
  fftshift(input);

  std::cout << "and then" << '\n' << input;
}

live example

您可以通过避免多次交换同一个单元格和/或使用 memmove 来制定一个稍微更有效的算法,但我认为它对于许多用途来说已经足够快了(在我的机器上,对于 1000x1000x100 表来说,fftshift 大约需要 130 毫秒) .

【讨论】:

  • 我使用动态分配,因为 a、b 和 c 在我的模拟中不是常量。我也尝试使用 memmove 函数来重写它。我根据你的建议用 memmove 修改了它,但 tempa3 仍然有问题。我只能得到全是 -6.2774e+066 的结果。新代码显示有问题,因为 cmets 没有足够的空间。
  • “我使用动态分配,因为 a、b 和 c 在我的模拟中不是常数。” std::vector 没有问题。至于memmove,你确定你没有在那之前的某个地方造成未定义的行为吗? (至少,你的代码用那个 memmove 对我有用)你所说的“足够的空间”是什么意思?
  • 未定义行为是什么意思?另外,当我回复评论时,有字符限制,因此我将新代码放在问题陈述部分@Canionos
  • @gugabrielle 有时,您编写的代码是合法的(即您可以编译它),但标准并未定义已编译程序的行为。例如,如果您编写一个将整数除以 0 的程序,则会导致未定义的行为:代码将编译但您不知道程序会做什么(它可能会崩溃,它可能会告诉您 1/0= 42等...任何行为都是有效的)更详细的解释,你可以阅读这个blog post;这里也应该有几个问题。
猜你喜欢
  • 2014-04-20
  • 2017-08-20
  • 2020-02-28
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 2019-07-25
  • 2017-11-03
  • 1970-01-01
相关资源
最近更新 更多