北邮22级信通院DSP:实验三(1):FFT变换、IFFT变换(附每步8点变换蝶形图)保姆级讲解+用C++程序实现复数域的FFT变换和IFFT变换+C++中的chrono头文件讲解

北邮22信通一枚~

跟随课程进度更新北邮信通院DSP的笔记、代码和文章,欢迎关注~

获取更多文章,请访问专栏:

北邮22级信通院DSP_青山入墨雨如画的博客-CSDN博客

目录

一、预备知识

1.1 FFT算法

1.2.1由DFT到FFT

1.2.2 基2时域抽选算法

第一步:

第二步:

第三步:

第四步:

1.2 IFFT算法

1.3快速傅里叶算法拓展

1.4 C++中的complex类型

1.5 C++中的cmath库

1.6 C++中处理程序运行时间的chrono头文件

1.6.1时钟类(Clocks):

1.6.2时间间隔(Durations):

1.6.3时间点(Time points):

1.6.4时间相关函数:

1.7 C++中的functional头文件与function方法       

二、程序设计思路

 2.1 FFT算法的实现

2.2 IFFT算法的实现

2.3FFT算法与IFFT算法的合并

2.4程序运行时间的函数

2.5占用内存空间的函数

三、代码部分

3.1代码部分

3.2运行效果 


一、预备知识

1.1 FFT算法

1.2.1由DFT到FFT

 FFT算法是DFT算法的优化算法,相比于DFT算法,FFT算法“对数式”地减少了运算复杂度,提高了程序运行的效率,节约了内存空间。

FFT简化DFT运算的本质是W因子的性质。

以4点DFT为例:

根据性质,可以化为:

整理可得:

所以:

可以看出,原本需要4^2=16次复数乘的DFT运算,被简化成了只需要进行一步复数乘的FFT运算,运算复杂度大大降低。 

1.2.2 基2时域抽选算法

第一步:

所以:

 以N=8点的FFT为例,所以第一步的蝶形图为:

 以下均以N=8点FFT为例。

第二步:

对x1(n)再奇偶分离得: 

可以发现,X1(k)和X2(k)也可以用X3(k)和X4(k)的线性组合表示。

复数乘法也仅仅为1次。 

 以N=8点的FFT为例,所以第二步的蝶形图为:

第三步:

由于N=2^m,所以可以一直二分分解下去。

直到最后分解为2点序列的FFT运算。

 以N=8点的FFT为例,所以第三步的蝶形图为:

第四步:

完整蝶形图 

1.2 IFFT算法

 IFFT算法是FFT算法的逆过程。

可以发现:

 所以对于N=8的IFFT:

可以看出,迭代的每一步相较于FFT,都多乘了一个1/2。 

N=8点的IFFT蝶形图如下:

1.3快速傅里叶算法拓展

关于基于时间/频率抽选的基2^N的FFT和IFFT算法,感兴趣的同学可以参考这篇博客:

Josh 的复习总结之数字信号处理(Part 5——部分 FFT 蝶形图)_fft蝶形图-CSDN博客

1.4 C++中的complex类型

         C++函数提供了专门用于复数计算的头文件#include<complex>。

        这里的complex需要声明是什么数据类型的complex,比如定义一个double类型的complex变量,我们应该写:

complex<double>complex_1={-1,2};

complex库中为我们提供了很多函数,比如取实部和取虚部运算,加减乘除的基本运算。

1.5 C++中的cmath库

         一些数学处理函数,比如三角函数和反三角函数都包含在cmath库中,加头文件#include<cmath>来导入这个库。

1.6 C++中处理程序运行时间的chrono头文件

  <chrono> 头文件中包含了一些主要的类和函数,而不是函数。这些类和函数用于处理时间相关的操作。以下是一些主要的类和函数:

1.6.1时钟类(Clocks):

  1. std::chrono::system_clock:提供了当前系统时间的功能。
  2. std::chrono::steady_clock:提供了一个稳定的时钟,用于测量时间间隔。
  3. std::chrono::high_resolution_clock:提供了高精度的时钟,通常用于测量程序的执行时间。

1.6.2时间间隔(Durations):

  1. std::chrono::duration:表示一段时间的长度,可以与时钟一起使用来表示时间间隔。
  2. std::chrono::nanosecondsstd::chrono::microsecondsstd::chrono::millisecondsstd::chrono::secondsstd::chrono::minutesstd::chrono::hours:表示不同单位的时间间隔。

1.6.3时间点(Time points):

  1. std::chrono::time_point:表示时钟上的一个特定时间点。
  2. std::chrono::system_clock::time_pointstd::chrono::steady_clock::time_pointstd::chrono::high_resolution_clock::time_point:表示特定时钟上的时间点。

