
简介这是一份面向Visual C开发者的newmat矩阵运算类库资源旨在解决C项目中手写矩阵与向量算法的繁琐问题常用于科学计算、图像处理、信号处理等需要大规模数值计算的场景。压缩包共97个文件约284KB核心由56个cpp源文件和12个h头文件组成并带有txt说明文档、mak工程文件、dox接口说明与多个可直接运行的示例程序资源结构完整清晰。newmat库封装了矩阵加法、减法、乘法、转置、求逆、行列式求解、特征值计算等常用操作且对内存管理和错误检查做了底层处理可让使用者专注于业务逻辑与算法设计。包内除核心库外还包含不同构建配置和示例例如矩阵基本运算、快速傅里叶变换、乔列斯基分解与奇异值分解等典型应用方便开发者对照学习或直接复用。目前已有163人学习下载这套轻量级、易集成的数学计算组件对需要在Visual C中高效完成矩阵计算的工程师与算法研究人员都有实用价值。1. 一个不新的库为什么还在 Visual C 项目里发光newmat 是一套用 C 写的矩阵运算库体积小、无依赖、源码直接编译即可用。它的名字看起来很老因为第一版来自上世纪 90 年代但至今还有大量工业和学术项目在 Windows 平台、Visual C 环境里使用它。原因很简单在很多只需要矩阵乘、求逆、算特征值的场景里Eigen 或 Armadillo 的模板复杂度反而成了负担而 newmat 的接口直白、编译快生成的库文件也就几百 KB。更重要的是它原生支持 Visual C不需要额外适配。标题里那个 newmat.lib.zip_数学计算_Visual_C_ 精确地描述了它的实际用途一个打包好的、可直接链接的静态库让你在 VC 项目里快速做数学计算。下面这篇东西从解压到编译、再写到能跑的矩阵运算代码按我平时在工程里用它的顺序走一遍。新手能照着搭出来熟练工也能看到一些不常注意的边界。2. 把 newmat.lib.zip 变成可用库Visual C 下的编译与配置2.1 解开 zip 后先看什么目录结构和版本识别拿到newmat.lib.zip第一件事是解压不是直接改名。压缩包解开后通常包含以下内容include/newmat.h主头文件所有矩阵类的声明都在里面include/newmatap.h辅助头文件包含矩阵分解、求逆等高级操作的声明include/newmatio.h输入输出相关的声明lib/可能已经有编译好的.lib文件也可能没有需要自己编译src/所有.cpp源文件大概 20 多个example/示例程序README或CHANGES版本记录先看newmat.h开头的#define通常会有类似NEWMAT_VERSION的宏表示版本号。注意不同版本的 newmat 在内存布局和函数签名上几乎没有变化但编译选项可能不同比如是否启用异常处理。如果 lib 目录里没有现成的.lib或者你拿到的 zip 是纯源码包就需要按下一步自己编译。2.2 用 Visual Studio 命令行编译 newmat 的完整步骤我一般不建议直接双击vcxproj去 vs 界面里点因为 newmat 的官方源码包很少带工程文件。更快的办法是用 Visual Studio 的开发者命令行工具直接跑编译脚本。以 VS2019 或 VS2022 为例打开 x64 Native Tools Command Prompt for VS 2022cd 到解压后的目录假设为C:\tools\newmatcd C:\tools\newmat # 编译所有源文件生成静态库 newmat.lib cl /c /EHsc /O2 /Iinclude src/*.cpp lib /OUT:newmat.lib *.obj参数说明/c只编译不链接生成.obj文件/EHsc启用 C 异常处理。newmat 的某些操作如矩阵维度不匹配会抛出异常这个参数必须加/O2最大速度优化数学库很吃这个/Iinclude指定头文件搜索路径src/*.cpp编译 src 目录下所有源文件执行完后目录下会出现一个newmat.lib。如果你要 32 位版本就用x86 Native Tools重新跑一遍。记得把不同架构生成的 lib 分开存放比如lib/x64/newmat.lib和lib/x86/newmat.lib。2.3 在 VC 项目中链接 newmat.lib 的三种配置方法编译出 lib 后要在你自己的项目里链接它。有三种方式按项目类型选一种。第一种最直接在 VS 项目属性里配置。右键项目 - 属性 - C/C - 常规 - 附加包含目录填C:\tools\newmat\include然后到链接器 - 常规 - 附加库目录填C:\tools\newmat\lib最后在链接器 - 输入 - 附加依赖项里写newmat.lib。第二种用代码指定适合快速测试// 在用到 newmat 的 .cpp 文件顶部或单独一个头文件里 #pragma comment(lib, C:\\tools\\newmat\\lib\\x64\\newmat.lib)第三种用 CMake。在CMakeLists.txt里添加add_library(newmat STATIC IMPORTED) set_target_properties(newmat PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/x64/newmat.lib INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include ) target_link_libraries(your_target PRIVATE newmat)注意前两种方法在 64 位和 32 位切换时容易弄混路径CMake 方式可以自动判断。但如果项目只是临时算点数据我反而推荐用#pragma comment因为改动少不污染工程文件。2.4 编译期常见错误和修复第一次连 newmat常见错误有这几个LNK2001 无法解析的外部符号_main这通常是你把 newmat.lib 链接到了一个没有 main 函数的 DLL 或控制台程序里。newmat 本身不依赖 main但如果你的项目是空项目请确认入口点存在。C2059 语法错误(或C2143多半是头文件包含顺序问题。请先包含newmat.h再包含newmatap.h最后newmatio.h。如果你的代码里用了std命名空间的头文件建议先包含标准库再包含 newmat 头文件。比如#include iostream #include vector #include newmat.h #include newmatap.h #include newmatio.hC2676 二进制“”未定义newmatio.h没包含。operator重载在里面声明。fatal error C1083: 无法打开包括文件 “newmat.h”附加包含目录没写对或者你用的是双引号而不是尖括号。确认路径下真有newmat.h。静态库与运行库不匹配如果在编译 newmat 时用了/MT静态运行时但你的项目是/MD动态运行时链接时会报LNK2038。解决方法是编译 newmat 时和项目保持一致。在编译命令里加/MD或/MT即可。3. 矩阵运算的入口newmat 的核心类与常用构造函数3.1 Matrix, ColumnVector, RowVector 和 DiagonalMatrix 的创建newmat 最常用的类是Matrix它表示一个二维矩阵。除此之外还有ColumnVector列向量、RowVector行向量、DiagonalMatrix对角矩阵、SymmetricMatrix对称矩阵等。创建方式非常直观#include newmat.h #include iostream int main() { // 创建 3x3 矩阵元素未初始化 Matrix A(3, 3); // 创建 3 元素列向量初始化为 0 ColumnVector b(3); b 0.0; // 创建 3 元素行向量用数组初始化 double vals[3] {1.0, 2.0, 3.0}; RowVector r(vals, 3); // 创建 4x4 对角矩阵主对角线给定 DiagonalMatrix D(4); D(1,1) 2.0; D(2,2) 3.0; D(3,3) 4.0; D(4,4) 5.0; return 0; }注意Matrix A(3, 3);不会自动清零里面的值是随机内存数据。如果需要零矩阵必须手动赋零或用Zero()方法。ColumnVector b(3); b 0.0;是合法的因为 newmat 允许用标量给向量/矩阵整体赋值。3.2 从数组和流读入矩阵的两种方式实际工作中矩阵数据通常来自文件或数组。newmat 支持直接构造// 从一维数组构造 2x3 矩阵数组按行填充 double arr[6] {1, 2, 3, 4, 5, 6}; Matrix M; M arr; // 注意这里没有指定维度需要在之前就定义好 M 的尺寸正确做法是先定义尺寸再读入Matrix M(2, 3); M arr;运算符在这里是“逐元素填充”不是流插入。它会按行优先顺序把数组元素复制到矩阵。从文件读取时可以用标准流配合operator包含newmatio.h后可用#include fstream #include newmat.h #include newmatio.h std::ifstream fin(data.txt); Matrix N; fin N; // 文件格式第一行两个数表示行数和列数后跟数字文件格式要求第一行是行数 列数下一行开始按行排列矩阵元素用空格或换行分隔。如果文件不满足这些要求会读到错误数据。为了避免建议读取前先检查fin状态。3.3 矩阵赋值与运算符重载的坑newmat 的是深拷贝不是引用拷贝。Matrix B A;会分配新内存修改 B 不影响 A。但注意下面这种Matrix A(2,2); Matrix B A; // OK深拷贝 Matrix C; C A; // 也 OKC 会先释放旧内存再分配和 A 相同的尺寸但是你不能对一个已经定型的矩阵用改变尺寸除非先调用ReSizeMatrix D(2,2); Matrix E(3,3); D E; // 错误不会newmat 会自动 ReSize实际上 newmat 重载了会自动调整尺寸但如果后续代码依赖 D 的旧尺寸就会出问题。我的习惯是显式D.ReSize(3,3); D E;以表达意图。另一个坑是商运算。A / B在 newmat 中是A * (B 的逆)的简写而不是逐元素除。如果你想要逐元素除必须手动循环。同样A * B是矩阵乘法不是点乘向量内积用的是DotProduct函数。这些和 MATLAB 的习惯不同刚转换过来时最容易踩。4. 写第一段数学计算代码用 newmat 做矩阵乘法和求逆4.1 最小可运行示例矩阵乘法并输出进入实战。假设你编译好了 newmat.lib并配置好了项目下面这一段就是完整的矩阵乘法程序#include iostream #include newmat.h #include newmatio.h using namespace std; int main() { Matrix A(2, 3); Matrix B(3, 2); A 1, 2, 3, 4, 5, 6; B 1, 0, 0, 1, 1, 1; Matrix C A * B; cout A * B endl; cout C endl; return 0; }编译运行后输出A * B 4.0000 3.0000 10.0000 6.0000说明A 1, 2, 3, 4, 5, 6;是 newmat 特有的初始化写法它会把后面的数值按行填充进矩阵数量必须等于矩阵元素总数否则会抛出异常。cout C输出的是矩阵默认格式是固定小数点后四位每个元素占 10 个字符宽。如果你想要更多精度可以用cout setprecision(15)但 newmat 的输出流重载不一定完全遵守 iomanip必要时可以格式化Row再输出。4.2 求逆、转置和行列式必须知道的条件求逆是最常见的数学计算。newmat 提供.i()方法、Inverse()函数以及运算符A / B相当于A * B.i()。看代码Matrix A(2, 2); A 4, 7, 2, 6; Matrix Ainv A.i(); cout A inverse: endl; cout Ainv endl; cout Check A * Ainv: endl; cout A * Ainv endl;输出应该是单位矩阵。但要小心newmat 求逆默认使用高斯消元不检查条件数。如果矩阵奇异即行列式为 0A.i()不会抛出异常而是返回一个可能全是 NaN 的矩阵。所以在求逆前除非你确定矩阵非奇异否则最好先计算行列式Real det A.determinant(); if (fabs(det) 1e-12) { cerr Matrix is singular or near-singular, inversion unsafe endl; return 1; } Matrix Ainv A.i();这里的Real是 newmat 定义的浮点类型默认是double。determinant()返回Real。转置很简单A.t()。注意t()返回的是临时对象不能直接用来修改。比如A.t()(1,1) 0;是非法操作。4.3 性能调试对时间有要求时怎么优化newmat 的性能在中小规模矩阵几百乘几百下完全够用。但如果你在循环里反复创建和销毁矩阵开销会很大。优化策略预先分配矩阵不要频繁ReSize。比如在迭代算法中每步都要算x A * x定义一次Matrix temp(n, n);然后循环体里用temp A * x;而不是创建新的 Matrix。尽量用InvertedMatrix预处理。如果你需要多次求A * B.i()可以先把逆算出来存起来而不是每次调用.i()。newmat 提供了一个高效机制InvertedMatrix invA A.i(); Matrix X1 invA * B1; Matrix X2 invA * B2;InvertedMatrix不会立刻计算逆而是在乘的时候按需计算并且内部缓存结果这样多次乘法只做一次分解。开启编译优化。上文编译 newmat 时加/O2就够了但如果你用的是 Debug 配置记得把项目的优化设为/O2或/Ob2。同时newmat 在 Release 和 Debug 下行为一致但 Debug 别加/RTC1运行时检查否则会触发 newmat 内部的内存越界检查导致误报。5. 求解线性方程组newmat 的高级应用5.1 用 Solve 方法处理 Axb在数学计算里解线性方程组Ax b远比求逆常见。newmat 重载了运算符/来实现x A / b;等价于x A.i() * b。但 better 的做法是用A.Solve(b)Matrix A(3, 3); ColumnVector b(3); A 2, 1, -1, -3, -1, 2, -2, 1, 2; b 8, -11, -3; ColumnVector x A.Solve(b); cout Solution x: endl; cout x endl;Solve使用 LU 分解比直接求逆再乘快一倍左右。注意Solve要求A是方阵且非奇异。如果不是方阵newmat 会抛出一个异常类型是BaseException你可以 try-catch 捕获try { x A.Solve(b); } catch (BaseException e) { cerr Exception: e.what() endl; return 1; }5.2 对称正定矩阵与 Cholesky 分解如果你的A是对称正定矩阵用Cholesky分解更稳更快。newmat 提供了Cholesky类#include newmatap.h SymmetricMatrix S(2); S 4, 2, 2, 3; Cholesky chol(S); Matrix L chol.lowerTriangular(); cout L: endl; cout L endl; // 解方程 S * x b ColumnVector b(2); b 1, 1; ColumnVector y chol.Solve(b);Cholesky构造函数会检查矩阵是否为对称正定。如果不是它会抛异常或者产生未定义结果。务必先确认你的矩阵满足条件。lowerTriangular()返回下三角矩阵 L满足S L * L.t()。5.3 特征值和奇异值分解的接口特征值分解在 newmat 中通过EigenValues类和SVD类实现。以SVD奇异值分解为例#include newmatap.h Matrix A(3, 2); A 1, 2, 3, 4, 5, 6; SVD svd(A); Matrix U svd.U(); Matrix V svd.V(); DiagonalMatrix D svd.D(); cout U: endl U endl; cout D: endl D endl; cout V: endl V endl;注意SVD分解的结果里D是对角矩阵其对角线元素是奇异值按降序排列。U和V是正交矩阵。这个接口很干净但有个陷阱SVD只对行数大于等于列数的矩阵有效。如果你的矩阵是“宽矩阵”行数小于列数newmat 会强制给它转置导致结果与预期不同。解决方法是先对 A 手动转置再分解。特征值分解的用法类似SymmetricMatrix A(2); A 2, 1, 1, 2; EigenValues eigen(A); ColumnVector evals eigen.EigenValues(); Matrix evecs eigen.EigenVectors();EigenValues类只接受对称矩阵。如果你传非对称矩阵newmat 不会报错但结果可能不对。实际使用时如果矩阵可能不对称用EigenValues(A, true)那个带参数的重载它会强制先做对称化。6. 收尾技巧newmat 与 Visual C 的版本兼容性和内存检查6.1 处理 newmat 的静态变量和多线程问题newmat 内部有几个全局静态变量用于控制输出格式、内存分配策略等。在单线程程序里没问题但在多线程环境下这些全局变量没有互斥保护如果多个线程同时做矩阵运算可能产生竞争。常见做法是在每个线程开始前设置这些变量比如int main_thread_no; // 设置 newmat 使用当前线程的全局变量 newmat_parallel_flag 1; // 开启并行支持如果版本里有的话但更稳妥的方案是每个线程各自维护自己的矩阵对象避免共享同一矩阵。如果你必须共享矩阵请在外层加锁比如std::mutex。6.2 用 CRT 调试堆定位内存泄漏newmat 的所有内存分配都是通过new/delete完成的所以 Visual C 的 CRT 内存泄漏检测能派上用场。在程序入口加上#define _CRTDBG_MAP_ALLOC #include stdlib.h #include crtdbg.h #include newmat.h int main() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); Matrix M(10, 10); // 你的代码 return 0; }运行完在输出窗口会显示泄漏信息。newmat 本身不会泄漏但如果你使用Matrix* p new Matrix(5,5);忘了delete就能精准定位。注意newmat 的某些内部缓冲可能保留到静态对象销毁这是正常的不要误报。6.3 让代码更稳的宏和预处理开关newmat 源码里提供了一些可以调整行为的宏在编译项目时你可以在“预处理定义”里加上宏名作用建议NO_NAN禁止 NaN 传播运算中出现 NaN 会报异常调试时开USE_VALIDATION开启维度检查和边界检查默认可能关闭强烈建议打开USING_DOUBLE使Real类型使用double不需要设置默认就是在 VS 项目属性 - C/C - 预处理器 - 预处理器定义 中加入USE_VALIDATION1。这样当你尝试访问越界元素或矩阵维度不匹配时newmat 会在运行时抛出异常而不是静默算错。这比事后排查数据要省时得多。最后一个小技巧用 newmat 时cout输出矩阵会走流操作符但如果你打算把矩阵数据导入 Python 或 MATLAB 分析直接在cout前面加一句cout C endl;不够——你可以用C.print(C)让输出带变量名或者直接用C.output(std::cout)它不会自动换行方便拼接。.output 的默认格式其实是原始数字适合用逗号分隔后保存为 CSV。std::ofstream file(matrix.csv); for (int i 1; i C.Rows(); i) { for (int j 1; j C.Cols(); j) { if (j 1) file , ; file C(i, j); } file \n; }这样导出的文件可以用 numpy 直接loadtxt。注意 newmat 的索引从 1 开始不是 0。这点你在写循环时最容易忽略也是初用者最常见的 off-by-one 错误源头。本文还有配套的精品资源点击获取