C#的数学库: ALGLIB, ILNumerics, Dew.Math, Math.NET Numerics等

news/2025/3/11 15:21:18/文章来源:https://www.cnblogs.com/firespeed/p/18676931

https://zhuanlan.zhihu.com/p/12783824787

以下4个为商业库,性能都不错,发布的dll文件均未加密,容易逆向。(等我以后有空了,再来详细对比一下4个商业库的性能)

  • ALGLIB(C++/C#/Java numerical analysis library): 有免费版和商业版,支持C++/C#/Java/Delphi/CPython五种语言,涵盖数据分析(分类与回归、统计学)、优化与非线性问题求解、插值与最小二乘拟合、线性代数、FFT等领域。免费版是开源的,官网下载的压缩包中就包含源代码,相对商业版缺少多线程、native HPC Kernels和Intel MKL,不想花钱的首选ALGLIB免费版;
  • ILNumerics(ILNumerics – Technical Computing): 只有收费版,支持多核并行计算,绘图功能比较强大,涵盖线性代数、FFT等领域,支持HDF5文件;
  • Dew.Math(Math Library Component Delphi Component C++ Builder Component Net): 只有收费版,涵盖矩阵和向量运算、DSP、统计学、数据挖掘、FFT等领域,利用AVX, AVX2, AVX512以及多核心加速。
  • Extreme.Numerics(Build financial, engineering and scientific applications faster): 只有收费版,涵盖统计学、概率论、回归、机器学习、线性、代数、矩阵、向量、求解、优化、曲线拟合、fft、积分、微分、插值等领域。

以下均为开源库,性能方面大多不如商业库,

  • SdcbArithmetic(): 开源,作者的知乎账号是 
    @周杰
     ,P/Invoke调用GMP(GNU Multiple Precision Arithmetic Library)和MPFR(multiple-precision floating-point computations)库,性能不错,可以进行高精度计算。
  • MathNET Numerics(GitHub - mathnet/mathnet-numerics): 开源,知名度比较高,但项目更新频率低,性能很差,bug很多(下面有几个具体的例子),只能做做玩具级的应用,或者学生写作业用。
  • AngouriMath(GitHub asc-community/AngouriMath): 符号运算库,可以解方程(组),求符号导数,解决集合问题等。
  • CSharpMath(GitHub - verybadcat/CSharpMath): 根据Latex表达式渲染公式和符号。
  • StringMath(GitHub - miroiu/string-math): 对字符串形式的数学表达式求值,比如double result = "1 * (2 - 3) ^ 2".Eval(); // 1.
  • ncalc(GitHub - ncalc/ncalc): 对字符串形式的数学表达式求值。
  • DecimalMath(GitHub - nathanpjones/DecimalMath): 包含Decimal版的Sqrt, Pow, Exp, Log, in, Cos, Tan, ASin, ACos, ATan, ATan2. 性能很差(下面有例子)。

以下为停止维护的开源库。

  • MetaNumerics: github仓库最后一次commit的时间是2020-08-22,基本等于停止维护;
  • AForgeNET: github仓库最后一次commit的时间是2020-06-19,基本等于停止维护;
  • AccordNET: github仓库已于2020-11-19转为archived状态,不再更新;

 

这里推荐一下本人的另一篇博客:C#数学相关开发性能优化方法


免费版ALGLIB 4.04.0的源代码中,diffequations.cs文件的第571~605行,用除法定义浮点常数时加了大量的(double)进行强制类型转换,显得十分繁琐和丑陋,不知为何要这么写,而不是写成1.0 / 5.0, 3.0 / 10.0等。

// Cask-Karp solver
// Prepare coefficients table.
// Check it for errors
//
state.rka = new double[6];
state.rka[0] = 0;
state.rka[1] = (double)1/(double)5;
state.rka[2] = (double)3/(double)10;
state.rka[3] = (double)3/(double)5;
state.rka[4] = 1;
state.rka[5] = (double)7/(double)8;
state.rkb = new double[6, 5];
state.rkb[1,0] = (double)1/(double)5;
state.rkb[2,0] = (double)3/(double)40;
state.rkb[2,1] = (double)9/(double)40;
state.rkb[3,0] = (double)3/(double)10;
state.rkb[3,1] = -((double)9/(double)10);
state.rkb[3,2] = (double)6/(double)5;
state.rkb[4,0] = -((double)11/(double)54);
state.rkb[4,1] = (double)5/(double)2;
state.rkb[4,2] = -((double)70/(double)27);
state.rkb[4,3] = (double)35/(double)27;
state.rkb[5,0] = (double)1631/(double)55296;
state.rkb[5,1] = (double)175/(double)512;
state.rkb[5,2] = (double)575/(double)13824;
state.rkb[5,3] = (double)44275/(double)110592;
state.rkb[5,4] = (double)253/(double)4096;
state.rkc = new double[6];
state.rkc[0] = (double)37/(double)378;
state.rkc[1] = 0;
state.rkc[2] = (double)250/(double)621;
state.rkc[3] = (double)125/(double)594;
state.rkc[4] = 0;
state.rkc[5] = (double)512/(double)1771;
state.rkcs = new double[6];
state.rkcs[0] = (double)2825/(double)27648;
state.rkcs[1] = 0;
state.rkcs[2] = (double)18575/(double)48384;
state.rkcs[3] = (double)13525/(double)55296;
state.rkcs[4] = (double)277/(double)14336;
state.rkcs[5] = (double)1/(double)4;
state.rkk = new double[6, n];

在ALGLIB的statistics.cs文件中,7704~7781行,写了19条if语句,没有用switch,也没有用数组,这实在是太过丑陋和低效了。如果在每个if中都写上return xxx,那也尚可接受,可是它没有return,做了大量的无效比较操作,令人唏嘘。

r = 0;
w = (int)Math.Round(-(7.141428e+00*s)+1.800000e+01);
if( w>=18 )
{r = -6.399e-01;
}
if( w==17 )
{r = -7.494e-01;
}
if( w==16 )
{r = -8.630e-01;
}
if( w==15 )
{r = -9.913e-01;
}
if( w==14 )
{r = -1.138e+00;
}
if( w==13 )
{r = -1.297e+00;
}
if( w==12 )
{r = -1.468e+00;
}
if( w==11 )
{r = -1.653e+00;
}
if( w==10 )
{r = -1.856e+00;
}
if( w==9 )
{r = -2.079e+00;
}
if( w==8 )
{r = -2.326e+00;
}
if( w==7 )
{r = -2.601e+00;
}
if( w==6 )
{r = -2.906e+00;
}
if( w==5 )
{r = -3.243e+00;
}
if( w==4 )
{r = -3.599e+00;
}
if( w==3 )
{r = -3.936e+00;
}
if( w==2 )
{r = -4.447e+00;
}
if( w==1 )
{r = -4.852e+00;
}
if( w<=0 )
{r = -5.545e+00;
}   
result = r;
return result;

 

MathNET Numerics

优点: 包含了线性代数(OpenBLAS)、特殊函数、概率论、回归分析、插值与拟合、积分、优化、解常微分方程等领域的函数。同时还提供了对F#语言的支持。支持导入matlab的.mat文件。支持CUDA和intel MKL.

缺点: 项目更新频率低,维护人员并不是很积极,几年前提的Pull requests和Issues都没有响应,其中某些代码的水平差得令人发指,比如位于src\Numerics\FindRoots.cs中的Quadratic函数,用于求解实系数的一元二次方程ax^2+bx+c=0,

public static (Complex, Complex) Quadratic(double c, double b, double a)
{if (b == 0d){var t = new Complex(-c/a, 0d).SquareRoot();return (t, -t);}var q = b > 0d? -0.5*(b + new Complex(b*b - 4*a*c, 0d).SquareRoot()): -0.5*(b - new Complex(b*b - 4*a*c, 0d).SquareRoot());return (q/a, c/q);
}
public static Complex SquareRoot(this Complex complex)
{if (complex.IsRealNonNegative()){return new Complex(Math.Sqrt(complex.Real), 0.0);}Complex result;var absReal = Math.Abs(complex.Real);var absImag = Math.Abs(complex.Imaginary);double w;if (absReal >= absImag){var ratio = complex.Imaginary / complex.Real;w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio))));}else{var ratio = complex.Real / complex.Imaginary;w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio))));}if (complex.Real >= 0.0){result = new Complex(w, complex.Imaginary / (2.0 * w));}else if (complex.Imaginary >= 0.0){result = new Complex(absImag / (2.0 * w), w);}else{result = new Complex(absImag / (2.0 * w), -w);}return result;
}