1.6.4时间相关函数:

  1. std::chrono::duration_cast:用于执行时间间隔的单位转换。
  2. std::chrono::time_point_cast:用于执行时间点的时钟转换。
  3. std::chrono::high_resolution_clock::now()std::chrono::steady_clock::now()std::chrono::system_clock::now():获取当前时间点。

1.7 C++中的functional头文件与function方法       

  <functional> 是 C++ 标准库中的头文件,其中包含了一组模板类和函数,用于支持函数对象(即可被调用的对象,如函数指针、lambda 表达式等)的操作和处理。

        其中,std::function 是一个模板类,可以用来包装各种可调用对象,包括函数指针、函数对象、成员函数指针、lambda 表达式等,从而实现统一的调用接口。

        使用 std::function,可以将不同类型的可调用对象赋值给同一个 std::function 对象,然后通过调用 std::function 对象来间接调用被包装的可调用对象,而无需关心其具体类型这样可以提高代码的灵活性和可维护性。

        例如:

#include <functional>
#include <iostream>void foo(int x) {std::cout << "foo: " << x << std::endl;
}int main() {std::function<void(int)> func = foo; // 将函数指针 foo 赋值给 funcfunc(42); // 调用 func,间接调用了 fooreturn 0;
}

         这段代码中,std::function<void(int)> 声明了一个函数对象 func,其参数为 int 类型,返回类型为 void。然后,将函数指针 foo 赋值给了 func,最后通过 func 来调用函数 foo

二、程序设计思路

 2.1 FFT算法的实现

        以下,均以N=8点FFT为例讲解。

        FFT算法的本质在于递归调用,每次对序列二分之后,对更小的序列继续做FFT变换,直到进行到最底端时候,返回所有现场。

根据以上思路,我们首先对序列做奇偶二分:

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}//…
}

 对每一部分更小的序列,使用FFT算法;

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       // 对偶数项进行递归FFT变换fft(odd, inverse);                        // 对奇数项进行递归FFT变换//…
}

直到进行到两点FFT变换,做该变换并逐层向上溯回,依次返回所有现场;

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       // 对偶数项进行递归FFT变换fft(odd, inverse);                        // 对奇数项进行递归FFT变换// 计算旋转因子的角度double angle = -2 * PI / n; Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果w *= wn; // 更新旋转因子}
}

以N=8的序列为例,第一步将8点序列奇偶二分,

执行到fft(even, inverse);和fft(odd, inverse);

 

分别对两个N=4的子序列进行FFT变换,这里保存现场。

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       //执行到这步,并保存现场。fft(odd, inverse);                        //执行到这步,并保存现场。
}

 对每一个N=4的子序列,进行奇偶二分。

重新执行下面的代码:

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       //执行到这步,并保存现场。fft(odd, inverse);                        //执行到这步,并保存现场。
}

 对每一个N=4的输入序列,都得到两个N=2的子序列。

执行到fft(even, inverse);和fft(odd, inverse);

 

分别对两个N=2的子序列进行FFT变换,这里保存现场。

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       //执行到这步,并保存现场。fft(odd, inverse);                        //执行到这步,并保存现场。
}

执行之后,对每个输入的N=2的新序列,会奇偶二分之后产生两个N=1的子序列。

之后执行这部分代码:

void fft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换
}

 

 

之后开始大规模返回现场。

首先对于N=1的两个序列结果,返回N=2序列保留的现场,执行这些代码:

void fft(vector<Complex>& a) 
{//这些部分已经执行过了,这里就以……形式标注,方便大家理解// 计算旋转因子的角度double angle = -2 * PI / n; Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果w *= wn; // 更新旋转因子}
}

之后对于N=2的两个序列结果,返回N=4序列保留的现场,执行这些代码:

void fft(vector<Complex>& a) 
{//这些部分已经执行过了,这里就以……形式标注,方便大家理解// 计算旋转因子的角度double angle = -2 * PI / n; Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果w *= wn; // 更新旋转因子}
}

之后对于N=4的两个序列结果,返回N=8序列保留的现场,执行这些代码:

void fft(vector<Complex>& a) 
{//这些部分已经执行过了,这里就以……形式标注,方便大家理解// 计算旋转因子的角度double angle = -2 * PI / n; Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果w *= wn; // 更新旋转因子}
}

最终修改了传入的变量vector<Complex>& a;

2.2 IFFT算法的实现

由于IFFT算法相较于FFT算法,只变了两个地方,一个是W因子的幂次由正变负,一个是每次迭代时候应多乘一个1/2的项,原因见上面:1.2 IFFT算法

