OpenMP并行化傅里叶变换

设计思想

傅里叶变换,表示能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。在不同的研究领域,傅里叶变换具有多种不同的变体形式,如连续傅里叶变换和离散傅里叶变换。
快速傅里叶变换 (Fast Fourier Transform),即利用计算机计算离散傅里叶变换(DFT)的高效、快速计算方法的统称,简称FFT,于1965年由J.W.库利和T.W.图基提出。
对多项式 f ( x ) = ∑ i = 0 n a i x i , g ( x ) = ∑ i = 0 n b i x i f(x)=∑_{i=0}^na_i x_i ,g(x)=∑_{i=0}^nb_i x_i f(x)=i=0naixi,g(x)=i=0nbixi,定义其乘积fg为 ( f g ) ( x ) = ( ∑ i = 0 n a i x i ) ( ∑ i = 0 n b i x i ) (fg)(x)=(∑_{i=0}^na_i x_i )(∑_{i=0}^nb_i x_i) (fg)(x)=(i=0naixi)(i=0nbixi)
显然我们可以以 O ( n 2 ) O(n^2) O(n2)的复杂度计算这个乘积的每一项的系数。
但FFT可以以 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度来计算这个乘积。
快速傅立叶算法核心的思想是分治,即把一个复杂的问题,分解为一个小的类似问题进行求解。
假定待变换离散时间序列信号长度为 n = 2 m n=2^m n=2m,将X(n)按照奇偶分组:
X ( k ) = ∑ r = 0 N / 2 − 1 x ( 2 r ) W N 2 r k + ∑ r = 0 N / 2 − 1 x ( 2 r + 1 ) W N 2 r + 1 k X(k)=\sum_{r=0}^{N/2-1}x(2r) W_N^2rk+∑_{r=0}^{N/2-1}x(2r+1) W_N^{2r+1}k X(k)=r=0N/21x(2r)WN2rk+r=0N/21x(2r+1)WN2r+1k

上式可变换为:
X ( k ) = ∑ r = 0 N / 2 − 1 x ( 2 r ) W N / 2 r k + W N k ∑ r = 0 N / 2 − 1 x ( 2 r + 1 ) W N / 2 r k X(k)=∑_{r=0}^{N/2-1}x(2r) W_{N/2}^{rk} +W_N^k ∑_{r=0}^{N/2-1}x(2r+1) W_{N/2}^{rk} X(k)=r=0N/21x(2r)WN/2rk+WNkr=0N/21x(2r+1)WN/2rk

A ( k ) = ∑ r = 0 N / 2 − 1 x ( 2 r ) W N / 2 r k A(k)=∑_{r=0}^{N/2-1}x(2r) W_{N/2}^{rk} A(k)=r=0N/21x(2r)WN/2rk
B ( k ) = ∑ r = 0 N / 2 − 1 x ( 2 r + 1 ) W N / 2 r k B(k)=∑_{r=0}^{N/2-1}x(2r+1) W_{N/2}^{rk} B(k)=r=0N/21x(2r+1)WN/2rk
其中, k 取 0 , 1 , … … N / 2 − 1 k取0,1,……N/2-1 k0,1,……N/21,从而
X ( k ) = A ( k ) + W N k B ( k ) X(k)=A(k)+W_N^k B(k) X(k)=A(k)+WNkB(k)
由于 A ( k ) , B ( k ) A(k),B(k) A(k),B(k)都是 N / 2 N/2 N/2点的DFT,X(k)为N点的DFT,这一分治思想还可以进一步做下去。这一方法通常是使用递归实现的,并行优化的难度较高。
为并行化快速傅里叶变换,需要使用非迭代版本,即先预处理每个位置上元素变换后的位置(每个位置分治后的最终位置为其二进制翻转后得到的位置),然后先将所有元素移到变换后的位置之后直接循环合并。
调整完循环顺序之后,第一层循环变量i表示每一层变换的跨度,第二层循环变量j表示每一层变换的第一个起点,第三层循环遍历k则表示实际变换的位置k和k+i。在这里,从第二层开始是没有循环依赖的,即对于不同的j,算法不会对同一块地址进行访问,因为访问的下标k=j(mod i)。
为公平起见,用于对比的串行版本快速傅里叶变换是直接在并行版本上删去编译推导#pragma omp for得到的。这是因为递归版本的快速傅里叶变换通常有较大的函数递归开销。
主函数运行时传入三个参数,第一个参数为.exe文件,第二个参数为并行部分使用的线程数量,第三个参数为傅里叶变换的幂次,因为傅里叶变换算法本身要求长度为2的幂次。
调整线程数与幂次,考虑并行化的加速比。