首先,没有处理 a=0 的情况,反而是考虑 b=0 这种并不需要单独考虑的情况;其次,根本没有必要调用对复数求平方根( Complex( , ).SquareRoot())这种计算代价很大的函数,Math.Sqrt(±Delta)足以解决问题;最后,没有必要使用代价较大的复数除法c/q. 我觉得哪怕是编程新手都很难写出这么蠢的代码。我提供了一个写法,在release版本中我的写法的速度是它的3.7倍,如果把我的写法中处理a=0的部分去掉,则速度是它的4.8倍。

public static (Complex, Complex) Quadratic(double c, double b, double a)
{Complex x1, x2;if (a == 0.0){if (b == 0.0){x1 = Complex.Zero / Complex.Zero;  // Complex.NaN;
			x2 = x1;}else{x1 = new Complex(-c / b, 0.0);x2 = x1;}}else{a = 1.0 / a;b = -0.5 * b * a;c = c * a;double delta = b * b - c;double sqrtDelta;if (delta < 0.0){sqrtDelta = Math.Sqrt(-delta);x1 = new Complex(b, sqrtDelta);x2 = new Complex(b, -sqrtDelta);}else{sqrtDelta = Math.Sqrt(delta);x1 = new Complex(b + sqrtDelta, 0.0);x2 = new Complex(b - sqrtDelta, 0.0);}}return (x1, x2);
}