所以对IFFT的实现, 代码部分与FFT非常类似:

void ifft(vector<Complex>& a) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}ifft(even, inverse);                       // 对偶数项进行递归IFFT变换ifft(odd, inverse);                        // 对奇数项进行递归IFFT变换// 计算旋转因子的角度double angle = 2 * PI / n;                Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果a[i] /= 2;a[i + n / 2] /= 2;w *= wn; // 更新旋转因子}
}

有关递归调用的过程请类比FFT。 

2.3FFT算法与IFFT算法的合并

由于FFT和IFFT的算法基本相同,我们可以将两个算法做一个合并,在传入的形参中新增bool型一个变量,用于判断是否为IFFT变换,如果是的话,就在FFT变换的基础上新增一些操作。

FFT和IFFT合并算法如下:

// FFT算法函数,参数 a 是输入的复数向量,
// inverse 表示是否进行逆变换,默认为 false
void fft(vector<Complex>& a, bool inverse = false) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       // 对偶数项进行递归FFT变换fft(odd, inverse);                        // 对奇数项进行递归FFT变换// 计算旋转因子的角度double angle = (inverse ? 2 : -2) * PI / n;       //false的话是-2,正变换;true的话是2,逆变换Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果if (inverse) {                                             // 如果是逆变换,则需要除以2a[i] /= 2;a[i + n / 2] /= 2;}w *= wn; // 更新旋转因子}
}// IFFT算法函数,参数 a 是输入的复数向量
void ifft(vector<Complex>& a) 
{fft(a, true); // 调用 FFT 函数,inverse 参数设置为 true,进行逆变换
}

2.4程序运行时间的函数

// 计算运行时间
template<typename Func>
void measureTime(Func func, const string& msg) {auto start = chrono::high_resolution_clock::now();func(); // 执行传入的函数auto end = chrono::high_resolution_clock::now();chrono::duration<double> duration = end - start;cout << msg << "运行时间:" << duration.count() * 1000 << " ms" << endl;
}

         需要加头文件#include <chrono>和#include <functional> 

         形参 msg 是用来传递一条描述性的消息,用于在输出中标识这次时间测量的内容。在输出中,这个消息会和执行时间一起显示,使得输出更具可读性,方便理解每次测量所针对的操作。

         auto start = chrono::high_resolution_clock::now();用于获取当前时间点;

        在 <chrono> 头文件中,duration 表示一段时间的长度,以指定的时间单位表示。它是一个模板类,可以根据需要指定不同的时间单位(如秒、毫秒、微秒等)。

        使用方法是首先创建一个 duration 对象,然后可以通过成员函数 count() 获取该持续时间的值,并根据需要转换为所需的时间单位。

        例如,对于 chrono::duration<double>,可以通过 count() 获取持续时间的双精度浮点数值,表示秒数。如果需要将其转换为毫秒,则可以乘以 1000。

2.5占用内存空间的函数

template<typename T>
size_t memoryUsage(const vector<T>& vec) {return vec.size() * sizeof(T);
}

        这段代码计算了一个 vector 对象所占用的内存大小。它通过 vec.size() 获取 vector 中元素的数量,然后乘以 sizeof(T),其中 Tvector 存储的元素类型,表示每个元素所占用的内存大小。

三、代码部分

3.1代码部分

