本人正在写矩阵史诗级玩法系列博客,写到求二元二次方程组的地方来了,消元后最高会生成一元四次方程,而这个求根公式虽然成熟,但代码量也不少,所以单独封装成工具类。

本不打算讲解的,但考虑到有的朋友可能没接触过复数,或者说虽然接触过复数但已经忘得一干二净,那这里我就简单说一下好了。在实数范围内,负数是没办法开平方的,而且对于一些简单的曲线求交(二次或以下)问题来说,出现要对负数开平方的话就意味着没有交点。然而在一些复杂的方程求解中,负数开平方也许是一个重要的中间过程,走完中间过程之后可能会得到实数根,所以数学家们定义了一个新的数i,名为虚数单位,并规定i^2=-1,同时可以使其参与到跟实数的四则运算中且交换律结合律等仍然成立。然后,实数1+实数2*i就构成了复数a+bi。

四次方程求解经常用到复数,其结构为实数部分一个系数(实部),虚数部分一个系数(虚部),代码如下。

function ComplexNum(real, image)
{this.real = isNaN(real) ? 0 : real;this.image = isNaN(image) ? 0 : image;
}

复数本身有四则运算,所以完整的代码我把这些运算的实现都写进去了,加减最好理解,乘法稍有难度,除法和开方需要套路,如果觉得看不懂,那可以自己百度或者查阅相关的高中数学教材来补补这方面的知识。

ComplexNum.js

