1.基本定义
本节主要是为了阅读本文后面的内容都供基础,仅对面向对象的基本概念和抽象代数中的基本概念做个简单介绍,并分析两者之间的联系,阐述笔者的基本思想。
面向对象(详细请查看MSDN) | 抽象代数(详细请查看维基百科) |
对象:一个自包含的实体用一组可识别的特性和行为来标识。 | 代数系统:一个非空集合A,定义A×A->A的二元运算(映射):f(a,b)=c。集合A与一个或多个这种映射f所组成的系统。 |
类:具有相同属性和功能的对象的抽象的集合。 | 群(G,·):运算满足结合律、有单位元、有逆元。运算满足交换律的群叫交换群。 |
实例:一个真实的对象。 | 环(R,+,*):两个代数运算(+,*),关于加法构成交换群,关于乘法结合律成立,乘法对加法的两个分配律成立。乘法满足交换律的环叫做交换环 |
封装:每个对象都包含它能进行操作所需要的所有信息,对象不必依赖其他对象来完成自己的操作。 | 域(F):有单位元(1!=0)且每个非零元都可逆的交换环。 |
继承:定义了类如何关联、共享特性。子类不但继承了父类的所有特性,还可以定义新的特性。 | 加法单位元e:a+e=e+a,通常叫零元,可用0表示。加法逆元:通常叫负元。乘法单位元:ae=ea,通常叫单位元,可用1表示。乘法逆元,通常仍叫逆元。 |
多态:不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。 | 结合律:(a·b)·c=a·(b·c) |
接口:把隐式公共方法和属性组合起来,以封特定功能的一个集合。 | 分配律:a*(b+c)=ab+ac,(b+c)*a=ba+ca |
抽象类:提供一个继承的出发点,是对类的一种抽象。 | 交换律:a·b=b·a |
泛型:具有点位符(类型参数)的类,结构、接口或方法,这些点位符是类、结构、接口和方法所存储或优胜的一个或多个类型点位符。 | 向量空间:非空集合V称为F域上的向量空间,V关于加法构成一个交换群,有一个标量乘法,满足结合律、二个分配律,域上的单位元乘任何向量,向量保持不变。 |
从上表可以看出代数系统是一个最为抽象的概念,而群则是一种具有代表性的代数系统,而环是在交换群的基本上新定义了一种运算,而域则是一种具有特殊性质的交换换。向量空间本身不一定是域,这里只做为数学知识介绍给大家,不作详细讨论。为了方便起见,群中的运算在本文中群用加法表示。接下来我要做的工作是:
1.用接口来定义运算。 |
2.用抽象类来表示一类代数系统:如群、环、域;用继承来表示代数系统之间的相互关系 |
3.用类来表示具体的代数系统,如非零有理数关于乘法构成的交换群。如整数关于加法构成的交换群;整数关于加法和乘法构成的交换环;有理数关于加法和乘法构成域等等。 |
4.类实现接口的算法中来体现运算的特殊性质。实际上如果一个运算是可计算的,那么存在一个程序能够表示这个运算[可计算理论] |
2.具体实现
笔者在实现过程中,开始想只用抽象类和接口,但是发现会涉及到频繁的强类型转换操作,故应用了泛型。这样可能导致代码可读性降低,使读者需要具有较好的对向对象基础。
2.1接口的定义
可加性泛型接口:
public interface IAddable<T>
{
T Add(T other);
}
可乘性泛型接口:
public interface IMultiplicable<T>
{
T Multiply(T other);
}
可求负元泛型接口:
public interface IOpposable<T>
{
T Opposite();
}
可求逆元泛型接口:
public interface IInvertible<T>
{
T Inverse();
}
T代表一个代数系统类,Add方法表示加法,Multiply表示乘法,Opposite表示负元,Inverse表示求逆元,如果一个对应的代数系统类实现了一个接口,就表示在这个代数系统定义了一个运算。
2.2抽象类的定义
public abstract class Group<T> : IAddable<T>, IOpposable<T>
{
public abstract T Add(T other);
public abstract T Opposite();
protected static T zero;
public static T Zero
{
get
{
return zero;
}
}
}
上面代码定义了一个继承了可加性泛型接口、和可求负元泛型接口有零的泛型抽象类,这个抽象类表示群的特征,相应运算的性质由类的具体实现来保证。
public abstract class Ring<T> : Group<T>, IMultiplicable<T>
{
public abstract T Multiply(T other);
protected static T unity;
public static T Unity
{
get
{
return unity;
}
}
}
上面代码定义一个继承于群泛型抽象类和可乘性泛型接口的泛型抽象类,这个抽象类表示了环的特征,相应运算的性质由类的具体实现来保证。
上两代码中定义了一个零元一个单元元,他们都是静态字段,因当在类的具体实现中,用静态函数对字段进行初始化。
public abstract class Field<T> : Ring<T>, IInvertible<T>
{
public abstract T Inverse();
}
上代码定义一个继承于环泛型类和可求逆元泛型接口的泛型抽象,这个抽象类表示了域的特征,相应运算的性质由类的具体实现来保证。
3一些简单的代数系统
3.1整数关于加法和乘法构成的交换环
public class Integer : Ring<Integer>,IComparable<Integer>,ISubstractable<Integer>
{
public long Value { get; set; }
public Integer()
{
Value = 0;
}
public Integer(long l)
{
Value = l;
}
static Integer()
{
zero = new Integer(0);
unity = new Integer(1);
}
public static implicit operator Integer(long i)
{
return new Integer(Convert.ToInt64(i));
}
public override Integer Add(Integer other)
{
return new Integer(this.Value + other.Value);
}
public override Integer Opposite()
{
return new Integer(-this.Value);
}
public override Integer Multiply(Integer other)
{
return new Integer(this.Value * other.Value);
}
public override Integer Copy()
{
return new Integer(this.Value);
}
public override string ToString()
{
return Value.ToString();
}
public Integer Substract(Integer other)
{
return this.Add(other.Opposite());
}
public int CompareTo(Integer other)
{
return this.Value.CompareTo(other.Value);
}
}
3.2有理数关于加法和乘法构成的域
public class RationalNumber:Field<RationalNumber>,IOperatable<RationalNumber>,IComparable<RationalNumber>
{
public long Numerator { get; set; }//分子
public long Denominator { get; set; }//分母
static RationalNumber()
{
zero = new RationalNumber(0, 1);
unity = new RationalNumber(1, 1);
}
public RationalNumber(long numerator, long denominator)
{
if (denominator == 0) throw new Exception("Denominator can't be 0.");
Numerator = numerator;
Denominator = denominator;
}
public RationalNumber()
{
Numerator = 0;
Denominator = 1;
}
public override string ToString()
{
if (Numerator == 0) return "0";
return string.Format("{0}/{1}",Numerator,Denominator);
}
public override RationalNumber Inverse()
{
if (Numerator == 0) throw new Exception("0 has not a inverse!");
return new RationalNumber(this.Denominator, this.Numerator);
}
public override RationalNumber Add(RationalNumber other)
{
return new RationalNumber(this.Numerator*other.Denominator+this.Denominator*other.Numerator,this.Denominator*other.Denominator);
}
public override RationalNumber Opposite()
{
return new RationalNumber(-this.Numerator, this.Denominator);
}
public override RationalNumber Multiply(RationalNumber other)
{
return new RationalNumber(this.Numerator*other.Numerator,this.Denominator*other.Denominator);
}
public RationalNumber Divide(RationalNumber other)
{
return this.Multiply(other.Inverse());
}
public RationalNumber Substract(RationalNumber other)
{
return this.Add(other.Opposite());
}
public override bool Equals(object obj)
{
if (obj.GetType() != this.GetType())
{
throw new Exception("Can't compare two objects with difference types!");
}
RationalNumber other = obj as RationalNumber;
return this.Numerator * other.Denominator == this.Denominator * other.Numerator;
}
public override int GetHashCode()
{
return (int)CantorPairingFunction.GetResult(Numerator, Denominator);
}
public override RationalNumber Copy()
{
return new RationalNumber(this.Numerator, this.Denominator);
}
public int CompareTo(RationalNumber other)
{
int a = (this.Numerator * other.Denominator - this.Denominator * other.Numerator).CompareTo(0);
int b = (this.Denominator * other.Denominator).CompareTo(0);
return (a * b).CompareTo(0);
}
}
4.改进
在对整数环类的有理数域类的实例进行运算的时候,如果直接调用这两个类的静态属性会发现静态字段为空值,也就是说静态构造函数并没有在第一次调用其静态属性和构造函数之前调用。原因是这些静态字段继承于其父类,这里相当于调用了父类的静态字段,因此只会自动调用其父类的静态构造函数,而其本身的静态构造函数不会被调用。解决方法可以用泛型约束,及给父类的泛型一个要具有默认构造函数的约束,使父类在调用其静态函数的时候调用泛型的默认构造函数来确保子类的静态构造函数在程序调用属性和构造函数之前调用!具体实现代码就不在本文中实现了,有兴趣的读者可以自行实现。
5.总结和感想
本文介绍了面向对象和抽象代数的基本概念,就三个最为基本的代数系统:群、环、域,利用面向对象的程序设计语言,定义了相应的代数系统抽象类,并给出了两个最为基本的代数系统(整数环和有理数域)的程序实现,有利于读者加深对面向对象的理解。同时本文给出了在CSharp语言下构建代数系统的基本方法,为用CSharp语言编写计算机代数系统提供了最为基础的知识。
本文为开博第一文,请大家多多指教。