#include<iostream>                                        // 输入输出流头文件
#include <complex>                                        // 包含复数类型的头文件
#include <vector>                                         // 包含向量容器的头文件
#include <cmath>                                          // 包含数学函数的头文件
#include <chrono>                                         // 用于处理程序运行时间的头文件
#include <functional>                                     // 引入 <functional> 头文件用于使用 std::function方法
using namespace std;//用反三角函数定义常数PI
const double PI = acos(-1.0);// 定义复数类型
typedef complex<double> Complex;// FFT算法函数,参数 a 是输入的复数向量,
// inverse 表示是否进行逆变换,默认为 false
void fft(vector<Complex>& a, bool inverse = false) 
{int n = a.size();                         // 获取输入向量的大小if (n <= 1) return;                       // 如果输入向量大小为1或0,则直接返回,无需变换// 分别定义偶数项和奇数项的向量vector<Complex> even(n / 2), odd(n / 2);for (int i = 0; i < n / 2; ++i){even[i] = a[i * 2];                   // 偶数项odd[i] = a[i * 2 + 1];                // 奇数项}fft(even, inverse);                       // 对偶数项进行递归FFT变换fft(odd, inverse);                        // 对奇数项进行递归FFT变换// 计算旋转因子的角度double angle = (inverse ? 2 : -2) * PI / n;       //false的话是-2,正变换;true的话是2,逆变换Complex w(1), wn(cos(angle), sin(angle));         // 初始化旋转因子for (int i = 0; i < n / 2; ++i) {a[i] = even[i] + w * odd[i];                  // 计算上半部分的结果a[i + n / 2] = even[i] - w * odd[i];          // 计算下半部分的结果if (inverse) {                                             // 如果是逆变换,则需要除以2a[i] /= 2;a[i + n / 2] /= 2;}w *= wn; // 更新旋转因子}
}// IFFT算法函数,参数 a 是输入的复数向量
void ifft(vector<Complex>& a) 
{fft(a, true); // 调用 FFT 函数,inverse 参数设置为 true,进行逆变换
}// 计算运行时间
template<typename Func>
void measureTime(Func func, const string& msg) {auto start = chrono::high_resolution_clock::now();func(); // 执行传入的函数auto end = chrono::high_resolution_clock::now();chrono::duration<double> duration = end - start;cout << msg << "运行时间:" << duration.count() * 1000 << " ms" << endl;
}// 计算内存占用
template<typename T>
size_t memoryUsage(const vector<T>& vec) {return vec.size() * sizeof(T);
}int main() 
{system("color 0A");// 输入序列vector<Complex> input = { (1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,1) };// 执行FFT变换fft(input);// 打印FFT变换结果cout << "FFT 变换结果:" << endl << endl;for (const auto& val : input) {cout << val << endl;}cout << endl;// 执行IFFT变换ifft(input);// 打印IFFT变换结果cout << "IFFT 变换结果:" << endl << endl;for (const auto& val : input) {cout << val << endl;}cout << endl;// 计算 IFFT 变换的运行时间measureTime([&input]() {ifft(input);}, "IFFT");// 计算内存占用cout << "内存占用:" << memoryUsage(input) << " bit" << endl;return 0;
}

3.2运行效果 

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

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

相关文章

Spring+Vue的卓越托管中心管理系统的设计与实现+PPT+论文+讲解+售后

相比于以前的传统手工管理方式&#xff0c;智能化的管理方式可以大幅降低运营人员成本&#xff0c;实现了卓越托管中心管理系统的标准化、制度化、程序化的管理&#xff0c;有效地防止了卓越托管中心管理系统的随意管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能…

【LAMMPS学习】八、基础知识(5.11)磁自旋

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

最新版Ceph( Reef版本)块存储简单对接k8s

当前ceph 你的ceph集群上执行 1.创建名为k8s-rbd 的存储池 ceph osd pool create k8s-rbd 64 642.初始化 rbd pool init k8s-rbd3 创建k8s访问块设备的认证用户 ceph auth get-or-create client.kubernetes mon profile rbd osd profile rbd poolk8s-rbd部署 ceph-rbd-csi c…

Nginx配置Https缺少SSL模块

1、Linux下Nginx配置https nginx下载和安装此处就忽略&#xff0c;可自行百度 1.1、配置https 打开nginx配置文件 vim /opt/app/nginx/conf/nginx.conf相关https配置 server {listen 443 ssl; #开放端口server_name echarts.net;#域名#redirect to https#ssl on; #旧版#ssl证…

c#实现音乐的“vip播放功能”

文章目录 前言1. c#窗体2. 功能3. 具体实现3.1 添加文件3.2 音乐播放3.3 其他功能 4. 整体代码和窗口5. 依赖的第三方库 前言 最近在QQ音乐里重温周杰伦的歌&#xff0c;觉得好听到耳朵怀孕&#xff0c;兴起想要下载下来反复听&#xff0c;发现QQ音乐VIP歌曲下载下来的格式居然…

微信小程序 手机号授权登录

手机号授权登录 效果展示 这里面用的是 uni-app 官方的登录 他支持多端发布 https://zh.uniapp.dcloud.io/api/plugins/login.html#loginhttps://zh.uniapp.dcloud.io/api/plugins/login.html#login 下面是代码 <template><!-- 授权按钮 --><button v-if&quo…

1984. 学生分数的最小差值C++

给你一个 下标从 0 开始 的整数数组 nums &#xff0c;其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。 从数组中选出任意 k 名学生的分数&#xff0c;使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。 返回可能的 最小差值 。 示例 1&#xff1a; 输入&…

硬件设计细节1-缓冲驱动器使用注意事项

目录 一、缓冲驱动器二、实例分析1.硬件结构2.问题描述3.原因分析4.原因定位 三、结论 一、缓冲驱动器 缓冲驱动器通常用于隔离、电平转换等应用场景。在使用时&#xff0c;需要关注的点较多&#xff0c;如电平范围、频率范围、延时、控制方式、方向以及输入输出状态。通常&am…

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…

