
MQL5 中的范畴论 (第 1 部分)
概述
范畴论是数学的一个分支,由艾伦伯格(Eilenberg)和麦克莱恩(Mac Lane)于 1940 年代赋予生命。 当爱因斯坦的相对论导致人们意识到没有单一视角来看待世界,但真正的力量在于能够在这些不同的视角之间进行转换,由此诞生了这一思路。因此,它作为一种分类形式,它并不详述被分类的对象本身,而是关注这些对象的内部关系,从而提炼出非常简洁的定义。 这种方式的重要性在于,从一个研究领域或学科中获取的概念可以是直观的,甚至可应用于不同的领域。 然而,在一开始,其主要用途是研究几何和代数之间的联系。 时至今日,这些用途显然超越了数学,所有内容对于本系列文章来说过于广泛,因此我们只详述它对于采用 MQL 编程语言的交易者的可能用途。
以 MQL5 编写智能系统,并进行交易的背景下,范畴论可用于分析和理解不同交易策略、工具和行情条件之间的关系。 它可以帮助交易者识别其交易系统中的常见形态和结构,并开发更加通用和灵活的交易算法,从而适应不断变化的市场条件。
范畴论也可验证交易系统的正确性和一致性,以及开发交易行为的正规模型。 提供清晰严谨的语言来表达和推理交易概念,范畴论可以帮助交易者编写更可靠和可维护的智能系统,并更有效地与其他交易者和研究人员交流他们的思路和策略。
域(Domains)和态射(Morphisms)
范畴域和态射(Morphisms)是范畴论的基本概念。 在范畴论中,范畴是元素的域(又名集合),以及态射(或箭头又名映射,又名函数)它们之间的集合。 在这些文章中,我们取箭头、函数或映射作为态射的基本单位。 这些态射针对范畴内每个域中的元素之间的关系进行编码,并且可将它们组合成更复杂的态射。
集合是数学中的基本概念,它们在范畴论中起着关键作用。 在范畴论中,我们将它们称为用于定义特定“种类”元素的域。 典型情况,如果我们研究域范畴,范畴的“元素”将是域本身。 这些域,通常但又并非总是,继续包含构成态射基础的其它元素。 此范畴中的态射将是域之间的函数,这些函数是一个域的每个元素与另一个域的唯一元素相关联的规则。 例如,如果我们有两个域 A 和 B,则从 A 到 B 的态射将是一个规则,它将 A 的每个元素分配给 B 的唯一元素。当态射从 A 转运到 B 时,A 被称为“本域”,而 B 是“协域”。 本域中的所有元素都将与协域中的至少一个元素有关系。 本域中的任何元素都不会保持“未映射”状态。 然而,协域中的某些元素可能是“未映射”的。 这经常表示如下。
//+------------------------------------------------------------------+ //| ELEMENT CLASS | //+------------------------------------------------------------------+ class CElement { protected: int cardinal; vector element; public: bool Cardinality(int Value) { if(Value>=0 && Value<INT_MAX) { cardinal=Value; element.Init(cardinal); return(true); } return(false); } int Cardinality() { return(cardinal); } double Get(int Index) { if(Index>=0 && Index<Cardinality()) { return(element[Index]); } return(EMPTY_VALUE); } bool Set(int Index,double Value) { if(Index>=0 && Index<Cardinality()) { element[Index]=Value; return(true); } return(false); } CElement(void) { Cardinality(0); }; ~CElement(void) {}; };
故此,从上面的清单中,我们可以从定义一个元素开始。 这是一个域的基本单位,您可以把它当作一个“集合的成员”。 该单元可以采用任何数据类型,无论是双精度型还是整数型,不过若要满足更复杂的数据类型和运算时,向量类型将更加灵活。 它允许可扩展性。 其大小,'cardinal' 参数受到保护,只能通过 Cardinality()' 函数访问或修改。 保护其访问可确保无论何时修改向量,都会相应调整其大小。 向量 “element” 本身也受到保护,以防止无效的索引错误。 故此,仅允许通过 “Get()” 和 “Set()” 函数进行访问。 'Set()' 函数还返回一个布尔值来验证赋值是否成功。
//+------------------------------------------------------------------+ //| DOMAIN CLASS | //+------------------------------------------------------------------+ class CDomain { protected: int cardinal; CElement elements[]; public: bool Cardinality(int Value) { if(Value>=0 && Value<INT_MAX) { cardinal=Value; ArrayResize(elements,cardinal); return(true); } return(false); } int Cardinality() { return(cardinal); } bool Get(int Index,CElement &Element) { if(Index>=0 && Index<Cardinality()) { Element=elements[Index]; return(true); } return(false); } bool Set(int Index,CElement &Value,bool IsNew=false) { if(Index>=0 && Index<Cardinality()) { if(!IsNew||New(Value)<0) { elements[Index]=Value; return(true); }} return(false); } //only unique elements allowed int New(CElement &Value) { bool _new=-1; // for(int o=0; o<cardinal; o++) { if(ElementMatch(Value,elements[o])) { _new=o; break; } } return(_new); } CDomain(void) { Cardinality(0); }; ~CDomain(void) {}; };
因此,其内包括元素的域类,已在上面介绍过了。 它的主要变量 “elements[]” 和它的大小 “cardinal” 如同上述在元素类中一样受到保护。 这里略有不同的是添加了 “New()” 方法。 该函数帮助检查所要添加到域中的新对象,并确保它们的唯一性。 范畴域仅包含唯一对象。 不允许有重复。
由于我们有这两个类,我们可以尝试构造一些域。 我们来尝试一个偶数和一个奇数域。 我们可以运行一个脚本来引用这些类。 脚本清单如下所示。
//+------------------------------------------------------------------+ //| INPUTS | //+------------------------------------------------------------------+ input int __domain_elements=3; input int __domain_morphisms=5; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //Declare a sets of natural even & odd numbers CDomain _evens,_odds; CreateNumberDomain(_evens,__domain_elements,2); CreateNumberDomain(_odds,__domain_elements,2,1); printf(__FUNCSIG__+" evens are... "+PrintDomain(_evens)); printf(__FUNCSIG__+" odds are... "+PrintDomain(_odds)); }
创建数字函数是一种使用自然数填充域的简单方法。 所有代码都附在文末,但为清楚起见,此处展示出它的设置。
'PrintSet()' 函数是我们经常提到的一种资源丰富的方法。 它简单地令我们能使用相应的括号和逗号查看域的内容,精心将元素向量结构与整体域结构分离。 如果我们运行上面的脚本,这就是我们应该看到的。
2022.12.08 18:49:13.890 ct_1 (EURGBP.ln,MN1) void OnStart() odds are... {(1.0),(3.0),(5.0),(7.0),(9.0),(11.0),(13.0)}
在括号中都是每个域的各种元素,因为它们是向量数据类型,这意味着它们本身可以有多个条目。 若发生这种情况时,在每个带有括号的条目之间出现逗号,如此来帮助定义元素边界的位置。
态射也是范畴论中的一个重要概念,它们为域中元素之间的关系进行编码。 根据范畴论规则,每个态射将域中的一个元素映射到协域中的一个元素。 这些函数可以组合成更复杂的态射,它们可以用来研究集合的性质,以及它们与其它集合的关系,正如我们将看到的。
通览伊始,态射有 5 种主要类型,可用于描述给定类别中对象之间的关系。 一些最常见的类型是:
- 单态:这些是单射(injective)态射,这意味着它们将源域中的每个元素映射到协域中的唯一对象。 不会有两个态射映射到协域中的同一对象。 在这种情况下,您也许有一些对象在目标中无映射。
- 表态:这些是漫射(surjective)态射,这意味着域中的元素映射到协域中的所有元素。 换言之,协域中没有任何元素会保持无映射。 在这种实例下,协域中的元素通常少于源集合中的元素。
- 同构:同构是双射(bijective)态射,这意味着它们在源和目标范畴中的对象之间建立一对一的对应关系。 它们既是单射的,也是漫射的,因为源集合和目标集合具有相同数量的对象。
- 自同态:自同态是从元素到自身的态射。 它构成了一个恒等映射,即一组链接回域的自同态。
- 自同构:自同构是同构和自同态的映射组合。
我们看看如何以 MQL 实现态射类,以及它与域的分组作为同态。
//+------------------------------------------------------------------+ //| MORPHISM CLASS | //+------------------------------------------------------------------+ class CMorphism { public: int domain; int codomain; CElement morphism; bool Morph(CDomain &D,CDomain &C,CElement &DE,CElement &CE,bool Add=true) { int _d=D.New(DE),_c=C.New(CE); // if(_d>=0 && _c>=0) { if(DE.Cardinality()==CE.Cardinality()) { domain=_d; codomain=_c; morphism.Cardinality(DE.Cardinality()); if(Add) { for(int c=0;c<morphism.Cardinality();c++) { morphism.Set(c,CE.Get(c)-DE.Get(c)); } } else { for(int c=0;c<morphism.Cardinality();c++) { if(DE.Get(c)!=0.0){ morphism.Set(c,CE.Get(c)/DE.Get(c)); } } } } } return(false); } CMorphism(void){ domain=-1; codomain=-1; }; ~CMorphism(void){}; };
态射类是非常基本的,只有域和协域索引参数以及一个 “Morph” 函数,本文当中我们不会关注该函数。 此处省略了对相应域集合和协域集合的引用,因为它们列在伞形同态类中,如下所示。
//+------------------------------------------------------------------+ //| HOMO-MORPHISM CLASS | //+------------------------------------------------------------------+ class CHomomorphism { protected: int cardinal; CMorphism morphism[]; public: bool init; CDomain domain; CDomain codomain; bool Cardinality(int DomainIndex,int CodomainIndex,bool Add=true) { bool _morphed=true; if ( !init ) { _morphed=false; return(_morphed); } if ( DomainIndex<0 || DomainIndex>=domain.Cardinality() || CodomainIndex<0 || CodomainIndex>=codomain.Cardinality() ) { _morphed=false; return(_morphed); } for(int m=0;m<cardinal;m++) { if(DomainIndex==morphism[m].domain) { _morphed=false; break; } } if(_morphed) { cardinal++; ArrayResize(morphism,cardinal); CElement _de,_ce; if(domain.Get(DomainIndex,_de) && codomain.Get(CodomainIndex,_ce)) { morphism[cardinal-1].Morph(domain,codomain,_de,_ce,Add); } } return(_morphed); }; int Cardinality() { return(cardinal); }; bool Get(int Index,CMorphism &Morphism) { if(Index>=0 && Index<Cardinality()) { return(true); } return(false); }; bool Set(int Index,CMorphism &Value) { if ( Index>=0 && Index<Cardinality() && Value.domain>=0 && Value.domain<domain.Cardinality() && Value.codomain>=0 && Value.codomain<codomain.Cardinality() ) { if(!MorphismMatch(Index,Value,morphism,Cardinality())) { morphism[Index]=Value; return(true); } } return(false); }; void Init(CDomain &Domain,CDomain &Codomain) { domain=Domain; codomain=Codomain; init=true; } CHomomorphism(void){ init=false; cardinal=0; }; ~CHomomorphism(void){}; };
在同态类中,“morphism[]” 变量及其大小 “morphisms” 由于上述原因而受到保护,我不再啰嗦。 可以说,两个 “Morphism()” 函数和 “Get()” 和 “Set()” 函数遵循类似于对象和集合类中的函数风格。 这里添加了一个 “init” 布尔参数,该参数指示类是否已进行初始化,即分配公开的 “domain” 和 “codomain” 集合。 将态射添加到受保护的 “morphism[]” 数组是通过提供域索引和协域索引来实现的。 这些索引需要限定在其各自的域大小范围内。 一旦它们传递到这里,就需要检查域索引是否没有被实际用过。 根据范畴论的规则,域中的所有对象都必须映射到协域中的对象,但它们只能映射一次。 故此,协域索引可以使用两次,但域索引只能使用一次。
我们稍微离题一点,看看子域(又名: 子集)。 子域是范畴论中有用的工具,其能力是列举域内的子域,并检查域是否是给定域的可能子域,这可能是一个资源非常丰富的工具。 我们来看一下完成这两项任务的函数。
//+------------------------------------------------------------------+ //| Domain Match function | //+------------------------------------------------------------------+ bool DomainMatch(CDomain &A,CDomain &B) { if(A.Cardinality()!=B.Cardinality()) { return(false); } bool _matched=true; for(int o=0; o<A.Cardinality(); o++) { CElement _a,_b; if(A.Get(o,_a) && B.Get(o,_b) && !ElementMatch(_a,_b)) { _matched=false; break; } } return(_matched); } //+------------------------------------------------------------------+ //| Is Subdomain function | //+------------------------------------------------------------------+ bool IsSubdomain(CDomain &Domain,CDomain &SubDomain) { bool _is_subdomain=false; int _subdomain_count=0; CDomain _subdomains[]; GetSubdomains(Domain,_subdomain_count,_subdomains); for(int c=0;c<_subdomain_count;c++) { if(DomainMatch(SubDomain,_subdomains[c])) { _is_subdomain=true; break; } } return(_is_subdomain); }
因此,如果我们运行下面的脚本:
//+------------------------------------------------------------------+ //| INPUTS | //+------------------------------------------------------------------+ input int __domain_elements=3; input int __domain_morphisms=5; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //Declare a sets of natural even & odd numbers CDomain _evens,_odds; CreateNumberDomain(_evens,__domain_elements,2); CreateNumberDomain(_odds,__domain_elements,2,1); printf(__FUNCSIG__+" evens are... "+PrintDomain(_evens)); printf(__FUNCSIG__+" odds are... "+PrintDomain(_odds)); int _subdomain_count=0; CDomain _subdomains[]; GetSubdomains(_evens,_subdomain_count,_subdomains); printf(__FUNCSIG__+" evens subs are... "+IntegerToString(_subdomain_count)); for(int s=0; s<_subdomain_count; s++) { printf(" with: "+PrintDomain(_subdomains[s])+", at: "+IntegerToString(s+1)); } }
它应该给我们出示这些日志:
2022.12.08 20:25:21.314 ct_1 (EURGBP.ln,MN1) void OnStart() evens subs are... 7 2022.12.08 20:25:21.314 ct_1 (EURGBP.ln,MN1) with: {(2.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 1 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(4.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 2 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 3 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(4.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 4 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 5 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(4.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 6 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(4.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 7
如果我们把空集也算作子集,则可能子集的实际数量为 8。
可以按如下所示编写附加检查子集的代码:
CDomain _smaller_evens; CreateNumberDomain(_smaller_evens,__domain_elements-2,2); printf(__FUNCSIG__+" smaller evens are... "+PrintDomain(_smaller_evens)); bool _is_subdomain=IsSubdomain(_evens,_smaller_evens);printf(__FUNCSIG__+" it is: "+string(_is_subdomain)+" that 'smaller-evens' is a subdomain of evens. "); _is_subdomain=IsSubdomain(_odds,_smaller_evens);printf(__FUNCSIG__+" it is: "+string(_is_subdomain)+" that 'smaller-evens' is a subdomain of odds. ");
其日志应该产生如下结果:
2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) void OnStart() smaller evens are... {(2.0)} 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) void OnStart() it is: true that 'smaller-evens' is a subset of evens. 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) void OnStart() it is: false that 'smaller-evens' is a subset of odds.
在范畴论中,同态是两个域之间一组态射的结构保持定义。 它是一个保留元素的关系和操作定义的函数。 同态在范畴论中很重要,因为它们提供了一种研究不同元素之间关系的方法,以及从现有元素构建新元素的方法。
考虑到元素之间的同态,可以深入了解元素的性质,及其在不同操作下的行为。 例如,两个域之间存在同态,可定义它们之间的等价概念,从而允许研究它们之间的关系。
范畴论中同态的另一个意义是,它们提供了一种通过继承这些属性从现有对象构建新对象的方法。 这些示例以及等价性将在后续文章中介绍,此处仅提及这些示例,以便为定义这个看似“无用”的类奠定基础。
我们来尝试创建一个同态类的实例,含有时间和收盘价集合的域和协域,令范畴论更生动。
然后,我们调用 'PrintHomomorphism()' 函数查看此实例。 这些自定义打印函数在查看创建的对象、集合、态射和同态时非常有用。 此为清单:
//+------------------------------------------------------------------+ //| Print Element function | //+------------------------------------------------------------------+ string PrintElement(CElement &O,int Precision=1,bool IsTime=false) { string _element="("; // for(int r=0; r<O.Cardinality(); r++) { if(!IsTime) { _element+=DoubleToString(O.Get(r),Precision); } else if(IsTime) { _element+=TimeToString(datetime(int(O.Get(r)))); } if(r<O.Cardinality()-1){ _element+=","; } } // return(_element+")"); } //+------------------------------------------------------------------+ //| Print Set function | //+------------------------------------------------------------------+ string PrintDomain(CDomain &S,int Precision=1,bool IsTime=false) { string _set="{"; // CElement _e; for(int o=0; o<S.Cardinality(); o++) { S.Get(o,_e); _set+=PrintElement(_e,Precision,IsTime);if(o<S.Cardinality()-1){ _set+=","; } } // return(_set+"}\n"); } //+------------------------------------------------------------------+ //| Print Morphism function | //+------------------------------------------------------------------+ string PrintMorphism(CMorphism &M, CDomain &Domain,CDomain &Codomain,int Precision=1,bool DomainIsTime=false,bool CodomainIsTime=false) { string _morphism=""; // CElement _d,_c; if(Domain.Get(M.domain,_d) && Codomain.Get(M.codomain,_c)) { _morphism=PrintElement(_d,Precision,DomainIsTime); _morphism+="|----->"; _morphism+=PrintElement(_c,Precision,CodomainIsTime); _morphism+="\n"; } // return(_morphism); } //+------------------------------------------------------------------+ //| Print Homomorphism function | //+------------------------------------------------------------------+ string PrintHomomorphism(CHomomorphism &H,int Precision=1,bool DomainIsTime=false,bool CodomainIsTime=false) { string _homomorphism="\n\n"+PrintDomain(H.domain,Precision,DomainIsTime); // _homomorphism+="|\n"; CMorphism _m; for(int m=0;m<H.Cardinality();m++) { if(H.Get(m,_m)) { _homomorphism+=(PrintMorphism(_m,H.domain,H.codomain,Precision,DomainIsTime,CodomainIsTime)); } } // _homomorphism+="|\n"; _homomorphism+=PrintDomain(H.codomain,Precision,CodomainIsTime); // return(_homomorphism); }
这里值得注意的输入是 “Precision” 和 “IsTime”。 这两个设置小数位以便显示浮点数据(向量的默认值),以及是否需要将对象/向量数据转换为时间,以便在返回的字符串中表示。
因此,为了创建我们的同态,我们将使用此脚本:
//Declare sets to store time & close prices CDomain _time,_close; MqlRates _rates[]; //Fill domain with 5 most recent bar time & MA values from chart if(CopyRates(_Symbol,_Period,0,__domain_morphisms,_rates)>=__domain_morphisms && _time.Cardinality(__domain_morphisms) && _close.Cardinality(__domain_morphisms)) { for(int m=0;m<__domain_morphisms;m++) { //Create uni row element CElement _t,_m; if(_t.Cardinality(1) && _m.Cardinality(1)) { datetime _t_value=_rates[m].time;//iTime(_Symbol,_Period,m); _t.Set(0,double(int(_t_value))); _time.Set(m,_t); double _m_value=_rates[m].close;//iClose(_Symbol,_Period,5);//,m,MODE_SMA,PRICE_CLOSE); _m.Set(0,_m_value); _close.Set(m,_m); } } } //Create homomorphism from time to close CHomomorphism _h;_h.Init(_time,_close); if(_h.init) { //Create 1-1 morphisms from time to MA int _morphisms=0; for(int m=0;m<__domain_morphisms;m++) { if(_h.Cardinality(m,m)){ _morphisms++; } } if(_morphisms>=__domain_morphisms) { printf(__FUNCSIG__+" homomorphism: "+PrintHomomorphism(_h,_Digits,true)); } }
我们创建两个集合 “_time” 和 “_close”。 然后,我们用来自 MqlRates 数组的数据填充它们。 '__set_morphisms' 是确定设置大小的输入参数。 在这种情况下,“time” 和 “_close” 集合的大小相同,并且它们与输入参数匹配。 然后用这两个集合初始化同态类实例 '_h”。 调用 “Morphisms()” 函数在两个集合之间创建简单的一对一态射,可检查该函数结果,因为它返回一个布尔值。 若为 true,则 '_morphisms' 参数将递增,一旦我们达到输入参数的大小,我们就调用 ‘Print Homomorphism()' 函数打印同态 '_h'。 我们的日志应该产生这个:
2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) void OnStart() homomorphism: 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) {(2022.07.01 00:00),(2022.08.01 00:00),(2022.09.01 00:00),(2022.10.01 00:00),(2022.11.01 00:00)} 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) | 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.07.01 00:00)|----->(0.83922) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.08.01 00:00)|----->(0.86494) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.09.01 00:00)|----->(0.87820) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.10.01 00:00)|----->(0.86158) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.11.01 00:00)|----->(0.87542) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) | 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) {(0.83922),(0.86494),(0.87820),(0.86158),(0.87542)}
在范畴理论中,列举同态的集合图像也需要应对自如。 图像仅指协域的子集,该子集仅含有从域映射的对象。 出于我们的目的,我们可用以下函数列举它:
//+------------------------------------------------------------------+ //| Image function | //+------------------------------------------------------------------+ void Image(CHomomorphism &H,CSet &Output) { for(int m=0;m<H.Morphisms();m++) { CObject _o; CMorphism _m; if(H.Get(m,_m) && H.codomain.Get(_m.codomain_index,_o)) { bool _matched=false; for(int o=0;o<Output.Objects();o++) { CObject _oo; if(Output.Get(o,_oo) && ObjectMatch(_o,_oo)) { _matched=true; break; } } if(!_matched) { Output.Objects(Output.Objects()+1); Output.Set(Output.Objects()-1,_o); } } } }
为了查看此代码的实际效果,我们用到此脚本:
//Create homomorphism from time to close CHomomorphism _h_image;_h_image.Init(_time,_close); if(_h_image.init) { //Create 1-1 morphisms from time to MA int _morphisms=0; for(int m=0;m<__set_morphisms;m++) { int _random_codomain=MathRand()%__set_morphisms; if(_h_image.Morphisms(m,_random_codomain)){ _morphisms++; } } if(_morphisms>=__set_morphisms) { CSet _image;_image.Objects(0); Image(_h_image,_image); printf(__FUNCSIG__+" image from homomorphism: "+PrintSet(_image,_Digits)); } }
我们在这里所做所有事就是在一个名为 '_h_image' 的新同态类中使用之前创建的两个集合。 这次的区别在于,当添加态射时,我们随机选择共域值,故此会有重复。 这意味着协域集合将与 “_h” 实例中的协域集合不同。 我们的日志应该产生这样:
2022.12.08 21:55:54.568 ct_1 (EURJPY.ln,H2) void OnStart() image from homomorphism: {(145.847),(145.188)}
2022.12.08 21:55:54.568 ct_1 (EURJPY.ln,H2)
显然,协域只有可能的三个对象中的两个对象,因此我们可以轻松地从给定集合中检索映射到的对象。 我们将在下一篇文章离继续从同构和自同态开始。
结束语
我们已经研究了范畴论的一些入门级概念,这是数学中用于对信息进行分类的一种越来越应对自如的方法。 其中包括一个元素、一个域和一个态射。 元素形成基本单元,它通常被认为是集合的成员,在范畴论中称为域。 本域通过其元素可以与其它域(称为协域)建立关系。 这些关系称为态射。 对于交易者来说,范畴论可以作为时间序列财经信息的分类器,从而作为评估和预测市场状况的工具。 我们将在下一篇文章里从此处继续。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/11849