运行结果

幂次:20

线程数23456
串行时间(s)0.5350.5450.4750.5370.503
并行时间(s)0.3250.2960.2650.2330.241
加速比1.6461.8411.7922.3052.087

结果分析

观察结果,发现对于并行程序,虽然线程数增加,但加速比始终保持在2-3之间,猜想可能是以下原因:OpenMP的parallel region结束时,线程之间需要同步,有隐式路障。最好的OpenMP使用场景是线程之间没有很多需要锁保护的共享访问;parallel region应该尽可能大,以抵消OpenMP多线程带来的额外同步开销。

程序源码

#include <iostream>
#include<vector>
#include<cmath>
#include<complex.h>
#include <omp.h>
using namespace std;
typedef long long ll;
typedef double lf;
#define M_PI 3.14159265358979323846
struct Rader : vector<int>
{Rader(int n) : vector<int>(1 << int(ceil(log2(n)))){for (int i = at(0) = 0; i < size(); ++i)if (at(i) = at(i >> 1) >> 1, i & 1)at(i) += size() >> 1;}
};
struct FFT : Rader
{vector<complex<lf>> w;FFT(int n) : Rader(n), w(size(), polar(1.0, 2 * M_PI / size())){w[0] = 1;for (int i = 1; i < size(); ++i)w[i] *= w[i - 1];}vector<complex<lf>> pfft1(const vector<complex<lf>> &a) const{vector<complex<lf>> x(size());
#pragma omp parallel forfor (int i = 0; i < a.size(); ++i)x[at(i)] = a[i];for (int i = 1; i < size(); i <<= 1)
#pragma omp parallel forfor (int j = 0; j < i; ++j)for (int k = j; k < size(); k += i << 1){complex<lf> t = w[size() / (i << 1) * j] * x[k + i];x[k + i] = x[k] - t, x[k] += t;}return x;}vector<complex<lf>> pfft2(const vector<complex<lf>> &a) const{vector<complex<lf>> x(size());
#pragma omp parallel
#pragma omp forfor (int i = 0; i < a.size(); ++i)x[at(i)] = a[i];for (int i = 1; i < size(); i <<= 1)
#pragma omp forfor (int j = 0; j < i; ++j)for (int k = j; k < size(); k += i << 1){complex<lf> t = w[size() / (i << 1) * j] * x[k + i];x[k + i] = x[k] - t, x[k] += t;}return x;}vector<complex<lf>> fft(const vector<complex<lf>> &a) const{vector<complex<lf>> x(size());for (int i = 0; i < a.size(); ++i)x[at(i)] = a[i];for (int i = 1; i < size(); i <<= 1)for (int j = 0; j < i; ++j)for (int k = j; k < size(); k += i << 1){complex<lf> t = w[size() / (i << 1) * j] * x[k + i];x[k + i] = x[k] - t, x[k] += t;}return x;}vector<ll> ask(const vector<ll> &a, const vector<ll> &b) const{vector<complex<lf>> xa(a.begin(), a.end()), xb(b.begin(), b.end());xa = fft(xa), xb = fft(xb);for (int i = 0; i < size(); ++i)xa[i] *= xb[i];vector<ll> ans(size());xa = fft(xa), ans[0] = xa[0].real() / size() + 0.5;for (int i = 1; i < size(); ++i)ans[i] = xa[size() - i].real() / size() + 0.5;return ans;}
};
int main(int argc, char **argv)
{//if (argc < 3)//return cerr << "Error: No Enough parameters (" << argv[0] << " <num-of-threads> <power-of-transform-length>).\n", 0;//omp_set_num_threads(atoi(argv[1]));//FFT fft(1 << atoi(argv[2]));FFT fft(1 << 20);for(int i=2;i<11;i++){omp_set_num_threads(i);cout<<"#"<<i<<endl;vector<complex<lf>> a(fft.begin(), fft.end());double t0 = omp_get_wtime();vector<complex<lf>> b = fft.fft(a);double t1 = omp_get_wtime();cout << "Serial Time: " << t1 - t0 << "s\n";vector<complex<lf>> c = fft.pfft1(a);double t2 = omp_get_wtime();cout << "Parallel Time1: " << t2 - t1 << "s\n";vector<complex<lf>> d = fft.pfft2(a);double t3 = omp_get_wtime();cout << "Parallel Time2: " << t3 - t2<< "s\n";if (b != c||c!=d)cerr << "Error: Parallel result are not equivalent to Serial result.\n";}
}

参考资料

快速傅里叶变换(FFT)超详解
手把手教快速傅立叶变换FFT算法
OpenMP实现并行快速傅里叶变换

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows各种文件操作、系统相关的命令行脚本

目录 写在前面文件操作命令说明遍历文件并写进txt按行读取txt并文件计数文件重命名 参考完 写在前面 1、本文内容 windows各种文件操作、系统相关的命令行脚本 请保存为.bat运行 2、平台 windows10 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/…

《零基础入门学习Python》第047讲:魔法方法:定制序列

0. 请写下这一节课你学习到的内容&#xff1a;格式不限&#xff0c;回忆并复述是加强记忆的好方式&#xff01; 常言道&#xff1a;“无规矩不成方圆”&#xff0c;讲的是万事万物的发展都要在一定的规则下去运行&#xff0c;只有遵循一定的协议去做&#xff0c;事情才能够按照…

如何将一个目录下的所有md文件导出成pdf

要将一个目录下的所有Markdown&#xff08;.md&#xff09;文件导出为PDF&#xff0c;您可以使用Node.js进行编程来实现。以下是一种可能的方法&#xff1a; 首先&#xff0c;您需要设置Node.js环境并安装依赖项。在命令行中导航到您的项目目录&#xff0c;并运行以下命令&…

前端js防抖

一、原生js防抖 <!DOCTYPE html> <html> <head><title>防抖按钮示例</title> </head> <body><button id"immediateButton">立即触发</button><button id"waitButton">等候触发</button&g…

Stream实现List和Map互转总结

本文来说下Stream实现List和Map互转总结 文章目录 实体类Map转List代码List转Map代码 实体类 本篇介绍Stream流List和Map互转&#xff0c;同时在转换过程中遇到的问题分析。 package cn.wideth.collect;import lombok.AllArgsConstructor; import lombok.Data; import lombok.N…

GAMES101作业2

文章目录 作业内容Step 1. 创建三角形的2维bounding boxStep 2. 判断bBox中的像素中心点是否在三角形内Step 3. 比较插值深度和Depth BufferMSAA 作业内容 在屏幕上画出一个实心三角形&#xff0c; 换言之&#xff0c;栅格化一个三角形。上一次作业中&#xff0c;在视口变化之…

二次元少女-InsCode Stable Diffusion 美图活动一期

一、 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置&#xff1a; 模型版本&#xff1a;chilloutmix_NiPrunedFp32Fix.safetensors 采样方法(Sampler)Sampling method&#xff1a;DPM SDE …

2023年经典【自动化面试题】附答案

一、请描述一下自动化测试流程&#xff1f; 自动化测试流程一般可以分为以下七步&#xff1a; 编写自动化测试计划&#xff1b; 设计自动化测试用例&#xff1b; 编写自动化测试框架和脚本&#xff1b; 调试并维护脚本&#xff1b; 无人值守测试&#xff1b; 后期脚本维…

C#如何控制IIS动态添加删除网站详解

目的是在Winform程序里面&#xff0c;可以直接启动一个HTTP服务端&#xff0c;给下游客户连接使用。 查找相关技术&#xff0c;有两种方法&#xff1a; 1.使用C#动态添加网站应用到IIS中&#xff0c;借用IIS的站群软件管理能力来提供HTTP接口。本文即对此做说明 2.在Winform…

【UE4 塔防游戏系列】07-子弹对敌人造成伤害

目录 效果 步骤 一、让子弹拥有不同伤害 二、敌人拥有不同血量 三、修改“BP_TowerBase”逻辑 四、发射的子弹对敌人造成伤害 效果 步骤 一、让子弹拥有不同伤害 为了让每一种子弹拥有不同的伤害值&#xff0c;打开“TotalBulletsCategory”&#xff08;所有子弹的父类…

【Spring Boot】Web开发 — Web开发简介

Web开发简介 首先介绍Spring Boot 提供的Web组件spring-boot-starter-web&#xff0c;然后介绍Controller和RestController注解&#xff0c;以及控制数据返回的ResponseBody注解&#xff0c;最后介绍Web配置&#xff0c;以便让读者对使用Spring Boot开发Web系统有初步的了解。…

linux下一个iic驱动(按键+点灯)-互斥

一、前提&#xff1a; 硬件部分&#xff1a; 1. rk3399开发板&#xff0c;其中的某一路iic&#xff0c;这个作为总线的主控制器 2. gd32单片机&#xff0c;其中的某一路iic&#xff0c;从设备。主要是按键上报和灯的亮灭控制。&#xff08;按键大约30个&#xff0c;灯在键的…

送呆萌的她一个皮卡丘(Python实现)

目录 1 呆萌的她 2 思维需要革新 3 送她的一个漂亮皮卡丘 4 Python完整代码奉上 1 呆萌的她 又是一季春风暖阳下, 你是一湾一湾羞涩的春波。 静静感受着&#xff0c; 你垂下的枝膊 在我的脸上轻轻抚摸 一对春燕,低低掠过 涟漪乍起&#xff0c;是你浅浅的笑窝...... 2 思…

(五)「消息队列」之 RabbitMQ 主题(使用 .NET 客户端)

0、引言 先决条件 本教程假设 RabbitMQ 已安装并且正在 本地主机 的标准端口&#xff08;5672&#xff09;上运行。如果您使用了不同的主机、端口或凭证&#xff0c;则要求调整连接设置。 获取帮助 如果您在阅读本教程时遇到问题&#xff0c;可以通过邮件列表或者 RabbitMQ 社区…

56 # 实现 pipe 方法进行拷贝

pipe 是异步的&#xff0c;可以实现读一点写一点&#xff0c;管道的优势&#xff1a;不会淹没可用内存&#xff0c;但是在导入的过程中无法获取到内容 const fs require("fs"); const path require("path");fs.createReadStream(path.resolve(__dirname…

前端 | (七)浮动 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;浮动介绍&#x1f407;元素浮动后的特点&#x1f407;浮动小练习&#x1f525;盒子1右浮动&#x1f525;盒子1左浮动&#x1f525;所有盒子都浮动&#x1f5…

Python 闭包 装饰器

闭包定义&#xff1a;在函数嵌套的前提下&#xff0c;内部函数使用了外部函数的变量&#xff0c;并且外部函数返回了内部函数&#xff0c;我们把这个使用外部函数变量的内部函数称为闭包. 下面实现一个在执行方法的前后打印日志的场景 第一种方法装饰器 1.定义外层函数(要求…

vscode 添加 ros头文件

解决 vscode不能支持ROS相关头文件和没有智能提示问题 vscode 编写pakage源文件代码,#include<ros/ros.h>等头文件时报错,无法运行智能提示 1.vscode中CTRL+P 2.键入ext install ms-iot.vscode-ros 按回车,等待下载ros插件 3.修改c_cpp_properties.json文件 鼠标…

数学建模 插值算法

有问题 牛顿差值也有问题它们都有龙格现象&#xff0c;一般用分段插值。 插值预测要比灰色关联预测更加准确&#xff0c;灰色预测只有2次 拟合样本点要非常多&#xff0c;样本点少差值合适

Spring底层

配置文件 配置优先级 之前讲解过&#xff0c;可以用这三种方式进行配置 那如果这三种都进行了配置&#xff0c;那到底哪一份生效呢&#xff1f; 结论 优先级从大到小 properties>yml>yaml然后就是现在一般都用yml文件进行配置 其他配置方式 除了配置文件外 还有不同…