再举一个证明其水平低下的例子,src\Numerics\SpecialFunctions\Factorial.cs中的Binomial函数,作用是计算组合数C^n_k=n!/(k!*(n-k)!)=n*(n-1)···(n-k+1)/(k*(k-1)···2*1),

public static double Binomial(int n, int k)
{if (k < 0 || n < 0 || k > n){return 0.0;}return Math.Floor(0.5 + Math.Exp(FactorialLn(n) - FactorialLn(k) - FactorialLn(n - k)));
}

这里面调用了3次FactorialLn(),就是对阶乘取自然对数,即ln(n!),它的阶乘函数是用查表法实现的(有一个含171个double型元素的数组),当n<171时,FactorialLn(n)的耗时约等于对数函数的耗时。n更大时会调用GammaLn函数,耗时更长。然后再调用指数函数和向下取整函数,这每一个函数的的耗时都不少。它这种写法在k比较大时才合适(我初步估计要大于100),下面是我用循环实现的写法:

static double MyBinomial(int n, int k)
{if (k < 0 || n < 0 || k > n){return double.NaN;}else if (k == 0 || k == n){return 1.0;}else{if (k > (n >> 1)){k = n - k;}double dblN = n;double dblK = k;double Cnk = dblN / dblK;for (double i = 1.0; i < dblK; i++){Cnk = Cnk * (dblN - i) / (dblK - i);}return Math.Round(Cnk);}
}

当k比较小时,我的写法的速度是它的几十倍,浮点误差也远小于它。它如果把FactorialLn在n比较小时也用查表法实现,速度能提高,误差也能减少。所以,更合理的做法是,k比较小时,用循环或者查阶乘表的方法实现,k比较大时,用查阶乘的对数表(n比较小)与GammaLn函数(n比较大),再结合指数函数Exp的方法。


DecimalMath的水平也不行,下面是它的Exp函数的实现,看一眼就知道没什么性能可言了。对于输入参数d,作者将其分成整数部分t和小数部分,竟然还是递归调用的,整数部分采用了快速幂算法(竟然不是查表)。不知道要把泰勒级数的计算进行循环展开,循环的终止条件竟然是if (nextAdd == 0),也不知道要把除以常数的除法改成乘法。decimal型的计算本来就很慢了(我做过两个简单的测试,decimal型的耗时是double型耗时的60~80倍),如果还不努力优化,基本上可以说慢得没法用。decimal型的Exp函数,允许的参数的最大值是 96*ln(2)=66.54,应该直接用一个含66个decimal型元素的数组保存Exp(1), Exp(2), Exp(3),......Exp(66),占用存储空间66*16=1056 Byte,但是速度是快速幂的几百倍。

