精华内容
下载资源
问答
  • 1) (2,2) (3,3) (1,1) (2,2) (3,3) 和Matlab的一样: testTest = [ complex(1,1) complex(2,2) complex(3,3); complex(1,1) complex(2,2) complex(3,3); complex(1,1) complex(2,2) complex(3,3)]; testTest test...

    有人可以向我解释为什么结果会有所不同.

    C中的代码:

    MatrixXcd testTest;

    testTest.resize(3,3);

    testTest.real()(0,0) = 1;

    testTest.real()(0,1) = 2;

    testTest.real()(0,2) = 3;

    testTest.real()(1,0) = 1;

    testTest.real()(1,1) = 2;

    testTest.real()(1,2) = 3;

    testTest.real()(2,0) = 1;

    testTest.real()(2,1) = 2;

    testTest.real()(2,2) = 3;

    testTest.imag()(0,0) = 1;

    testTest.imag()(0,1) = 2;

    testTest.imag()(0,2) = 3;

    testTest.imag()(1,0) = 1;

    testTest.imag()(1,1) = 2;

    testTest.imag()(1,2) = 3;

    testTest.imag()(2,0) = 1;

    testTest.imag()(2,1) = 2;

    testTest.imag()(2,2) = 3;

    cout<< endl << testTest << endl;

    cout<< endl << testTest.transpose() << endl;

    cout<< endl << testTest*testTest.transpose() << endl;

    cout<< endl << testTest << endl;

    C的结果:

    (1,1) (2,2) (3,3)

    (1,1) (2,2) (3,3)

    (1,1) (2,2) (3,3)

    (1,1) (1,1) (1,1)

    (2,2) (2,2) (2,2)

    (3,3) (3,3) (3,3)

    (0,28) (0,28) (0,28)

    (0,28) (0,28) (0,28)

    (0,28) (0,28) (0,28)

    (1,1) (2,2) (3,3)

    (1,1) (2,2) (3,3)

    (1,1) (2,2) (3,3)

    和Matlab写的一样:

    testTest = [ complex(1,1) complex(2,2) complex(3,3);

    complex(1,1) complex(2,2) complex(3,3);

    complex(1,1) complex(2,2) complex(3,3)];

    testTest

    testTest'

    testTest*testTest'

    testTest

    Matlab结果:

    testTest =

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    ans =

    1.0000 - 1.0000i 1.0000 - 1.0000i 1.0000 - 1.0000i

    2.0000 - 2.0000i 2.0000 - 2.0000i 2.0000 - 2.0000i

    3.0000 - 3.0000i 3.0000 - 3.0000i 3.0000 - 3.0000i

    ans =

    28 28 28

    28 28 28

    28 28 28

    testTest =

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    1.0000 + 1.0000i 2.0000 + 2.0000i 3.0000 + 3.0000i

    C返回中testTest * testTest’的乘法返回带有实部0和成像部分28的复数.Matlab只返回值为28的dobule.

    解决方法:

    因此,如果您将MATLAB测试更改为

    testTest*testTest.'

    结果应该是一样的.

    如果你想在eigen中进行复数转置,那么你可以去matrix.adjoint()(或matrix.conjugate().transpose())

    标签:c,matlab,eigen

    来源: https://codeday.me/bug/20190824/1710057.html

    展开全文
  • 一、前言 最近在企业实习,花了三个星期的时间肝了一个室内定位算法(雏形,还未优化),实现matlab和C的联、联调,其难点...谨以此连载文档记录一下算法开发的关键--复数矩阵运算。 网上关于C语言实现矩阵运算的de

    一、前言

       本连载文章主要内容是实现复数矩阵的各种运算,并利用matlab进行联写、联调,验证C语言编写的各个矩阵运算函数的正确性。其难点笔者认为在于矩阵的各种运算,C++中有Eigen库可用,以前在学slam和做课题时候在Ubuntu下用过一段时间的Eigen,功能强大,提供了各种典型的matrix计算接口,但是在C中,需要我们自己编写每个功能模块。谨以此连载文档记录一下算法开发的关键--复数矩阵运算的实现,纯粹为了学习交流。
       网上关于C语言实现矩阵运算的demo较多,但是大体都不够完整和准确,很多function直接git下来各种问题,且用起来没有统一的标准,另外,只在实数域(Double)内讨论,但是我们知道在算法领域,很多矩阵运算都涉及到复数(Complex)运算,复数域内矩阵运算其实与实数域还是有很大区别的(PS:这也是我在写C程序时候遇到的坑,之后本文会一一介绍)。
       因此,在本连载文章中,我将完全使用C语言完成对矩阵的定义、内存初始化、访问索引、内存销毁的基本功能,更重要的是编写求取代数余子式、行列式、逆矩阵、协方差矩阵、特征值、特征向量等功能函数。所有函数demo我都单独拿出来仔细讲解,并结合matlab,编程对比验证demo正确性,大家可以放心阅读和使用。
    

    二、知识储备和编程工具

       1. 知识储备:熟练C语言,精通指针、数组等C语言“灵魂”知识;
       2. IDE工具:Visual Studio2019Community; MATLAB2019a;
       3. C相关头文件:complex库
    

    三、矩阵的定义

    注意到社区内有小伙伴用二维数组来定义一个矩阵,但是矩阵并不总是二维的,在matlab里面,矩阵可以是N维的,若是试图用更高维度的数组去完成定义,在后续的访问矩阵/数组元胞时候显得非常冗余。虽说C是结构化程序设计语言,但一定的“鲁棒性”也是需要的。另外我是用C++和matlab居多,所以总是或多或少在编程时候有一定的C++的思想。后者面向对象,具有类模板和模板函数。
    PS:在之后贴出来的code中,我都会分别给出Double和Complex两份demo,以及调用到的complex库文件的相关函数接口说明。

    1.头文件

    	 # include <complex.h>  // Complex Numbers
    	 # include <stdlib.h>   // malloc/free, et.al
    

    说明
    关于复数头文件,C11开始提供了大量函数接口,这里我只对用到的单独拿出来讲一下。有兴趣的可以点开源文件看看都有什么内容。首先是定义,源文件见下:

    	#ifndef _C_COMPLEX_T
        	#define _C_COMPLEX_T
        	typedef struct _C_double_complex
        	{
           	 double _Val[2];
        	} _C_double_complex;  // This is what I need
    
       	 typedef struct _C_float_complex
        	{
            	float _Val[2];
        	} _C_float_complex;
    
        	typedef struct _C_ldouble_complex
        	{
            	long double _Val[2];
        	} _C_ldouble_complex;
    	#endif
    
    	typedef _C_double_complex  _Dcomplex;  // This is what I need
    	typedef _C_float_complex   _Fcomplex;
    	typedef _C_ldouble_complex _Lcomplex;
    

    简而言之,complex.h头文件通过定义一个结构体来表示一个复数。具体说来,结构体内部成员是一个一维数组,存储两个数据,由该数组来储存复数的实部(real)和虚部(imaginary),根据实/虚部浮点精度不同定义了三种数组,分别是double、float、long double,对应定义了三种结构体类型,再对应每个function都定义了三个函数。谨记,这三种常规类型指的是复数的实部和虚部的数据类型。算法中,我用到的是double类型。所以以下涉及到复数运算的我都只调用了complex.h头文件中跟double类型相关的函数。

    2.定义

    这里我用一个结构体来完成对矩阵的定义和“模拟”。

    	typedef _Dcomplex ComplexType;   // Complex Variables
    	typedef double DoubleType;       // Double Variables
    
    	/* Complex Cell */
    	typedef struct   // Matrix Structor
    	{
    		int row, column;  // Size
    		ComplexType* arrayComplex;     // Pointer to Cell
    	}Matrix;
    
    	/* Double Cell */
    	typedef struct
    	{
    		int row, column;
    		DoubleType* arrayDouble;
    	}Matrix2Double;
    	/* bool */
    	typedef enum 
    	{ 
    		False = 0, True = 1 
    	}Bool;
    

    说明
    (1) 虽然complex.h头文件内已经对复数类型_C_double_complex取了别名_Dcomplex,为了区别double和complex,我对二者再次取了一个别名,遵守Google命名规则,随后的定义矩阵的两个结构体类型和bool变量的枚举类型同理;
    (2) 重点来了,两种不同元素类型的矩阵,我定义了两个结构体,一个是元胞是实数类型的,一个是复数类型的。结构体内部两个int类型的member代表的是矩阵的行数(row)和列数(column),指针member用于指向存储元素/元胞(cell)的一段内存,因此矩阵元胞可以用指针来进行访问和遍历,此时内部数据结构指针是1D的,但却可以表示2D及以上的矩阵,从这里可以看到指针代替数组的优越性;
    (3) 最后,笔者还定义了一个枚举类型,后续的用于安全机制的函数有用到;
    (4) 可以看到,两个不同类型的矩阵其结构体其实完全一样,只是内部指针变量类型不一样。

    3.初始化内存分配

    首先是写一个对一个复数进行初始化的函数,因为复数也是一个结构体,当定义一个复数变量时候,最好也对其进行初始化。此外,后续涉及到复数的累加累乘,因此也需要对其赋值和初始化:

    	/* Initiation of Complex Number */
    	void InitComplex(ComplexType* Complex)  // Transfer of Pointer
    	{
    		Complex->_Val[0] = 0.0;
    		Complex->_Val[1] = 0.0;
    
    	}
    

    然后是对矩阵进行初始化:

    	/* Initiation of Complex Matrix */
    	void InitComplexMatrix(Matrix* matrix, int row, int column)  // Transmiss of Pointer
    	{
    		int size = row * column * sizeof(ComplexType);
    		if (size <= 0)
    		{
    			puts("ERROE: An invalid matrix!\n");
    			return;
    		}
    		matrix->arrayComplex = (ComplexType*)malloc(size); 			// initiate pointer
    		if(matrix->arrayComplex)
    		{
    			matrix->row = row;                           			 //  initiate row and size
    			matrix->column = column;
    			for (int index = 0; index < row * column; index++)       //  initiate cell
    			{
    				InitComplex(matrix->arrayComplex + index);  // call InitComplex() function
    			}
    		}
    	}
    	
    
    	/* Initiation of Double Matrix */
    	void InitDoubleMatrix(Matrix2Double* matrix, int row, int column)
    	{
    		int size = row * column * sizeof(DoubleType);
    		if (size <= 0)
    		{
    			puts("ERROE: An invalid matrix!\n");
    			return;
    		}
    		matrix->arrayDouble = (DoubleType*)malloc(size);
    		if (matrix->arrayDouble)
    		{
    			matrix->row = row;
    			matrix->column = column;
    			for (int row_i = 0; row_i < row; ++row_i)
    			{
    				for (int column_j = 0; column_j < column; ++column_j)
    				{
    					matrix->arrayDouble[row_i * matrix->column + column_j] = 0.0;
    				}
    			}
    
    		}	
    	}
    

    说明
    (1) 矩阵初始化涉及到确定矩阵row/column的大小、初始化元胞,也就是对结构体变量进行初始化赋值,其核心是对指针动态申请一段内存,采用的是malloc()函数,包含在头文件stdlib.h中;
    (2) 用指针访问/遍历(iteration)矩阵元胞,其实也就是指针指向的每个单元内保存的数,在initiate cell环节,这里其实有三种表示方式,主要是指针访问元素矩阵访问元胞具有不同的方式。后续访问元素/元胞时候我都是随机选一种写的(sorry,我当时写代码时候这些都不是重点,所以想到哪种写哪种),但是为了避免读者阅读混乱,这里,笔者都给出来:

    	// Solution_1
    	for (int index = 0; index < row * column; index++)       //  initiate cell
    	{
    		InitComplex(matrix->arrayComplex + index);
    	}
    	// Solution_2
    	for (int index = 0; index < row * column; index++)
    	{
    		InitComplex(&(matrix->arrayComplex[index]));
    	}
    	// Solution_3
    	for (int row_i = 0; row_i < row; ++row_i)
    	{
    		for (int column_j = 0; column_j < column; ++column_j)
    		{
    			InitComplex(matrix->arrayComplex + row_i * matrix->column + column_j);
    		}
    	}
    

    (3) 另外,为了安全起见,我还加入了两个if语句,可以进一步减少最后compile时出现的各种堆栈溢出等内存bug和exception,比如free掉已经不存在的内存或者malloc失败的内存,也就是C语言使用指针出现的野指针堆错误
    (4) double类型的矩阵类同complex类型,甚至在初始化值时候还要简单一点,因为每个double元胞只有一个元素,而complex有实部和虚部两个。

    4.内存销毁

    我们知道在Linux内,malloc()和free()系统函数总是成对出现,有内存申请必有内存的释放,同样在VS下,利用一个结构体变量定义了一个矩阵之后,调用了malloc()函数申请了一段memory,故而需要释放:

    	/* Validity of Complex Matrix */
    	Bool IsNullComplexMatrix(const Matrix* matrix)
    	{
    		int size = matrix->row * matrix->column;
    
    		if (size <= 0 || matrix->arrayComplex == NULL)
    		{
    			return True;
    		}
    		return False;
    	}	
    	/* Free Memory of Complex Matrix */
    	void DestroyComplexMatrix(Matrix* matrix)
    	{
    		if (!IsNullComplexMatrix(matrix))    // Nested Call of IsNullComplexMatrix()
    		{
    			free(matrix->arrayComplex);      // Pair: malloc--free
    			matrix->arrayComplex = NULL;
    		}
    		matrix->row = matrix->column = 0;
    	}
    	
    	
    	/* Validity of Double Matrix */
    	Bool IsNullDoubleMatrix(const Matrix2Double* matrix)
    	{
    		int size = matrix->row * matrix->column;
    
    		if (size <= 0 || matrix->arrayDouble == NULL)
    		{
    			return True;
    		}
    		return False;
    	}
    	/* Free Memory of Double Matrix */
    	void DestroyDoubleMatrix(Matrix2Double* matrix)
    	{
    		if (!IsNullDoubleMatrix(matrix))
    		{
    			free(matrix->arrayDouble); 
    			matrix->arrayDouble = NULL;
    		}
    		matrix->row = matrix->column = 0;
    	}
    

    说明
    (1) 首先,定义一个IsNullComplexMatrix()函数用于判断矩阵是否初始化成功,主要判断结构体内部的member是否初始化成功(体现在矩阵,就是行列数和内存)。属于保障内存安全的操作。看到没,这里我就调用了前文说到的Bool枚举类型成员;
    (2) 之后,定义一个DestroyComplexMatrix()函数释放结构体内部的member,即嵌套调用free()释放掉指针成员指向的内存,并置为NULL(这步虽然不是必须的但是笔者建议各位读者每次都加上),再将两外两个int类型的member变量置为0;
    (3) 关于double类型,同样给出,不再赘述。
    (4) 补充一点,在我的算法框架中,出现了大量矩阵的定义,也就是多次内存的申请和释放,甚至在几个核心的算法函数内部,出现了二十个以上的矩阵,要是每个矩阵调用一次初始化和销毁函数,会严重影响编程美观和效率。因此,我又写了几个初始化(initiate)和内存销毁(destroy)函数,其目的是一次性只调用一个函数将我需要的所有的矩阵进行内存的申请和释放。当然,这个功能只在我的具体算法场景下用得到,所以我只是做一个补充拓展贴出来,另外,我只给出内存销毁函数,后续我会依次写demo验证一下这个function:

    	/* Free Memory of Complex Matrice Array */
    	void DestroyComplexMatrixArray(Matrix matrixArray[], int num)  // Array Transfer--->Pointer Transfer
    	{
    		if (num)      // if no matrix
    		{
    			for (int i = 0; i < num; i++)
    			{
    				DestroyComplexMatrix(&matrixArray[i]);  // Nested Call of DestroyComplexMatrix()
    			}
    		}
    	}
    
    
    	/* Free Memory of Double Matrice Array */
    	void DestroyDoubleMatrixArray(Matrix2Double* matrixArray, int num)
    	{
    		if (num)  // if no cell
    		{
    			for (int i = 0; i < num; i++)
    			{
    				DestroyDoubleMatrix(&matrixArray[i]);
    			}
    		}
    	}
    

    5.获取矩阵行、列、元胞数目

    这里我也定义了三个函数用于返回相应的值:

    /* Return Matrix Row Size */
    int MatrixRow(const Matrix* matrix)
    {
    	return matrix->row;
    }
    /* Return Matrix Column Size */
    int MatrixColumn(const Matrix* matrix)
    {
    	return matrix->column;
    }
    /* Return Complex Matrix Size */
    int MatrixSize(const Matrix* matrix)
    {
    	return MatrixRow(matrix) * MatrixColumn(matrix) ;   // Size Refers to Numbers of Cell of Matrix
    }
    

    说明
    (1) 由于这里的矩阵笔者用的结构体来~~“模拟”~~ 实现,我们为了让矩阵看起来“更像”我们数学中那样的“格式”,数学美学要用计算机实现,还是定义了相关函数来返回我们要的量,但是事实上,可以直接采用如下格式直接获取

    /** pointer     &   variable**/
    matrix->row; // matrix.row
    matrix->column; // matrix.column
    matrix->row * matrix->column; // matrix.row * matrix.column
    

    四、总结

    这是第一章节的内容,整体上来讲,只是为了完成矩阵的定义、初始化、内存销毁等,但是非常重要,因为后续的所有矩阵运算函数和接口都基于此,事实上,我遇到的很多问题都是出现在这一部分。为后续方便读者阅读,或者帮助读者编程时候排雷,笔者对个人编程习惯和这部分遇到的坑做个总结:

    1. 尽量写void类型的函数,避免在函数内部定义local variable,也就是局部矩阵,频繁调用初始化和销毁函数,造成计算机内存频繁的申请和释放;
    2. 函数形参尽量定义为指针类型,尤其是矩阵/结构体变量,因为是void函数,没有return返回值,因此用传地址方式而不能采用传值操作,避免出错;
    3. 调用函数时候,实参尽量定义为一般变量,尤其是矩阵/结构体变量,这样可以避免结构体指针变量本身就需要分配和释放内存,关于这一点,由于笔者使用C语言编程经历真心不多,所以当时把实参也清一色定义为指针,造成后续在每个函数里面出现了大量结构体指针初始化、结构体内部指针初始化的操作,矩阵多了之后就很混乱,如果分配和释放不及时,会造成编译时候cast各种exception,这也是我当时遇到过的第一个大坑;
    4. 因为const在C++里面意义非常丰富,在C里面,我也习惯性只要是在函数内部不改变传入的实参值的,都在定义函数时为形参加上一个const。这样可以避免在复杂的函数嵌套调用实参和形参传递矩阵运算过程中出现对本不想修改的变量进行修改的情况,造成结果不可预测。事实上,只要是不会修改的实参,我们在定义形参的时候都应为其加上一个const;
    5. 在定义函数内部,只要是传入了一个实参矩阵,我都对其进行是否为空的判断(调用一个IsNullComplexMatrix()函数),再进行下面的功能实现。事实上,编程时候,都应该在code内部加上一些安全性的操作逻辑;
    6. 下一章我将详细给出实数、复数域上矩阵运算的函数,敬请期待…
      传送门:(二)
    展开全文
  • 笔记源自:清华大学公开课:线性代数2——第12讲:复数与复矩阵 目录 目录 引言 复矩阵 Hermitian矩阵 厄米特Hermite矩阵 酉unitary矩阵 复正规阵 离散傅里叶变换DFT 快速傅里叶变换FFT 引言 ...

    此博客停止更新迁移至SnailDove’s Blog查看本文点击此处

    笔记源自:清华大学公开课:线性代数2——第12讲:复数与复矩阵

    目录

    引言

    之前接触的大部分线性代数知识都只考虑实数情形,但复数情形不可避免会遇到。例如 (cosθsinθsinθcosθ) ( c o s θ − s i n θ s i n θ c o s θ ) 没有实特征值(除了极特殊情形),目的:比较实数和复数情形的异同,注意学习复数和实数的区别联系

    复数复习:

      • i2=1 i 2 = − 1 , 一个复数 a+bi=z a + b i = z a a 实部(real part)b虚部(imaginary part),可以把实部 a a 看成x轴分量,虚部b看成y轴分量。复数的共轭(complex conjugate) z=a+biz¯=abi z = a + b i → z ¯ = a − b i 长度 |z|=a2+b2=(abi)(a+bi)=zz¯ | z | = a 2 + b 2 = ( a − b i ) ( a + b i ) = z z ¯ z z 的长度不能定义为(a+bi)2,长度必须是正值,如果把复数 z z 看成一个2维向量,那么它的长度显然就是定义中给出的), 矩阵的共轭定义为: A=(aij)n×n,aijCA¯=(aij¯)n×n性质 AB¯¯¯¯¯¯¯¯=A¯B¯ zz¯=|z|2 A B ¯ = A ¯ B ¯   z z ¯ = | z | 2
      • {长度为1(单位圆上)的复数} {二阶旋转矩阵},且保持乘法。 z=cosθ+isinθA2=(cosθsinθsinθcosθ) z = c o s θ + i s i n θ → A 2 = ( c o s θ − s i n θ s i n θ c o s θ ) 。验证性质: z1=eiθ1,z2=eiθ2Az1=(cosθsinθsinθcosθ),Az2=(cosθsinθsinθcosθ)z1z2=ei(θ1+θ2)=(cos(θ1+θ2)sin(θ1+θ2)sin(θ1+θ2)cos(θ1+θ2))=Az1z2 z 1 = e i θ 1 , z 2 = e i θ 2 → A z 1 = ( c o s θ − s i n θ s i n θ c o s θ ) , A z 2 = ( c o s θ − s i n θ s i n θ c o s θ ) → z 1 z 2 = e i ( θ 1 + θ 2 ) = ( c o s ( θ 1 + θ 2 ) − s i n ( θ 1 + θ 2 ) s i n ( θ 1 + θ 2 ) c o s ( θ 1 + θ 2 ) ) = A z 1 z 2
      • 欧拉公式(Euler formula) eiθ=cosθ+isinθ e i θ = c o s θ + i s i n θ 极分解(polar decomposition) z=reiθ=r(cosθ+isinθ)zn=rneinθ=rn(cos(nθ)+isin(nθ)) z = r e i θ = r ( c o s θ + i s i n θ ) → z n = r n e i n θ = r n ( c o s ( n θ ) + i s i n ( n θ ) ) ,这里z的公式中三角函数部分长度为1,所以r即z的长度,这样任何一个复数都可以用 reiθ r e i θ 表示。
      • 单位根 xn=1 x n = 1 有n个复根 e2kπin,k=0,1,2,,n1 e 2 k π i n , k = 0 , 1 , 2 , … , n − 1 ,令 ω=e2πin1+ω+ω2++ωn1=0 ω = e 2 π i n → 1 + ω + ω 2 + ⋯ + ω n − 1 = 0 ,例如:求 (1+i)81+i=2eiπ4,(1+i)8=(2)8ei2π=16 ( 1 + i ) 8 ← 1 + i = 2 e i π 4 , ( 1 + i ) 8 = ( 2 ) 8 e i 2 π = 16
      • 代数基本定理: anxn++a1x+a0=0,aiC a n x n + ⋯ + a 1 x + a 0 = 0 , a i ∈ C 有n个复数根(可能重复),设 aiR,anxn++a1x+a0=0 a i ∈ R , a n x n + ⋯ + a 1 x + a 0 = 0 的非实数的复根也是成对出现,即若 z=a+bi(b0) z = a + b i ( b ≠ 0 ) 是它的根,则 z¯=abi z ¯ = a − b i 也是它的根,复数根是成对出现的。 奇次实系数方程总有一个实根。(注:公开课字幕内容如下:因为我们知道复根是成对出现的,所以对一个实系数方程,它的复根实际上是2的倍数,因为它是成对出现的,但是奇数次实系数呢,所以它必然除了复根应该有一个实根,不然的话它只有偶数的根,这样就跟它奇数次矛盾)。
      • 实系数多项式(次数 1 ≥ 1 )的 f(x) f ( x ) 可分解成 f(x)=a(xλ1)n1(xλs)ns(x2b1x+c)e1(x2btx+c)et f ( x ) = a ( x − λ 1 ) n 1 ⋯ ( x − λ s ) n s ( x 2 − b 1 x + c ) e 1 ⋯ ( x 2 − b t x + c ) e t λi λ i 即实数根,后 t t 项即复数根给出来的,后面这种形式无法写成实根的一次形式,也就是它的判别式小于0(有复数根),不能写成前s项的形式。例如: xm1=k=0m1(xωk),ωk=ei2kπm x m − 1 = ∏ k = 0 m − 1 ( x − ω k ) , ω k = e i 2 k π m ωmk=ei2(mk)πm=ei2π(1km)=cos(2π(1km))+isin(2π(1km))=cos(2kπm)isin(2kπm)=ωk¯¯¯¯¯,km<1(xωk)(xωmk)=x2(ωk+ωmk)x+(ωkωmk)=x22cos(2kπmx)+1 ω m − k = e i 2 ( m − k ) π m = e i 2 π ( 1 − k m ) = c o s ( 2 π ( 1 − k m ) ) + i s i n ( 2 π ( 1 − k m ) ) = c o s ( 2 k π m ) − i s i n ( 2 k π m ) = ω k ¯ , k m < 1 ⇒ ( x − ω k ) ( x − ω m − k ) = x 2 − ( ω k + ω m − k ) x + ( ω k ω m − k ) = x 2 − 2 c o s ( 2 k π m x ) + 1 , 同理可得: xm+1=k=0m1(xξk),ξk=ei(π+2kπ)m x m + 1 = ∏ k = 0 m − 1 ( x − ξ k ) , ξ k = e i ( π + 2 k π ) m

    例题:证明 cosπ2n+1cos2π2n+1cosnπ2n+1=12n c o s π 2 n + 1 c o s 2 π 2 n + 1 ⋯ c o s n π 2 n + 1 = 1 2 n
    要证明这个需要以下3点:
    (1) 1ei2θ=1cos2θisin2θ=2cosθ(cosθ+isinθ)|1cos2θisin2θ|=2|cosθ| − 1 − e i 2 θ = − 1 − c o s 2 θ − i s i n 2 θ = − 2 c o s θ ( c o s θ + i s i n θ ) ⇒ | − 1 − c o s 2 θ − i s i n 2 θ | = 2 | c o s θ |
    (2)设 ω=cos2π2n+1+isin2π2n+1=ei2π2n+1(|1ω|=2|cos(π2n+1)| ω = c o s 2 π 2 n + 1 + i s i n 2 π 2 n + 1 = e i 2 π 2 n + 1 ( ⇒ | − 1 − ω | = 2 | c o s ( π 2 n + 1 ) | ,那么 x2n+x2n1++1=(xω)(xω2)(xω2n)() x 2 n + x 2 n − 1 + ⋯ + 1 = ( x − ω ) ( x − ω 2 ) ⋯ ( x − ω 2 n ) ( ∗ ) 推导如下: x2n+11=(x1)(xω)(xω2)(xω2n)x2n+11x1=(xω)(xω2)(xω2n)1(1x2n+1)1x=(xω)(xω2)(xω2n) x 2 n + 1 − 1 = ( x − 1 ) ( x − ω ) ( x − ω 2 ) ⋯ ( x − ω 2 n ) ⇒ x 2 n + 1 − 1 x − 1 = ( x − ω ) ( x − ω 2 ) ⋯ ( x − ω 2 n ) ⇒ 1 ( 1 − x 2 n + 1 ) 1 − x = ( x − ω ) ( x − ω 2 ) ⋯ ( x − ω 2 n )
    (3) cos(2n+1k)π2n+1=coskπ2n+1 c o s ( 2 n + 1 − k ) π 2 n + 1 = c o s k π 2 n + 1
    () ( ∗ ) 等式中 x=1 x = − 1 ,且取两边长度 1=|(1ω)(1ω2)(1ω2n) 1 = | ( − 1 − ω ) ( − 1 − ω 2 ) ⋯ ( − 1 − ω 2 n ) 中右边每一项利用(1)式子得到 |1ω|=2|cosπ2n+1|,|1ω2|=2|cos2π2n+1|,|1ωn|=2|cosnπ2n+1| | − 1 − ω | = 2 | c o s π 2 n + 1 | , | − 1 − ω 2 | = 2 | c o s 2 π 2 n + 1 | , … | − 1 − ω n | = 2 | c o s n π 2 n + 1 |

    从n+1项起根据(3)得:

    |1ωn+1|=2|cos(n+1)π2n+1|=2|cos(2n+1n)π2n+1|=2|cos(πnπ2n+1)|=2|cos(nπ2n+1)|=|1ωn| | − 1 − ω n + 1 | = 2 | c o s ( n + 1 ) π 2 n + 1 | = 2 | c o s ( 2 n + 1 − n ) π 2 n + 1 | = 2 | c o s ( π − n π 2 n + 1 ) | = 2 | c o s ( n π 2 n + 1 ) | = | − 1 − ω n |

    |1ωn+2|=2|cos(n+2)π2n+1|=2|cos[(2n+1)(n1)]π2n+1|=2|cos(π(n1)π2n+1)|=2|cos(n1)π2n+1|=|1ωn1| | − 1 − ω n + 2 | = 2 | c o s ( n + 2 ) π 2 n + 1 | = 2 | c o s [ ( 2 n + 1 ) − ( n − 1 ) ] π 2 n + 1 | = 2 | c o s ( π − ( n − 1 ) π 2 n + 1 ) | = 2 | c o s ( n − 1 ) π 2 n + 1 | = | − 1 − ω n − 1 |

    ⋯ ⋯

    |1ω2n|=2|cos2nπ2n+1|=2|cos(2n+11)π2n+1|=2|cos(ππ2n+1)|=2|cos(π2n+1)|=|1ω| | − 1 − ω 2 n | = 2 | c o s 2 n π 2 n + 1 | = 2 | c o s ( 2 n + 1 − 1 ) π 2 n + 1 | = 2 | c o s ( π − π 2 n + 1 ) | = 2 | c o s ( π 2 n + 1 ) | = | − 1 − ω |

    复矩阵

    Hermitian矩阵

    复数矩阵 A=(aij)m×n,aijC A = ( a i j ) m × n , a i j ∈ C , 那么称 AT¯¯¯¯¯¯¯(=A¯T) A T ¯ ( = A ¯ T ) Hermitian 矩阵,记为 AH A H 。例如: Z=(1+ii)ZH=(1ii) Z = ( 1 + i i ) → Z H = ( 1 − i − i ) ,而且发现 ZZH=||Z||2 Z Z H = | | Z | | 2 ,这个可以类比实数中的 xTx=||x||2 x T x = | | x | | 2 性质 (AH)H=A,(AB)H=BHAH ( A H ) H = A , ( A B ) H = B H A H (按照共轭转置即可求得),正如在 Rn R n 的定义内积,在 C C 上也可以定义内积u,vCn,uHv=(u¯1u¯n)(v1...vn)=u¯1v1++u¯nvn内积的性质 uHv=vHu¯¯¯¯¯¯¯¯¯ u H v = v H u ¯

    厄米特Hermite矩阵

    在实数矩阵中有对称矩阵的概念和作用,复数矩阵有类似的——厄米特矩阵(Hermite matrix),定义为: A=AH A = A H ,即一个矩阵的共轭转置等于它本身,那么称这种矩阵为Hermite阵。例: (21i1+i3) ( 2 1 + i 1 − i 3 )

    • 性质1:Hermite阵对角线元素为实数。

    • 性质2: zC,A=AHzHAz z ∈ C , A = A H ⇒ z H A z 是一个实数。证明如下: zHAz¯¯¯¯¯¯¯¯¯¯¯¯T=(zHAz)H=zHAHz=zHAz z H A z ¯ T = ( z H A z ) H = z H A H z = z H A z

    • 性质3:设 A,B A , B 是Hermite阵,则 A+B A + B 也是,证明: (A+B)H=AH+BH=A+B ( A + B ) H = A H + B H = A + B 。进一步,若 AB=BA A B = B A (即乘法可交换的时候),则 AB A B 是Hermite阵。 An ⇒ A n 是Hermite阵。

    • 性质4:设 A A 是一个n阶复矩阵, AAH,A+AH A A H , A + A H 是Hermite阵,联系对比实对称矩阵的 AAT,ATA,A+AT A A T , A T A , A + A T

    • 性质5:一个Hermite矩阵A的特征值是实数。证明:设 Az=λ0z A z = λ 0 z ,则 zHAz=λ0zHz z H A z = λ 0 z H z zHAz z H A z zHz z H z 均为实数 λ0(z00) ⇒ λ 0 ( z 0 ≠ 0 ) 是实数。

    • 性质6:一个Hermite阵的不同特征值的特征向量相互正交。证明:设 (1)Az1=λ1z1,(2)Az2=λ2z2,λ1λ2 ( 1 ) A z 1 = λ 1 z 1 , ( 2 ) A z 2 = λ 2 z 2 , λ 1 ≠ λ 2 , 在(1)两边同乘以 zH2 z 2 H 得: (3)zH2Az1=zH2λ1z1(4)zH2AHz1=(Az2)Hz1=λ2¯¯¯¯¯zH2z1=λ2zH2z1 ( 3 ) z 2 H A z 1 = z 2 H λ 1 z 1 ⇒ ( 4 ) z 2 H A H z 1 = ( A z 2 ) H z 1 = λ 2 ¯ z 2 H z 1 = λ 2 z 2 H z 1 ,由 (3)(4)λ1zH2z1=λ2zH2z1(λ1λ2)zH2z1=0 ( 3 ) ( 4 ) ⇒ λ 1 z 2 H z 1 = λ 2 z 2 H z 1 ⇒ ( λ 1 − λ 2 ) z 2 H z 1 = 0 ,因为 λ1λ2 λ 1 ≠ λ 2 得: zH2z1=0 z 2 H z 1 = 0

    酉unitary矩阵

    酉矩阵是正交阵的复数类比。 Un×n U n × n 是酉矩阵 zCn,||Uz||=||z|| ∀ z ∈ C n , | | U z | | = | | z | | ,证明: UHU=In|Uz|2=zHUHUz=zHz=|z|2|Uz|=|z||λ|=1 U H U = I n ⇒ | U z | 2 = z H U H U z = z H z = | z | 2 ⇒ | U z | = | z | ⇒ | λ | = 1 。得出与实数矩阵类似的性质1:酉矩阵乘以任何向量不改变它的模长。性质2 U U 是酉矩阵,则U的特征值模长为1。 例: u=1212016161+i361i3231+i32313 u = ( 1 2 − 1 6 1 − i 3 2 3 1 2 1 6 − 1 + i 3 2 3 0 1 + i 3 6 1 3 ) |detU|=|λi|=1 | d e t U | = ∏ | λ i | = 1 (行列式的长度等于特征值长度的乘积)。

    而实数的正交阵,也有类似的性质。下面证明正交阵不同特征值对应的特征向量相互正交:

    因为 Q Q 正交阵,QTQ=E,|Q|=1=λ1λ2λn,设 λ1,λ2 λ 1 , λ 2 Q Q 的两个不同的特征值,ξ1,ξ2为对应的特征向量 (1)Qξ1=λ1ξ1,(2)Qξ2=λ2ξ2,(3)(ξ2)TQT=λ2(ξ2)T(3)(1)ξT2QTQξ1=λ1λ2ξT2ξ1(λ1λ21)ξT2ξ1=0 ( 1 ) Q ξ 1 = λ 1 ξ 1 , ( 2 ) Q ξ 2 = λ 2 ξ 2 , ( 3 ) ( ξ 2 ) T Q T = λ 2 ( ξ 2 ) T ⇒ ( 3 ) ( 1 ) ⇒ ξ 2 T Q T Q ξ 1 = λ 1 λ 2 ξ 2 T ξ 1 ⇒ ( λ 1 λ 2 − 1 ) ξ 2 T ξ 1 = 0
    |λ1|=|λ2|=1,λ1λ2 | λ 1 | = | λ 2 | = 1 , λ 1 ≠ λ 2 ,得 ξ2Tξ1=0,ξ2,ξ1 ξ 2 T ξ 1 = 0 , 因 此 ξ 2 , ξ 1 正交。

    复正规阵

    酉阵和Hermite矩阵均为复正规矩阵,即: AHA=AAH A H A = A A H 。 酉相似:设 A,B A , B 是;两 n n 阶复矩阵,若存在酉矩阵U,使得 A=UHBU A = U H B U ,则 A A B酉相似(联系实数矩阵的正交相似)。定理:设 A A 复正规阵,则

    1. 向量u A A 的关于λ的特征向量 u ⇔ u AH A H 的关于 λ¯ λ ¯ 的特征向量。证明:
      Au=λu(AλI)u=0 A u = λ u ⇒ ( A − λ I ) u = 0 B=AλI||BHu||2=uHBBHu=uHBHBu=||Bu||2=0 B = A − λ I ⇒ | | B H u | | 2 = u H B B H u = u H B H B u = | | B u | | 2 = 0 ,因为 ||BHu||2=0BHu=0,(AλI)H=BH(AHλ¯I)u=0AHu=λ¯u | | B H u | | 2 = 0 ⇒ B H u = 0 , ( A − λ I ) H = B H ⇒ ( A H − λ ¯ I ) u = 0 ⇒ A H u = λ ¯ u

      • 不同特征值的特征向量正交。证明与Hermite矩阵一样。
      • 定理(Schur)任意一个复矩阵 A A 酉相似于一个上三角阵。即: Uunitary matrix, Acomplex matrix,UH=U1,UHAU=(λ1000λn)任意一个复正规阵酉相似于对角阵,特别地,酉相似于 111 ( 1 1 ⋱ 1 ) , UHAU=diag(λ1,,λn)AU=λU U H A U = d i a g ( λ 1 , … , λ n ) ⇒ A U = λ U

        一个实矩阵 A A 是正规的ATA=AAT。例如, A A 是正交阵或者A是对称(反对称)矩阵。

        如果 A A 是正规的,那么存在正交阵Ω使得:

        ΩTAΩ=(a1b1b1a1)(asbsbsas)λ2s+1λn Ω T A Ω = ( ( a 1 b 1 − b 1 a 1 ) ⋱ ( a s b s − b s a s ) λ 2 s + 1 ⋱ λ n ) ,即实正规阵正交相似于分块对角阵

        对于复正规阵酉相似对角阵 UHAU=diag(λ1,,λn)AU=λU U H A U = d i a g ( λ 1 , … , λ n ) ⇒ A U = λ U ,这里如果把 U U 的列向量写成uk=β+iγ,  k[1,n], β,γRn,例如: (1+i1i)=(11)+i(11) ( 1 + i 1 − i ) = ( 1 1 ) + i ( 1 − 1 )

        Auk=λkukA(β+iγ)=λk(β+iγ) A u k = λ k u k ⇒ A ( β + i γ ) = λ k ( β + i γ ) ,令 λk=a+ib λ k = a + i b ,得: Aβ=aβbγ,Aγ=bβ+aγ A β = a β − b γ , A γ = b β + a γ ⇒
        A(β,γ)=(β,γ)(abba) A ( β , γ ) = ( β , γ ) ( a b − b a ) ,所以 Ω Ω 的实际上是由 U U 的特征向量的实部和虚部组成的这样一个形式。 Ω是一个正交阵,那 β β γ γ 是不是正交的?它们的长度相等嘛?不然无法保证 Ω Ω 是一个正交阵。 结论:设 A A n解实正交阵。若 λ=a+ib(b0) λ = a + i b ( b ≠ 0 ) A A 的特征值,x=x1+ix2, x1,x2Rn是对应的特征向量,则 ||x1||=||x2|| | | x 1 | | = | | x 2 | | ,且 x1,x2 x 1 , x 2 是相互正交的。

        证明:如果 λ=a+ib λ = a + i b A A 的特征值,那么λ=aib 也是 A A 的特征值。因为A实正交阵,所以对 Ax=λx A x = λ x 取两边共轭得: Ax¯¯¯¯¯¯¯=Ax¯=λ¯x¯ A x ¯ = A x ¯ = λ ¯ x ¯ 。那么得到 λ,λ¯ λ , λ ¯ 都是 A A 的特征值,由于正交阵不同特征值对应的特征向量正交,所以x¯Hx=0,x=x1+ix2,x¯=x1ix2||x1||=||x2||,x1Tx2=0

        例2:证明: (cosθsinθsinθcosθ) ( c o s θ − s i n θ s i n θ c o s θ ) (eiθ00eiθ) ( e i θ 0 0 e − i θ ) 酉相似。 U=12(i11i) U = 1 2 ( i 1 1 i )

        例3:设 A A 是Hermite阵,则I+iA是非奇异的。由于A的特征值是实数,那么 I+iA I + i A 特征值的是 λi+1 λ i + 1 不可能是0,行列式就不可能是0,因此是非奇异的。如果A是Hermite阵,那么 U=(IiA)(I+iA)1 U = ( I − i A ) ( I + i A ) − 1 是酉阵,验证 UH=(IiA)1(I+iA)=(I+iA)(IiA)1 U H = ( I − i A ) − 1 ( I + i A ) = ( I + i A ) ( I − i A ) − 1 (注:分块是相同的矩阵是可交换即变成分块对角阵),这个是用来通过实对称阵或Hermite阵构造酉矩阵

        离散傅里叶变换DFT

        回忆若 f(x),f(x) f ( x ) , f ′ ( x ) 是piecewise连续的且 f(x+L)=f(x) f ( x + L ) = f ( x ) , 则 f(x)=a0+(ancos(2πnxL)+bnsin(2πnxL)),an=2LL0f(x)cos2πnxLdx, bn=2LL0f(x)sin2πnxLdx f ( x ) = a 0 + ∑ ( a n c o s ( 2 π n x L ) + b n s i n ( 2 π n x L ) ) , a n = 2 L ∫ 0 L f ( x ) c o s 2 π n x L d x ,   b n = 2 L ∫ 0 L f ( x ) s i n 2 π n x L d x , 令 V={f(x)|f(x)}R V = { f ( x ) | f ( x ) 如上条件 } → R ∞
        f(x)(a0,a1,b1,a2,b2,) f ( x ) → ( a 0 , a 1 , b 1 , a 2 , b 2 , … )
        这是一个线性映射, (a0,a1,b1,) ( a 0 , a 1 , b 1 , … ) f(x)