Matrix,Fraction,Point,Linec++
//計算幾何偏差修正Math.EPS=0.00000001;//判斷x的符號Math.cmp=function(x) { if(Math.abs(x)<Math.EPS)return 0; if(x>0){ return 1; }else{ return -1; }}//矩陣類class Matrix { //將一個數組構建成一個矩陣,行Row、列Column constructor(data,Row,Column){ this.data=data||[]; this.Row=Row;//行 this.Column=Column;//豎 } //根據行、列返回矩陣元素 getItem(r,c){ return this.data[r*this.Column+c]||0; } //根據行、列設置矩陣元素 setItem(r,c,item){ this.data[r*this.Column+c]=item; } //換行 swapRow(r1,r2){ for(let c=0;c<this.Column;c++){ const cache=this.getItem(r1,c) this.setItem(r1,c,this.getItem(r2,c)) this.setItem(r2,c,cache); } } oneRowEach(r,callback){ for(let c=0;c<this.Column;c++){ callback(this.getItem(r,c),r,c) } } //按行遍歷矩陣元素,返回元素item,行r,列c rowEach(callback){ for(let r=0;r<this.Row;r++){ for(let c=0;c<this.Column;c++){ callback(this.getItem(r,c),r,c) } } } //按豎遍歷矩陣元素,返回元素item,行r,列c columnEach(callback){ for(let c=0;c<this.Column;c++){ for(let r=0;r<this.Row;r++){ callback(this.getItem(r,c),r,c) } } } //行循環 oneRowMap(r,callback){ this.oneRowEach(r,(item,r,c)=> { this.setItem(r,c,callback(item,r,c)); }) } //按行map矩陣元素 rowMap(callback){ this.rowEach((item,r,c)=> { this.setItem(r,c,callback(item,r,c)); }) } //相加 add(matrix){ if(matrix instanceof Matrix&& this.Row === matrix.Row && this.Column === matrix.Column){ const nMatrix=new Matrix([],this.Row,this.Column) this.rowEach(function (item,r,c) { nMatrix.setItem(r,c,item+matrix.getItem(r,c)) }) return nMatrix; }else{ throw '方法plus 參數錯誤'; } } //相減 sub(matrix){ if(matrix instanceof Matrix&& this.Row === matrix.Row && this.Column === matrix.Column){ const nMatrix=new Matrix([],this.Row,this.Column) this.rowEach(function (item,r,c) { nMatrix.setItem(r,c,item-matrix.getItem(r,c)) }) return nMatrix; }else{ throw '方法minus 參數錯誤'; } } //相乘 multiply(obj){ if(obj instanceof Matrix){ return this.multiplyMatrix(obj) }else if(typeof obj=='number'){ return this.multiplyNumber(obj) }else{ throw 'multiply 輸入的參數類型錯誤'; } } //矩陣與數相乘,返回一個新的矩陣 multiplyNumber(number){ const nMatrix=new Matrix([],this.Row,this.Column) this.rowEach((item,r,c)=> { nMatrix.setItem(r,c,item*number) }) return nMatrix; } //矩陣與矩陣相乘 矩陣A的行必須與矩陣B的列數相等 multiplyMatrix(matrix){ if(this.Row!==matrix.Column){ throw '矩陣A的行必須與矩陣B的列數相等'; } const nMatrix=new Matrix([],this.Row,matrix.Column) for(let r=0;r<this.Row;r++){ for(let mc=0;mc<matrix.Column;mc++){ let num=0; for(let c=0;c<this.Column;c++){ num=num+this.getItem(r,c)*matrix.getItem(c,mc) } nMatrix.setItem(r,mc,num) } } return nMatrix; } //切割 cut({r,c,w,h}){ const mt=this; const nMt=new Matrix([],h,w); nMt.rowMap(function (item,r1,c1) { return mt.getItem(r+r1,c+c1) }) return nMt; } //複製生成一個新的矩陣 clone(){ return new Matrix([].concat(this.data),this.Row,this.Column) } //轉換成字符圖形 toString(){ let str='['; for(let r=0;r<this.Row;r++){ str=str+'\n' for(let c=0;c<this.Column;c++){ if(r==this.Row-1&&c==this.Column-1){ str=str+this.getItem(r,c); }else{ str=str+this.getItem(r,c)+','; } } } str=str+'\n]' return str; }}/*Gauss 消元傳入一個矩陣,傳出結果*/function Gauss(matrix){ let l=[];//是否爲自由元 let ans=[];//存儲解 const n=matrix.Column-1;//解的個數 let res=0,r=0; for(let i=0;i<matrix.Column;i++){ for(let j=r;j<matrix.Row;j++){ if(Math.abs(matrix.getItem(j,i))>Math.EPS){ if(j!==r){ //行交換位置 for(let k=i;k<=n;k++){ const temp1=matrix.getItem(j,k) const temp2=matrix.getItem(r,k) matrix.setItem(j,k,temp2) matrix.setItem(r,k,temp1) } } break; } } // console.log(matrix.toString(),r,i) if(Math.abs(matrix.getItem(r,i)<Math.EPS)){ ++res; console.log('continue') continue; } //方程相減,消除元 for(let j=0;j<matrix.Row;j++){ if(j!==r&&Math.abs(matrix.getItem(j,i))>Math.EPS){ let tmp=matrix.getItem(j,i)/matrix.getItem(r,i); for(let k=i;k<=n;k++){ const item=matrix.getItem(j,k)-tmp*matrix.getItem(r,k) matrix.setItem(j,k,item) } } } l[i]=true; r++; } //輸出答案 for(let i=0;i<n;i++){ if(l[i]){ for(let j=0;j<n;j++){ if(Math.abs(matrix.getItem(j,i))>0){ ans[i]=matrix.getItem(j,n)/a.getItem(j,i) } } } } return ans;}//將一個矩陣轉換成上三角矩陣function upperMatrix(oriMatrix) { const matrix=oriMatrix.clone(); let r=0; //生成上三角矩陣 for(let i=0;i<matrix.Row;i++){ //循環行 for(let j=r;j<matrix.Row;j++){ if(Math.abs(matrix.getItem(j,i))>Math.EPS){ if(j!==r){ //行交換位置 matrix.swapRow(j,r) } break; } } if(Math.abs(matrix.getItem(r,i)<Math.EPS)){ continue; } //方程相減,消除元 for(let j=0;j<matrix.Row;j++){ if(j!==r&&Math.abs(matrix.getItem(j,i))>Math.EPS){ let tmp=matrix.getItem(j,i)/matrix.getItem(r,i); for(let k=i;k<matrix.Column;k++){ const item=matrix.getItem(j,k)-tmp*matrix.getItem(r,k) matrix.setItem(j,k,item) } } } r++; } return matrix}//求矩陣的逆function Inverse(matrix){ if(matrix.Row!==matrix.Column){ throw '矩陣的行與列須要相等'; } const N=matrix.Row; //方程矩陣A const A = new Matrix([],N,2*N); for(let r=0;r<N;r++){ for(let c=0;c<N;c++){ A.setItem(r,c,matrix.getItem(r,c)) } } for(let r=0;r<N;r++){ for(let c=N;c<N*2;c++){ if(r===c-N){ A.setItem(r,c,1) }else{ A.setItem(r,c,0) } } } //換成上三角矩陣 const B=upperMatrix(A) //左邊轉成單位矩陣 for(let i=0;i<N;i++){ if(Math.abs(B.getItem(i,i))!==1){ for(let k=N;k<2*N;k++){ B.setItem(i,k,B.getItem(i,k)/B.getItem(i,i)) } B.setItem(i,i,1) } } //輸出結果 const C = new Matrix([],N,N); C.rowMap(function (item,r,c) { return B.getItem(r,c+N); }) return C;}//歐幾里得算法 求兩個數a、b的最大公約數function gcd(a,b){ return b===0?a:gcd(b,a%b)}//分數類 分子,分母class Fraction{ constructor(num=0,den=1){ if(den<0){ num=-num; den=-den; } if(den===0){ throw '分母不能爲0' } let g=gcd(Math.abs(num),den) this.num=num/g; this.den=den/g; } //加 add(o){ return new Fraction(this.num*o.den+this.den*o.num,this.den*o.den) } //減 sub(o){ return new Fraction(this.num*o.den-this.den*o.num,this.den*o.den) } //乘 multiply(o){ return new Fraction(this.num*o.num,this.den*o.den); } //除 divide(o){ return new Fraction(this.num*o.den,this.den*o.num); } //小於 lessThan(o){ return this.num*o.den<this.den*o.num; } //等於 equal(o){ return this.num*o.den===this.den*o.num; }}//點類function Point(x,y) { if(this instanceof Point){ if(Math.cmp(x.toFixed(2)-x)==0){ x=Number(x.toFixed(2)); } if(Math.cmp(y.toFixed(2)-y)==0){ y=Number(y.toFixed(2)); } this.x=x; this.y=y; }else{ return new Point(x,y) }}//向量的模長Point.prototype.norm=function(){ return Math.sqrt(this.x*this.x+this.y*this.y);}// 加Point.add=function(a,b){ return new Point(a.x+b.x,a.y+b.y)}// 減Point.sub=function(a,b){ return new Point(a.x-b.x,a.y-b.y);}// 等於Point.equals=function(a,b){ return Math.cmp(a.x-b.x)===0&&Math.cmp(a.y-b.y)===0;}//乘 向量與數字Point.multiply=function(a,b){ if(a instanceof Point&&typeof b=='number'){ return Point(a.x*b,a.y*b) } if(b instanceof Point&&typeof a=='number'){ return Point(a*b.x,a*b.y) }}//除 向量與數字Point.divide=function(a,b){ return Point(a.x/b,a.y/b)}//向量的叉積Point.det=function (a,b) { return a.x*b.y-a.y*b.x;}//向量的點積Point.dot=function (a,b) { return a.x*b.x+a.y*b.y;}//兩個點的距離Point.dist=function (a,b) { return Point.sub(a,b).norm()}//逆時針旋轉,a爲弧度Point.rotate=function (p,A) { if(A===0){return p;} const tx=p.x; const ty=p.y; return Point(tx*Math.cos(A)-ty*Math.sin(A),tx*Math.sin(A)+ty*Math.cos(A))}//計算幾何線段類function Line(a,b) { if(this instanceof Line){ this.a=a; this.b=b; }else{ if(a instanceof Point&&b instanceof Point){ return new Line(a,b) }else{ throw 'Line 參數錯誤' } }}//點p到線段s、t的距離Line.dis_point_segment=function(p,s,t){ if(Math.cmp(Point.dot(Point.sub(p,s),Point.sub(t,s)))<0){ return Point.sub(p,s).norm() } if(Math.cmp(Point.dot(Point.sub(p,t),Point.sub(s,t)))<0){ return Point.sub(p,t).norm() } return Math.abs(Point.det(Point.sub(s,p),Point.sub(t,p))/Point.dist(s,t))}//點到線段的垂足Line.pointProjLine=function(p,s,t){ const r=Point.dot(Point.sub(t,s),Point.sub(p,s))/Point.dot(Point.sub(t,s),Point.sub(t,s)) return Point.add(s,Point.multiply(r,Point.sub(t,s)))}//點是否在線段上Line.pointOnSegment=function(p,s,t){ return Math.cmp(Point.det(Point.sub(p,s),Point.sub(t,s)))===0&&Math.cmp(Point.det(Point.sub(p,s),Point.sub(p,t)))<=0}//判斷線段a、b是否平行,a、b 爲lineLine.parallel=function (a,b) { return !Math.cmp(Point.det(Point.sub(a.a,a.b),Point.sub(b.a,b.b)))}//判斷線段a、b是否相交,返回交點Line.lineMakePoint=function (a,b) { if(Line.parallel(a,b)){ return false; } const s1=Point.det(Point.sub(a.a,b.a),Point.sub(b.b,b.a)); const s2=Point.det(Point.sub(a.b,b.a),Point.sub(b.b,b.a)); return Point.divide(Point.sub(Point.multiply(s1,a.b),Point.multiply(s2,a.a)),s1-s2);}// 將直線a沿法向量方向平移距離len獲得的直線Line.moveD=function (a,len) { let d=Point.sub(a.b,a.a) d=Point.divide(d,d.norm()); d=Point.rotate(d,Math.PI/2); return Line(Point.add(a.a,Point.multiply(d,len)),Point.add(a.b,Point.multiply(d,len)))}module.exports={ Matrix, Fraction, Point, Line}