public static decimal Exp(decimal d)
{decimal result;decimal nextAdd;int iteration;bool reciprocal;decimal t;reciprocal = d < 0;d = Math.Abs(d);t = decimal.Truncate(d);if (d == 0){result = 1;}else if (d == 1){result = E;}else if (Math.Abs(d) > 1 && t != d){// Split up into integer and fractionalresult = Exp(t) * Exp(d - t);}else if (d == t)   // Integer power{result = ExpBySquaring(E, d);}else                // Fractional power < 1{iteration = 0;nextAdd = 0;result = 0;while (true){if (iteration == 0){nextAdd = 1;               // == Pow(d, 0) / Factorial(0) == 1 / 1 == 1}else{nextAdd *= d / iteration;  // == Pow(d, iteration) / Factorial(iteration)}if (nextAdd == 0) break;result += nextAdd;iteration += 1;}}if (reciprocal) result = 1 / result;return result;
}

下面是DecimalMath的阶乘函数的实现,根本就不应该用循环,应该直接查表实现,因为 28!=3.049×1029>296−1=7.923×1028 =decimal.MaxValue,只需要存28个decimal值就可以了。用循环就慢到可悲了。

public static decimal Factorial(decimal n)
{if (n < 0) throw new ArgumentException("Values less than zero are not supoprted!", "n");if (Decimal.Truncate(n) != n) throw new ArgumentException("Fractional values are not supoprted!", "n");var ret = 1m;for (var i = n; i >= 2; i += -1){ret *= i;}return ret;
}

 

编辑于 2025-01-03 21:29・IP 属地广东

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

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

相关文章

瑞芯微开发板/主板Android配置APK默认开启性能模式方法

本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行…

深入解析 Spring AI 系列:分析 Spring AI 可观测性

今天我们将讨论之前略过的可观测性部分的代码。在这里,我想简单说明一下,当时这部分代码属于必须编写的固定模板,因此在最初的讨论中我们直接跳过了它。虽然这部分代码乍看之下可能显得比较复杂,但实际上它的核心功能只是链路追踪的实现而已。既然如此,接下来我们就不再赘…

【Maldev】Early Bird 注入

一、介绍 QueueUserAPC 用于执行本地 APC 注入,APC 注入利用需要一个已挂起或可警报的线程才能成功执行 Payload。但是很难碰到处于这些状态的线程,尤其是以普通用户权限运行的线程,而Early Bird注入则是利用CreateProcess WinAPI 创建一个挂起的进程,并使用其挂起线程的句…

ThreadPool解析

Thread_Pool 项目解析 简介 ThreadPool 是一个轻量级的 C++ 线程池实现,旨在简化多线程编程。 项目分析 我们首先上github的项目地址:https://github.com/progschj/ThreadPool,然后克隆项目到本地。点开项目的ThrealPool.h文件,查看源码: #ifndef THREAD_POOL_H #define TH…

[docker逃逸] 使用DirtyPipe漏洞逃逸

本文作者CVE-柠檬i CSDN:https://blog.csdn.net/weixin_49125123 博客园:https://www.cnblogs.com/CVE-Lemon 微信公众号:Lemon安全 前言 本文使用代码下载链接:利用CVE-2022-0847 (Dirty Pipe) 实现容器逃逸 (github.com) 由于本人才疏学浅,本文不涉及漏洞原理,仅有复现…

RestAPI实现聚合

API语法 聚合条件与query条件同级别,因此需要使用request.source()来指定聚合条件。聚合的结果解析:@Override public Map<String, List<String>> filters(RequestParams params) {try {// 1.准备RequestSearchRequest request = new SearchRequest("hotel&…

elasticsearch之数据聚合

**聚合(aggregations)**可以让我们极其方便的实现对数据的统计、分析、运算。例如:什么品牌的手机最受欢迎? 这些手机的平均价格、最高价格、最低价格? 这些手机每月的销售情况如何?实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效…

【通讯协议】OPC协议

OPC通讯协议 特点:支持多种数据结构和负责数据类型,需要多的硬件和软件资源,成本较高,安全性较高。 应用场景:连接多个不同工业自动化设备 什么是OPC通讯协议 OPC是英文“OLE for Process Control”的缩写,是工业自动化领域中的一种工业通信标准。它通过定义一些在不同平…

海外泼天流量|浅谈全球化技术架构

本文对海外泼天流量现状做了快速整理,旨在抛砖引玉,促进国内企业在出海过程中,交流如何构建全球化技术架构的落地经验,相信会有越来越多资深人士分享更深层次的实践。 登陆小红书,搜索 refugee,你就能看到一个不一样的小红书。随机点击几个,让大数据记住你,就能持续看到…

绿联网卡

目录1: 安装2:检查3:常见问题网络连接有网卡,状态为已禁用 1: 安装插入电脑 弹窗“Setup.exe”,安装驱动, 如果没有驱动,则找到 Ugreen Wireless进行驱动安装。驱动安装成功后效果2:检查驱动安装好后,u盘插拔一下,观察确定是哪个WLAN3:常见问题 网络连接有网卡,状态为…

kali安装教程

kali和GNOME桌面安装教程 kali下载 https://www.kali.org/get-kali/ 到kali官网,下载镜像安装下载完应该是:kali-linux-2024.4-installer-amd64.iso 然后新建虚拟机选择稍后安装操作系统:选择如图所示操作系统 后面的,我都给的挺多,主要不想它卡,哈哈哈网络选择NAT就行,…