【问题标题】:Is there an O(1) way to separate real and imaginary parts of complex array when passed to pgplot?当传递给 pgplot 时,是否有一种 O(1) 方法来分离复数数组的实部和虚部?
【发布时间】:2014-07-18 05:08:23
【问题描述】:

我的代码中有一个库为我生成的复杂浮点数组。考虑它是这样的:

float _Complex data[N];

为了将其视为具有实部和虚部的单独数组,我遍历数组并获取如下值:

float real[N];
float imag[N];
for (int pt=0;pt<N;pt++) {
  real[pt] = creal(data[pt]);
  imag[pt] = cimag(data[pt]));    
}

但这确实是低效的,因为在执行时间和空间方面这是一个O(N) 操作。我想知道是否可以使用一些指针算法来分离数组,从而减少执行时间和内存使用?

我需要分别绘制实值和虚值。我的绘图库 PGPLOT 需要将一组值发送给它,因此我不能“就地”使用复杂的数组。

【问题讨论】:

  • 不,如果你想要 O(1),你将需要就地使用它们。
  • 我不能就地使用它们。编辑了问题。
  • 首先,这真的是C题,还是C++题?你在说什么绘图库?其次,您确定没有办法将数组步幅传递给绘图库吗?也许可以修改您的绘图库以接受一个步幅,那么您根本不必复制数据。
  • 我想下一个问题的教训是:抽象出细节只会在一定程度上有所帮助。在这里,你的问题是你预设了一个问题的解决方案,而没有告诉我们问题是什么。这个问题确实与 PGPLOT 库有很大关系,与复杂性理论没有太大关系:)
  • @KubaOber C++ 没有 _Complex

标签: c arrays time-complexity complex-numbers


【解决方案1】:

pgplot 库提供了一个无跨度的界面来绘制标记数组:

void cpgpt(int n, const float *xpts, const float *ypts, int symbol);

但这只是对cpgpt1 的个别调用的简单包装。因此,添加一个跨步界面很容易:

void cpgpts(int n, int stride, const float *xpts, const float *ypts, int symbol) {
  for (int i = 0; i < n; ++i) {
    cpgpt1(*xpts, *ypts, symbol);
    xpts += stride;
    ypts += stride;
  }
}

当然,您会想为复杂到浮点转换的丑陋编写包装器。例如:

void cpgptsc(int n, const float _Complex *pts, int symbol) {
  cpgpts(n, 2, (const float*)pts, ((const float*)pts)+1, symbol);
}

例子:

// Plot the data as symbols on the Re-Im plane
cpgptsc(count, data, symbol);

您可以类似地重新实现cpgline

void cpglines(int n, int stride, const float *xpts, const float *ypts, int symbol) {
  cpgmove(*xpts, *ypts);
  for (int i = 1; i < n; ++ i) {
    xpts += stride;
    ypts += stride;
    cpgdraw(*xpts, *ypts);
  }
}

void cpglinesc(int n, const float _Complex *pts, int symbol) {
  cpglines(n, 2, (const float*)pts, ((const float*)pts)+1, symbol);
}

例子:

// Plot the data as lines on the Re-Im plane
cpglinesc(count, data);

如果您只绘制单个组件(无论是真实的还是虚构的),为它创建一个合理的包装器同样简单:

void cpglinesx(int n, int stride, float dx, float x0, const float *ypts) {
  cpgmove(x0, *ypts);
  for (int i = 1; i < n; ++ i) {
    x0 += dx;
    ypts += stride;
    cpgdraw(x0, *ypts);
  }
}

void cpglinesxre(int n, float dx, float x0, const float _Complex *pts) {
  cpglinesx(n, 2, dx, x0, (const float*)pts);
}

void cpglinesxim(int n, float dx, float x0, const float _Complex *pts) {
  cpglinesx(n, 2, dx, x0, ((const float*)pts)+1);
}

然后,要绘制从 x=0 开始的虚数分量,增量为 1.0,您可以这样做:

// Plot the imaginary coordinates of all the data
cpglinesxim(count, 1.0, 0.0, data);

【讨论】:

  • 我不能接受你的回答,因为这有点偏离问题的标题。但这真的救了我今天的命,非常感谢。我希望我能做到+100。
  • @VivekVK 当然不是标题,因为您的标题预设了一个解决方案。您正在寻找您甚至不需要找到的东西 :) 祝您的项目好运。几周前,我刚刚参观了 Green Bank 的 NRAO,虽然只是为了欣赏它的景色和它的壮观 :) 射电天文学非常酷(这是指你前面的个人资料照片,我想像某种射电望远镜):)
  • @VivekVK 简单的解决方案...修正你的标题然后接受。
  • @KubaOber 是澳大利亚的 Molonglo 合成望远镜 :)
【解决方案2】:

给定:

float _Complex data[N];

我们知道:

float *ptr = (float *) data;
ptr[2 * n + 0] <- real part.
ptr[2 * n + 1] <- imaginary part.

我们可以看到一些合理化here。基本上,float _Complex 将具有与float[2] 相同的内存布局。

修改它以使所有实数都连续需要与问题中提到的O(n) 版本类似的操作。

【讨论】:

  • 是的,这是有道理的。那么我必须处理延迟。无论如何感谢您的回复..
  • 我不能 100% 确定这是否具有明确定义的行为,但它是最接近解决方案的东西......
  • @KubaOber 我正在使用 PGPLOT 绘制这些,因为这是用于项目的标准库。我不认为它提供了一种跨步的方式,因为它只是一个古老的 fortran 绘图库的包装器。
【解决方案3】:

如果您有一个长度为 N 的输入数组,那么实际上没有一个 O(1) 算法可以遍历所有这些。

【讨论】:

  • 我所期待的是,如果复杂库将其存储为两个数组,则可能有一种方法可以获取指向数组实部和虚部的第一个元素的指针,并在以后访问它.
  • @VivekVK 将复数作为独立的实部和虚部存储在数组中是有效减半 CPU 缓存大小的可靠方法。没有理智的实施会做这样的事情。你的问题预设了一些根本不存在的东西。
  • @VivekVK:没有办法在 O(1) 中获得连续的零件数组
  • C 明确要求实部和虚部与对应的实数浮点类型匹配的表示相邻存储。实现可以忽略此要求的唯一情况是编译器可以看到对象地址的所有使用,并且可以证明程序无法观察到备用存储格式。
猜你喜欢
  • 1970-01-01
  • 2014-08-26
  • 1970-01-01
  • 1970-01-01
  • 2017-04-06
  • 2014-04-15
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
相关资源
最近更新 更多