function ComplexNum(real, image)
{this.real = isNaN(real) ? 0 : real;this.image = isNaN(image) ? 0 : image;/*** 设置复数的值 * @param reala 复数的实部* @param imagea 复数的虚部* */function setTo(reala, imagea){this.real = reala;this.image = imagea;}this.setTo = setTo;/*** 复数相加 * @param complex 要与其相加的复数* @return * */function add(complex){return new ComplexNum(this.real + complex.real, this.image + complex.image);}this.add = add;/*** 为了使用上方便一点,此处加上与实数相加的方法 * @param num 要与其相加的数字* @return * */function addNumber(num){return new ComplexNum(this.real + num, this.image);}this.addNumber = addNumber;/*** 复数相减* @param complex 要与其相减的复数* @return * */function subtract(complex){return new ComplexNum(this.real - complex.real, this.image - complex.image);}this.subtract = subtract;/*** 为了使用上方便一点,此处加上与实数相减的方法 * @param num 要与其相减的数字* @return * */function subtractNumber(num){return new ComplexNum(this.real - num, image);}this.subtractNumber = subtractNumber;/*** 复数乘法 (运算法则是利用多项式乘法进行展开得到)* @param complexNum 要与其相乘的复数* @return * */function multiply(complexNum){return new ComplexNum(this.real * complexNum.real - this.image * complexNum.image, this.image * complexNum.real + this.real * complexNum.image);}this.multiply = multiply;/*** 为了使用上方便一点,此处加上与实数相乘的方法 * @param num 要与其相乘的数字* @return * */function multiplyNumber(num){return new ComplexNum(this.real * num, this.image * num);}this.multiplyNumber = multiplyNumber;/*** 复数除法 (运算法则是通过平方差公式,分子分母同时乘以分母的共轭复数以去除分母中的虚部,然后就利用乘法法则进行计算)* @param complexNum 要与其相除的复数* @return * */function divide(n){//分母化为实数var denominator = n.real * n.real + n.image * n.image;//分子也乘以同样的复数,并除以分母即得最终结果return new ComplexNum((this.real * n.real + this.image * n.image) / denominator, (this.image * n.real - this.real * n.image) / denominator);}this.divide = divide;/*** 为了使用上方便一点,此处加上与实数相除的方法 * @param num 要与其相除的数字* @return * */function divideNumber(num){return new ComplexNum(this.real / num, this.image / num);}this.divideNumber = divideNumber;/*** 开平方运算* @return * */function squareRoot(){         return this.getRoot(2);}this.squareRoot = squareRoot;/*** 开立方运算 * @return * */function cubicRoot(){return this.getRoot(3);}this.cubicRoot = cubicRoot;/*** 开任意整数次方的运算 * @param times* @return * */function getRoot(times){var vec = [];           //复数开方运算的原理是把辐角根据次数进行平分var degree = this.degree;degree /= times;//然后多个方根平分360度,所以需要算出每个方根之间的辐角间隔var degreeUnit = 360 / times;//复数长度(模)直接开方即可var lengthRoot = Math.pow(this.length, 1 / times);var cosDic = AngleUtil.getCosDic();var sinDic = AngleUtil.getSinDic();//然后就能通过循环生成所有开方结果for(var i = 0; i < times; i ++){var currentDegree = (degree + i * degreeUnit);var currentAngle = currentDegree * Math.PI / 180;var cos = isNaN(cosDic[currentDegree]) ? Math.cos(currentAngle) : cosDic[currentDegree];var sin = isNaN(sinDic[currentDegree]) ? Math.sin(currentAngle) : sinDic[currentDegree];//trace(lengthRoot * cos, lengthRoot * sin);vec.push(new ComplexNum(lengthRoot * cos, lengthRoot * sin));}return vec;}this.getRoot = getRoot;/*** 复数的辐角主值(复数所在点与坐标连线跟水平线的夹角),以弧度为单位* 必要时可用degree属性可以更有效避免边缘位置的浮点误差引发的错误* @see degree()* */    Object.defineProperty(this, "angle", {get: getAngle});function getAngle(){//由于复数开方基于角度,所以一旦有浮点误差就会导致角度在边缘位置出现突跃导致严重错误,所以遇到浮点误差的话,我会强制将角度设置为0if(Math.abs(this.image) < 0.0000001){return this.real > 0 ? 0 : Math.PI; }else if(Math.abs(this.real) < 0.0000001){return this.image > 0 ? Math.PI * 0.5 : (this.image == 0 ? 0 : - Math.PI * 0.5);}else{return Math.atan2(this.image, this.real);}}/*** 复数的辐角主值,以角度值表示(跟弧度相比,该方法能更有效地避免边缘位置的浮点误差引发的错误)* */    Object.defineProperty(this, "degree", {get: getDegree});   function getDegree(){//由于复数开方基于角度,所以一旦有浮点误差就会导致角度在边缘位置出现突跃导致严重错误,所以遇到浮点误差的话,我会强制将角度设置为0if(Math.abs(this.image) < 0.0000001){return this.real > 0 ? 0 : 180; }else if(Math.abs(this.real) < 0.0000001){return this.image > 0 ? 90 : (this.image == 0 ? 0 : 270);}else{return Math.atan2(this.image, this.real) / Math.PI * 180;}}/*** 复数的模(长度) * @return * */Object.defineProperty(this, "length", {get: getLength});function getLength(){return Math.sqrt(this.real * this.real + this.image * this.image);}/*** 返回当前复数的一个副本 * @return * */function clone(){return new ComplexNum(this.real, this.image);}this.clone = clone;function toString(){var realStr = String((this.real != 0 || this.image == 0) ? real : "");var imageStr = (this.image == 0) ? "" : ((this.image < 0 ? ("-" + (-this.image)) : ((realStr == "" ? "" : "+") + this.image)) + "i");return realStr + imageStr;}this.toString = toString;
}

复数乘法跟三角函数密切相关,而且60度倍数的三角函数出现频率特别高,所以我又定义了一些常量,以防直接计算累积不必要的浮点误差。

ComplexConsts.js

function ComplexConsts()
{}/*** OMEGA是模为1,幅角主值等于120度的复数,它是1开三次方的结果,在解3次和4次方程中非常常用 * OMEGA就是希腊字母里最像w的那个* */
ComplexConsts.OMEGA = new ComplexNum(1 / 2, Math.sqrt(3) / 2);/*** 理论上OMEGA的平方可以用两个OMEGA相乘得到,但由于容易产生浮点误差,加上数值固定,因此也直接做成常量*  */
ComplexConsts.OMEGA_SQUARE = new ComplexNum(1 / 2, Math.sqrt(3) / 2);      /*** OMEGA的3次方恰好等于实数1,就最好不要再自己用三个OMEGA来相乘了*  */
ComplexConsts.OMEGA_CUBIC = new ComplexNum(1, 0);/*** OMEGA的0次方,纯属为了让代码清晰而定义的常量*  */
ComplexConsts.OMEGA_ZERO = new ComplexNum(1, 0);/*** 虚数单位i*  */
ComplexConsts.I = new ComplexNum(0, 1);

AngleUtil.js

function AngleUtil()
{}AngleUtil.getCosDic = function()
{if(AngleUtil._cosDic == undefined){AngleUtil._cosDic = [];AngleUtil._cosDic[60] = AngleUtil._cosDic[-60] = AngleUtil._cosDic[300] = AngleUtil._cosDic[-300] = 0.5;AngleUtil._cosDic[120] = AngleUtil._cosDic[-120] = AngleUtil._cosDic[240] = AngleUtil._cosDic[-240] = -0.5;AngleUtil._cosDic[0] = AngleUtil._cosDic[360] = 1;AngleUtil._cosDic[180] = AngleUtil._cosDic[-180] = -1;AngleUtil._cosDic[90] = AngleUtil._cosDic[270] = AngleUtil._cosDic[-90] = AngleUtil._cosDic[-270] = 0; }return AngleUtil._cosDic;
}AngleUtil.getSinDic = function()
{if(AngleUtil._sinDic == undefined){AngleUtil._sinDic = [];AngleUtil._sinDic[30] = AngleUtil._sinDic[150] = AngleUtil._sinDic[-330] = AngleUtil._sinDic[-210] = 0.5;AngleUtil._sinDic[-30] = AngleUtil._sinDic[-150] = AngleUtil._sinDic[210] = AngleUtil._sinDic[330] = -0.5;AngleUtil._sinDic[90] = AngleUtil._sinDic[-270] = 1;AngleUtil._sinDic[270] = AngleUtil._sinDic[-90] = -1;AngleUtil._sinDic[180] = AngleUtil._sinDic[-180] = AngleUtil._sinDic[0] = AngleUtil._sinDic[360] = 0;}return AngleUtil._sinDic;
}

真没想到,弄完这些辅助类就把文章撑那么长了。那我就分开两篇写好了。

其实我可以弄附件,但有些东西没测好,附件又似乎不太好改,那就还是直接贴吧。

下篇我贴出解方程的核心类。

四次方程根式解+四次以上方程近似解的js实现代码(上)——复数类+复数常量+三角函数简表相关推荐

  1. 为何高于四次的方程没有根式解?

    又见于知乎为何从五次方程开始就没有加减乘除开方的求根公式了?的回答. 可能不少人上学的时候都曾对这个问题感兴趣,至少我是一个.无意间在知乎上看到这个问题,又勾起了自己的兴趣,然后就上网.找书钻研了一番 ...

  2. matlab 二分法求方程近似解

    二分法求方程近似解 %用二分法求方程x^2-2=0近似解 function result=approximate_solution(d,a,b) %精度值d,初始值a,b f=@(x)x^2-2;%匿 ...

  3. linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  4. Android Studio 插件开发详解四:填坑

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78265540 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  5. springboot 详解 (四)redis filter

    ---------------------------------------------------------------------------------------------------- ...

  6. 数据结构--图(Graph)详解(四)

    数据结构–图(Graph)详解(四) 文章目录 数据结构--图(Graph)详解(四) 一.图中几个NB的算法 1.普里姆算法(Prim算法)求最小生成树 2.克鲁斯卡尔算法(Kruskal算法)求最 ...

  7. .NET DLL 保护措施详解(四)各操作系统运行情况

    我准备了WEB应用程序及WinForm应用程序,分别在WIN SERVER 2012/2008/2003.Win7/10上实测,以下为实测结果截图: 2012 2008 2003 WIN7 WIN10 ...

  8. mybatis 鉴别其_MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询

    MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询 白玉 IT哈哈 支持的 JDBC 类型 为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 ...

  9. 系统集成管理师2011下半年软考透解 四

    本文讲的是 :  系统集成管理师2011下半年软考透解 四  , 1 . 项目建议书应该包括的核心内容可不包括 ( ) . A.项目建设必需的条件 B.项目的必要性 C.项目的风险预测及应对措施 D. ...

最新文章

  1. mybatis学习笔记
  2. 使用Maven搭建一个Web项目
  3. poj2442Sequence(优先队列)
  4. 东北大哥在线反套路hhhhhh | 今日最佳
  5. 泉州经贸职业技术学院计算机系,部门简介-泉州经贸职业技术学院网络电教中心...
  6. 如何使用python导入mat格式的数据并整理
  7. 初学Python——字符串相关操作
  8. 外挂摄像头?iPhone XI新概念图曝光:差点就信了...
  9. Redis分布式锁的正确实现方式
  10. UE4官方文档UI学习:6. UMG 使用菜单锚显示弹出菜单
  11. 使用广告终结者屏蔽页面的任意部分
  12. 富士通750打印机驱动步骤_非网络激光打印机如何进行网络打印?
  13. linux git ssh目录权限,Git SSH Key的配置问题
  14. Photoshop对图片加边框
  15. 数据库ALTER语句使用
  16. 轻量级微信动态活码生成管理源码
  17. FOne CodeSec代码泄露检测工具
  18. alt在计算机中代表什么,计算机中alt+enter是什么快捷键
  19. 高德地图交通态势爬取
  20. 浅谈汽车OTA的现状与未来发展趋势

热门文章

  1. Python数字图像处理---1.1图像的像素格式与图像读写
  2. Linux如何访问网络 - 管理Linux的联网
  3. 新进入一个研发团队,或者项目组要做什么?
  4. zblog php 优化,Zblog单页面优化,Zblog后台地址修改
  5. spring cloudAlibaba gateway网关报错,显示无法找到注册中心注册自己。
  6. ARM通用中断控制器GIC之中断处理状态机 Interrupt handling state machine
  7. 用粒子群解决有约束的最优解问题
  8. 如何关闭windows杀毒软件
  9. 开源消息总线eventBus学习
  10. 软件构架实践 第2版 学习笔记