配置好Maven后本地仓库不自动加载以及创建Maven报错的解决方法

先退出到全局模式再点击All Settings 点击maven 修改Maven home path修改了这个local不同步 点击Flie中的Module 然后选择maven 点击next设置需要存放的位置以及组名 然后报错 报错误的原因是因为maven版本太高和与你使用的IDEA版本不兼容 将maven的版本改为3.6并修改环境变量 …

【C++】从零开始认识多态

送给大家一句话&#xff1a; 一个犹豫不决的灵魂&#xff0c;奋起抗击无穷的忧患&#xff0c;而内心又矛盾重重&#xff0c;真实生活就是如此。 ​​​​ – 詹姆斯・乔伊斯 《尤利西斯》 _φ(*&#xffe3;ω&#xffe3;)&#xff89;_φ(*&#xffe3;ω&#xffe3;)&…

ReactFlow的ReactFlow实例事件传参undefined处理状态切换

1.问题 ReactFlow的ReactFlow实例有些事件我们在不同的状态下并不需要&#xff0c;而且有时候传参会出现其它渲染效果&#xff0c;比如只读状态下我们不想要拖拉拽onEdgesChange连线重连或删除的功能。 2.思路 事件名称类型默认值onEdgesChange(changes: EdgeChange[]) >…

21物联1班常用网络扫描

网络扫描 1.网络扫描概述2.网络扫描步骤及分类具体步骤 1.网络扫描概述 网络安全扫描技术是一种基于Internet远程检测目标网络或本地主机安全性脆弱点的技术。通过网络安全扫描&#xff0c;系统管理员能够发现所维护的Web服务器的各种TCP/IP端口的分配、开放的服务、Web服务软件…

Unity 性能优化之UI和模型优化(九)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、选择UI二、UGUI的优化1.Raycast Target2.UI控件的重叠3.TextMeshPro 二、模型优化1.Model选项卡Mesh CompressionRead/Write Enabled设置Optimize Ga…

【JVM】内存结构

内存结构 Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区&#xff0c;其中有一些会随着虚拟机启动而创建&#xff0c;随着虚拟机退出而销毁。另外一些则是与线程一一对应的&#xff0c;这些与线程一一对应的数据区域会随着线程开始和结束而创建和销毁。 线程私有…

21物联1班常用网络命令

常用网络命令 ipconfig&#xff08;配置&#xff09;ping(测试)命令1&#xff1a;ping 172.16.0.12&#xff1a;ping ip -t3&#xff1a;ping ip -l 3000&#xff08;注意每个之间都存在空格&#xff09;4&#xff1a;ping ip -n count netstat&#xff08;网络&#xff09;命令…

技术分享-上海泗博MPI转以太网模块MPI-131实现Node-RED直接访问西门子PLC数据

上海泗博自动化MPI-131是一款用于西门子S7系列PLC&#xff08;包括S7-200、S7-300、S7-400&#xff09;以及西门子数控机床&#xff08;如840D、840DSL等&#xff09;的以太网通讯模块&#xff0c;无需编程&#xff0c;即插即用&#xff0c;支持通过模块上下载PLC程序和数据监控…

【busybox记录】【shell指令】shuf

目录 内容来源&#xff1a; 【GUN】【shuf】指令介绍 【busybox】【shuf】指令介绍 【linux】【shuf】指令介绍 使用示例&#xff1a; 打乱内容 - 默认输出 打乱内容 - 最多输出n行 打乱内容 - 将输出写入文件 打乱内容 - 重复输出 打乱内容 - 打乱本条指令的参数 打…

Django调用MTP服务器给指定邮箱发送邮件

Django调用MTP服务器发送邮箱 邮箱的激活链接含有用户数据不能直接发送需要对其进行加密 发送邮箱是借助SMTP服务器进行中转 一. 配置SMTP服务中的邮箱信息以及激活链接 1. 配置邮箱权限 打开网易邮箱设置点击POP3 开启选项 注 : 在打开的过程中会弹出授权密码一点要保存 …

OpenAI泄密者加入马斯克xAI,技术版图扩张;OpenAI推出可识别DALL·E 3图像的AI检测工具

&#x1f989; AI新闻 &#x1f680; OpenAI泄密者加入马斯克xAI&#xff0c;技术版图扩张 摘要&#xff1a;最近&#xff0c;曾在OpenAI任职并被指控泄露机密的Pavel Izmailov迅速加入了马斯克旗下的xAI团队&#xff0c;成为研究员。在加入之前&#xff0c;Izmailov因涉嫌泄…