UNPKG

rlab

Version:

Javascript scientific library like R

578 lines (481 loc) 14.7 kB
// module : Field & Group Theory // 注意: 箭頭函數會自動將 this 變數綁定到其定義時所在的物件,因此以下很多地方不能用箭頭函數。 // 參考: https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions var N, A, S, R; module.exports = A = S = N = R = require("./set"); // var N = R.Number = {} // var A = R.Algebra = {} // var S = R.Set; // A.Integer=require("./integer"); var eq = R.eq, extend=R.extend; // ========== Group & Field Property ================ // ref:https://en.wikipedia.org/wiki/Group_(mathematics) // 封閉性:For all a, b in G, a • b, is also in G A.closability=function(set, op, a, b) { return set.has(op(a,b)) } // 結合性:For all a, b and c in G, (a • b) • c = a • (b • c). A.associativity=function(set, op, a, b, c) { return eq(op(op(a,b),c), op(a,op(b,c))) } // 單位元素:Identity element A.identity=function(set, op, e, a) { return eq(op(e,a),a) } // 反元素:Inverse element A.inversability=function(e, inv, a) { return eq(op(a,inv(a)),e); } // 交換性: A.commutative=function(op,a,b) { return eq(op(a,b),op(b,a)); } // 左分配律: A.ldistribute=function(add, mul, a,b,c) { return eq(mul(a, add(b,c)), add(mul(a,b), mul(a,c))); } // 右分配律: A.rdistribute=function(add, mul, a,b,c) { return A.ldistribute(b,c,a); } // 封閉性 A.isClose=function(g) { var a=g.random(), b=g.random(); return A.closability(g, a, b); } // 結合性 A.isAssociate=function(g) { var a=g.random(), b=g.random(), c=g.random(); return A.associativity(g, g.op, a, b, c); } // 單位元素 A.isIdentify=function(g) { var a=g.random(), b=g.random(), c=g.random(); return A.associativity(g, g.op, a, b, c); } // 反元素 A.isInversable=function(g) { var a=g.random(), b=g.random(), c=g.random(); return A.associativity(g, g.op, a, b, c); } // 左分配律 A.isLeftDistribute=function(f, m, add, mul) { var a=f.random(), b=g.random(), c=g.random(); return A.ldistribute(add, mul, a, b, c); } // 右分配律 A.isRightDistribute=function(f, m, add, mul) { var a=f.random(), b=f.random(), c=g.random(); return A.rdistribute(add, mul, a, b, c); } // 原群 A.isMagma = function(g) { return A.isClose(g) } // 半群 A.isSemiGroup = function(g) { return A.isClose(g) && A.isAssociate(g) } // 么半群 A.isMonoid = function(g) { return A.isSemiGroup(g) && A.isIdentify(g) } // 群 A.isGroup = function(g) { return A.isMonoid(g) && A.isInversable(g) } // 交換群 A.isAbelGroup = function(g) { return A.isGroup(g) && A.isCommutable(g) } // 擬群 A.isQuasiGroup = function(g) { return A.isClose(g) && A.isInversable(g) } // 環群 A.isLoop = function(g) { return A.isQuasiGroup(g) && A.isIdentify(g) } // 環 : 沒有乘法反元素的體。 A.isRing = function(f) { return isAbelGroup(f.addSet) && isSemiGroup(f.mulSet); } // 體 : 具有加減乘除結構的集合 A.isField = function(f) { return isAbelGroup(f.addSet) && isAbelGroup(f.mulSet); } // 模 : 向量的抽象化 A.isModule = function(r,m) { return A.isRing(r)&&A.isAbelGroup(m); // &&A.isLeftDistribute(r.madd, m.add); } // ========== Group ================= A.SemiGroup={ power:function(x,n) { var p=this.e; for (var i=0;i<n;i++) { p=this.op(p,x); } return p; }, leftCoset:function(g, H) { var set = new Set(); for (var i in H) set.add(this.op(g,H[i])); return set; }, rightCoset:function(H, g) { var set = new Set(); for (var i in H) set.add(this.op(g,H[i])); return set; }, } // 半群 A.Monoid={} // 么半群 extend(A.Monoid, A.SemiGroup) A.Group={ // inv:function(x) { return x.inv() }, } extend(A.Group, A.Monoid); A.AbelGroup = {} // 交換群 extend(A.AbelGroup, A.Group); // PermutationGroup A.PermutationGroup={ op:function(x,y) { var z = []; for (var i in x) z[i] = y[x[i]]; return z; }, inv:function(x) { var nx = []; for (var i in x) { nx[x[i]] = i; } return nx; }, } extend(A.PermutationGroup, A.Group); // 循環群 Cyclic Group : a group that is generated by a single element (g) A.CyclicGroup={ G:[], // g:g, op:function(x,y) {}, inv:function(x) {}, create(g) { var t = e; for (var i=0; !t.eq(e); i++) { G[i]=t; t=op(g,G[i]); } } } extend(A.CyclicGroup, A.Group); // NormalSubGroup : 正規子群 A.isNormalSubGroup=function(G,H,g,h) { return H.has(G.op(G.op(g,h), G.inv(g))); } // 商群 Quotent Group : aggregating similar elements of a larger group using an equivalence relation that preserves the group structure A.QuotentGroup={ eq:function(x,y) {}, op:function(x,y) {}, inv:function(x) {}, } extend(A.QuotentGroup, A.Group); // Normal SubGroup : gH = Hg // https://en.wikipedia.org/wiki/Normal_subgroup A.NormalSubGroup={ op:function(x,y) {}, inv:function(x) {}, } extend(A.NormalSubGroup, A.Group); // 群同構第一定理: 給定 GG和 G ′ 兩個群,和 f : G → G ′ 群同態。則 Ker ⁡ f 是一個 G 的正規子群。 // 群同構第二定理:給定群 G 、其正規子群 N、其子群 H,則 N ∩ H 是 H 的正規子群,且我們有群同構如下: H / ( H ∩ N ) ≃ H N / N // 群同構第三定理: 給定群 G, N 和 M,M 為 G 的正規子群,滿足 M 包含於 N ,則 N / M 是 G / M 的正規子群,且有如下的群同構: ( G / M ) / ( N / M ) ≃ G / N . // ========== Field ================= A.Ring = { // Ring (環) : 可能沒有乘法單位元素和反元素的 Field neg:function(x) { return this.addSet.inv(x) }, add:function(x,y) { return this.addSet.op(x,y) }, sub:function(x,y) { return this.addSet.op(x, this.addSet.inv(y)) }, mul:function(x,y) { return this.mulSet.op(x,y) }, power:function(x,n) { return this.mulSet.power(x,n) }, init:function(addSet, mulSet) { this.addSet = addSet; this.mulSet = mulSet; this.zero = addSet.e; }, // Ideal (理想): 子環,且 i·r ∈ I (左理想), r·i ∈ I (右理想) ideal:function(i) {}, // https://en.wikipedia.org/wiki/Ideal_(ring_theory) } // (F,+,*) : (F,+), (F-0,*) 均為交換群。 A.Field = { div:function(x,y) { return this.mulSet.op(x, this.mulSet.inv(y)) }, inv:function(x) { return this.mulSet.inv(x) }, init:function(addSet, mulSet) { A.Ring.init.call(this, addSet, mulSet); this.one = mulSet.e; }, } extend(A.Field, A.Ring); // https://zh-classical.wikipedia.org/wiki/%E6%A8%A1_(%E4%BB%A3%E6%95%B8) A.Module = A.Field;// Module(模) : (R +) is Ring, (R × M → M) // ========== Float Field ================= A.FloatAddGroup={ e:0, op:function(x,y) { return x+y }, inv:function(x) { return -x}, } extend(A.FloatAddGroup, A.AbelGroup, S.Float); A.FloatMulGroup={ e:1, op:function(x,y) { return x*y }, inv:function(x) { return 1/x}, } extend(A.FloatMulGroup, A.AbelGroup, S.Float); A.FloatField=extend({}, A.Field, S.Float); A.FloatField.init(A.FloatAddGroup, A.FloatMulGroup); // ========== Finite Field ================= A.FiniteAddGroup={ e:0, op:function(x,y) { return (x+y)%this.n }, inv:function(x) { return (this.n-x) } } extend(A.FiniteAddGroup, A.AbelGroup); A.FiniteMulGroup={ e:1, op:function(x,y) { return (x*y)%this.n }, inv:function(x) { return this.invMap[x] }, setOrder:function(n) { this.n = n; let invMap = new Map(); for (var x=1; x<n; x++) { var y = this.op(x,x); invMap.set(x,y); } this.invMap = invMap; } } extend(A.FiniteMulGroup, A.AbelGroup); A.FiniteField=extend({}, A.Field); A.FiniteField.create=function(n) { var finiteField = extend(S.Finite(n), A.FiniteField); var addSet = extend(S.Finite(n), {n:n}, A.FiniteAddGroup); var mulSet = extend(S.Finite(n), {n:n}, A.FiniteMulGroup); finiteField.init(addSet, mulSet); mulSet.setOrder(n); return finiteField; } class MathObj { constructor() {} str() { return this.toString() } } A.MathObj = MathObj; // =========== Field Object ============== class FieldObj extends MathObj { constructor(field) { super(); this.field = field; var p = Object.getPrototypeOf(this); p.zero = field.zero; p.one = field.one; } add(y) { return this.field.add(this,y) } mul(y) { return this.field.mul(this,y) } neg() { return this.field.neg(this) } inv() { return this.field.inv(this) } div(y) { return this.field.div(this,y) } sub(y) { return this.field.sub(this,y) } power(n) { return this.field.power(this,n) } isZero(x) { return this.field.isZero(this) } isOne(x) { return this.field.isOne(this) } eq(y) { return this.field.eq(this, y) } neq(y) { return this.field.neq(this, y) } mod(y) { return this.field.mod(this, y) } } A.FieldObj = FieldObj; // =========== Complex Field ============== A.ComplexField=extend({}, A.Field); class Complex extends FieldObj { constructor(a,b) { super(A.ComplexField); this.a = a; this.b = b; } conj() { return new Complex(this.a, -1*this.b); } str() { var op = (this.b<0)?'':'+'; return R.nstr(this.a)+op+R.nstr(this.b)+'i'; } toString() { return this.str() } toPolar() { var a=this.a, b=this.b, r=Math.sqrt(a*a+b*b); var theta = Math.acos(a/r); return {r:r, theta:theta} } power(k) { var p = this.toPolar(); return Complex.polarToComplex(Math.pow(p.r,k), k*p.theta); } sqrt() { return this.power(1/2); } static toComplex(o) { if (R.isFloat(o)) return new Complex(o, 0); else if (o instanceof Complex) return o; console.log('o=', o); throw Error('toComplex fail'); } static polarToComplex(r,theta) { var a=r*Math.cos(theta), b=r*Math.sin(theta); return new Complex(a, b); } static parse(s) { var m = s.match(/^([^\+]*)(\+(.*))?$/); var a = parseFloat(m[1]); var b = typeof m[3]==='undefined'?1:parseFloat(m[3]); return new Complex(a, b) } } R.Complex = Complex; R.polarToComplex = Complex.polarToComplex; R.toComplex = Complex.toComplex; var C = (a,b)=>new Complex(a,b); var enumComplex=[C(1,0),C(0,1),C(0,0),C(2,3),C(-5,4),C(-10,-7)]; S.ComplexSet=new S.create(enumComplex); S.ComplexSet.has = (a)=>a instanceof Complex; A.ComplexAddGroup={ e:new Complex(0,0), op:function(x,y) { x = Complex.toComplex(x), y=Complex.toComplex(y); return new Complex(x.a+y.a, x.b+y.b) }, inv:function(x) { x = Complex.toComplex(x); return new Complex(-x.a, -x.b) } } extend(A.ComplexAddGroup, A.AbelGroup, S.ComplexSet); A.ComplexMulGroup={ e:new Complex(1,0), op:function(x,y) { x = Complex.toComplex(x), y=Complex.toComplex(y); return new Complex(x.a*y.a-x.b*y.b, x.a*y.b+x.b*y.a); }, inv:function(x) { x = Complex.toComplex(x); var a=x.a,b=x.b, r=a*a+b*b; return new Complex(a/r, -b/r); } } extend(A.ComplexMulGroup, A.AbelGroup, S.ComplexSet); extend(A.ComplexField, S.ComplexSet); A.ComplexField.init(A.ComplexAddGroup, A.ComplexMulGroup); // =========== Ratio Field ============== A.RatioField=extend({}, A.Field); class Ratio extends FieldObj { constructor(a,b) { super(A.RatioField); this.a = a; this.b = b; } reduce() { var a = this.a, b=this.b; var c = R.gcd(a, b); return new Ratio(a/c, b/c); } toString() { return this.a+'/'+this.b; } static parse(s) { var m = s.match(/^(\d+)(\/(\d+))?$/); var a = parseInt(m[1]); var b = typeof m[3]==='undefined'?1:parseInt(m[3]); return new Ratio(a, b) } } N.Ratio = Ratio; A.RatioAddGroup={ e:new Ratio(0,1), op:function(x,y) { return new Ratio(x.a*y.b+x.b*y.a, x.b*y.b) }, inv:function(x) { return new Ratio(-x.a, x.b); }, } extend(A.RatioAddGroup, A.AbelGroup); A.RatioMulGroup={ e:new Ratio(1,1), op:function(x,y) { return new Ratio(x.a*y.a, x.b*y.b) }, inv:function(x) { return new Ratio(x.b, x.a) }, } extend(A.RatioMulGroup, A.AbelGroup); A.RatioField.init(A.RatioAddGroup, A.RatioMulGroup); // Function R.isField = A.isField=function(x) { return R.isBool(x) || R.isNumber(x) || x instanceof A.FieldObj; } R.parse = N.parse = function(s) { if (s.indexOf(';')>=0) { var m = split(s, ";"), matrix; for (var i=0; i<m.length; i++) { matrix[i] = N.parse(m[i]); } return matrix; } if (s.indexOf(',')>=0) { var a = split(s, ","), array; for (var i=0; i<a.length; i++) { array[i] = N.parse(a[i]); } return array; } else if (s.indexOf('/')>=0) return N.Ratio.parse(s); else if (s.indexOf('i')>=0) return N.Complex.parse(s); else { return parseFloat(s); } } N.op = function(op,x,y) { if (y instanceof N.Complex) { x = x.toComplex(); switch (op) { case 'add':return x.add(y); case 'sub':return x.sub(y); case 'mul':return x.mul(y); case 'div':return x.div(y); case 'sqrt':return x.sqrt(); case 'power':return x.power(y); } } else if (y instanceof Array) { switch (op) { case 'add':return y.add(x); case 'sub':return y.sub(x).neg(); case 'mul':return y.mul(x); case 'div':return y.div(x).inv(); } } else { switch (op) { case 'add':return x+y; case 'sub':return x-y; case 'mul':return x*y; case 'div':return x/y; case 'sqrt':return (x>=0)?Math.sqrt(x):x.toComplex().sqrt(x); case 'power':return (y>=0)?Math.pow(x,y):x.toComplex().power(x,y); } } throw Error('N.op:invalid '+op); } N.neg=function(x) { return -x } N.inv=function(x) { return 1/x } N.add=function(x,y) { return N.op('add', x, y) } N.sub=function(x,y) { return N.op('sub', x, y) } N.mul=function(x,y) { return N.op('mul', x, y) } N.div=function(x,y) { return N.op('div', x, y) } N.mod=function(x,y) { return x%y } N.sqrt=function(x) { return N.op('sqrt', x) } N.power=function(x,y) { return N.op('power', x, y) } N.eval=function(x) { return f(x) } R.mixThisMap(Number.prototype, N, { add:'add', sub:'sub', mul:'mul', div:'div', mod:'mod', neg:'neg', inv:'inv', sqrt:'sqrt', power:'power', eval:'eval', toComplex:'toComplex', }); R.mixThis(Number.prototype, Math, [ 'log', 'exp', 'abs', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'ceil', 'floor', 'round', ]);