以C#为例,讲解如何建立一个类,这其中需要考虑需要什么样的数据(成员),什么样的属性以及方法,以及提供给外部程序调用,最后考虑怎么样去实现这样的算法。例如对于一个向量Vector(类)而言,它需要一行数据,元素类型可以是int、double、float形式(或则是泛型<T>);需要的方法:向量的构造、显示、增删改查、运算符重载、求和、均值、最大值、最小值等处理。 本博客利用C#进行编程,新建了一个Vector类(向量),具备向量的常见运算操作和方法;
程序+讲解文档下载:点击下载。
Step01:新建Vector类
新建一个Vector类;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace XMU.DataBase
{public class Vector{}
}
注:
- 命名空间中,之所以有“.DataBase”,是因为我在项目中新建了一个名为“DataBase”的文件夹,为了方便调用,此处可省略“.DataBase”。
- 为了便于调用,将Vector类改为public形式。
Step02:定义成员
建立成员;Vector类需要一个容器“_data”存放数据;
public class Vector //<T>{/// <summary>/// 成员:向量存储,/// 不希望直接被外部访问,所以用protected/// </summary>protected double[] _data;}
Step03:定义属性
建立属性;对于一个Vector数据,属性为Vector的长度;因此建立一个公开属性Length;
/// <summary>/// 属性:获取Vector的长度(元素个数)/// </summary>public int Length => _data.Length;
Step04:向量的方法
建立Vector类的方法; Vector方法有:构造方法Vector()、显示方法ToString()、Vector向量的增删改查功能、向量的运算方法;
Step041:向量构造
建立构造函数Vector();建立一个Vector有多种方法,每种方法都需要单独写一个构造函数,Vector构造方法有3中,分别是空Vector、全0或全1、根据一串数建立Vector;具体如下:
Step0411:构造空向量
建立一个空的Vector向量方法;
/// <summary>/// 方法:构造方法,构造一个空的Vector/// </summary>public Vector(){_data = new double[0];}
注:构造函数,不能写数据类型输出类型或则不输出void;
Step0412:构造全0或全1向量
建立一个全0或全1的Vector向量方法;
/// <summary>/// 方法:构造方法,构造一个全0或全1的Vector/// </summary>/// <param name="length">向量的长度</param>/// <param name="fillOne">可选,false(默认)表示全0的vector向量,/// true表示全1的Vector向量</param>public Vector(int length, bool fillOne = false){_data = new double[length];if (fillOne) // 是否为全1的Vector向量{for(int i = 0; i < length; i++){_data[i] = 1;}}}
Step0413:根据数组构造向量
根据一串数据,建立Vector向量方法;
/// <summary>/// 方法:构造方法,根据一串double类型的数,建立Vector/// </summary>/// <param name="values">表示一串double类型的数据</param>public Vector(params double[] values){// 建立一个空的向量(),用于存放数据_data = new double[values.Length];//for(int i = 0; i < values.Length; i++)//{// _data[i] = values[i];//}Array.Copy(values, _data, values.Length);}
Step042:显示方法重写
建立显示方法ToString();由于默认的显示方式是返回类名,因此需要重写override;
/// <summary>/// 方法:显示Vector元素/// </summary>/// <returns></returns>public override string ToString(){//return base.ToString(); // 默认返回类型,需重写return "[" + string.Join(", ", _data) + "]";}
注:sting.Join() 的功能是以指定的分隔符,以字符串的形式串联起来;
当补全显示方法时,可以验证前面的构造方法是是否正确,因此可以在主函数Program.cs 中验证(下文的的验证代码,都是写在这个文件main中);
// 验证 Vector的几种构造方法// 第一种方法:构造空的VectorVector v1 = new Vector();Console.WriteLine("空的Vector构造:" + v1);// 第二种方法:构造全0或全1的Vector向量Vector v21 = new Vector(5);Console.WriteLine("全0的Vector构造:" + v21);Vector v2 = new Vector(5, true);Console.WriteLine("全1的Vector构造:" + v2);// 第三种构造方法:根据一串数,构造VectorVector v3 = new Vector(1, 2, 3, 4);Console.WriteLine("根据一串数构造Vector:" + v3);
Step043:增删改查
建立Vector的增删改查方法;具体如下:
Step0431:查询元素
Vector向量元素查询与设置(修改);根据提供的索引,查询get、设置(修改)set 向量Vector对应位置的元素;
/// <summary>/// 方法:查询或修改设置index对应位置的元素/// </summary>/// <param name="index"></param>/// <returns></returns>public double this[int index]{get => _data[index]; // 查询set => _data[index] = value; // 设置(修改)}
验证Vector的查询和修改方法;
// 查询index位置处的元素Console.WriteLine("该位置处的元素为" + v3[2]);// 设置修改index位置处的元素v3[2] = 0;Console.WriteLine("该位置处的元素为" + v3[2]);
结果:
该位置处的元素为3 该位置处的元素为0 |
Step0432:删除元素
Vector向量元素增加方法;根据提供的一个向量,在指定位置(可选)出增加某个元素或某串元素;
/// <summary>/// 方法:在index对应(this)位置前插入向量v1/// </summary>/// <param name="v1">需要插入的向量</param>/// <param name="index">插入的位置/// (索引前,例如index=0,表示在第0个索引位置前插入向量v1)/// index =-1 或则大于this.Length的长度都表示在向量末尾插入,反之,则出错</param>/// <returns></returns>public Vector Insert(Vector v1, int index = -1){// 去除 index索引 index<-1 的情况if (index < -1){throw new Exception("index输入错误,请输入不小于-1的int值");}// 将index=1 或大于 index>Length变化为index=Lengthif (index > Length || index == -1){index = Length;}// 新建一个长度为Length + v1.Length的空向量(容器)Vector v = new Vector(Length + v1.Length);// 将源数据(被插入的数据)_data,复制到v向量容器中,复制元素的个数为indexArray.Copy(_data, v._data, index);// 将源数据(插入的数据)v1._data,复制到v向量容器中,复制元素的个数为v1.LengthArray.Copy(v1._data, 0, v._data, index, v1.Length);// 将源数据(被插入的数据)_data,从 index索引以后的元素,// 复制到v向量容器中,复制元素的个数为Length - indexArray.Copy(_data, index, v._data, index + v1.Length, Length - index);return v;}
验证:
// 插入(末尾增加,就相当于在末尾插入)v3[2] = 3;Vector v4 = new Vector(5, 6);Console.WriteLine("被插入数据:" + v3);Console.WriteLine("插入的数据:" + v4);Console.WriteLine("插入后的结果:" + v3.Insert(v4, 3));
结果:
被插入数据:[1, 2, 3, 4] 插入的数据:[5, 6] 插入后的结果:[1, 2, 3, 5, 6, 4] |
注释:这个入参(插入的数据)必须是Vector,若是其他数据(比如说数组),可以用步骤Step0413的方法,将一串数据,构造成Vector,在执行插入;
Console.WriteLine("插入后的结果:" + v3.Insert(new Vector(5, 6), 3));
Step0433:删除指定长度连续的字符串
Vector向量元素删除方法;根据提供的索引范围(开始索引starIndex和删除元素的长度delLength,delLength可选,默认delLength= 1),在指定位置(可选)出删除delLength个元素;
/// <summary>/// 方法:根据指定的索引位置和删除元素的个数,删除向量元素/// </summary>/// <param name="startIndex">开始删除的位置索引</param>/// <param name="delLenth">删除元素的长度(个数)</param>/// <returns></returns>public Vector DeleteLen(int startIndex, int delLenth = 1){if (startIndex < 0 || startIndex > Length - 1 || delLenth < 1){throw new Exception("删除方法的输入参数有误");}// 防止删除的长度过长if (startIndex + delLenth > Length){delLenth = Length - startIndex;}// 新建 空向量(容器)Vector v = new Vector( Length - delLenth);// 存储第一段;Array.Copy(_data, v._data, startIndex);// 存储第二段Array.Copy(_data, startIndex + delLenth, v._data, startIndex, Length - startIndex - delLenth);return v;}
验证:
// 删除,将v3中第3位和第四位删除掉,startIndex = 2,delLength = 2;Vector v5 = v3.Insert(v4, 2);Console.WriteLine("向量:" + v5);Console.WriteLine("删除部分元素后的向量:" + v5.DeleteLen(2, 2));
Step0434:根据索引删除字符串
Vector向量元素删除方法(第二种方法);根据一些列索引位置,删除元素;(待补充)
Step0435:查询元素对应的索引
根据元素查询元素在向量Vector中对应的所有索引位置;
/// <summary>/// 方法:根据元素的值,查询该值对应的所有的索引/// </summary>/// <param name="value"></param>/// <returns>返回Vector元素为value的所有索引,是int[]类型数据</returns>public int[] QueryElementIndex(double value){// 首先不要判断向量中有多少个元素valueint count = 0;for (int i = 0; i < Length; i++){if (_data[i] == value){count++;}}// 在根据count,创建适合大小为count的容器indexContainerint[] indexContainer = new int[count]; int index = 0; // 容器indexContainer的索引for (int i = 0; i < Length; i++){if (_data[i] == value){// 存放符合条件的索引值到容器indexContainer中indexContainer[index] = i;index++;}}return indexContainer;}
验证:
// 查询向量中Vector元素为value的所有索引Vector v6 = new Vector(3,4,3,2,3,1,2,3);int[] indexContainer = v6.QueryElementIndex(3);Console.WriteLine(string.Join(",", indexContainer));
结果:
0,2,4,7 |
Step0436:在指定位置前插入向量
在一个向量指定位置上,插入一个新的向量
/// <summary>/// 方法:在index对应(this)位置前插入向量v1/// </summary>/// <param name="v1">需要插入的向量</param>/// <param name="index">插入的位置/// (索引前,例如index=0,表示在第0个索引位置前插入向量v1)/// index =-1 或则大于this.Length的长度都表示在向量末尾插入,反之,则出错</param>/// <returns></returns>public Vector Insert(Vector v1, int index = -1){// 去除 index索引 index<-1 的情况if (index < -1){throw new Exception("index输入错误,请输入不小于-1的int值");}// 将index=1 或大于 index>Length变化为index=Lengthif (index > Length || index == -1){index = Length;}// 新建一个长度为Length + v1.Length的空向量(容器)Vector v = new Vector(Length + v1.Length);// 将源数据(被插入的数据)_data,复制到v向量容器中,复制元素的个数为indexArray.Copy(_data, v._data, index);// 将源数据(插入的数据)v1._data,复制到v向量容器中,复制元素的个数为v1.LengthArray.Copy(v1._data, 0, v._data, index, v1.Length);// 将源数据(被插入的数据)_data,从 index索引以后的元素,// 复制到v向量容器中,复制元素的个数为Length - indexArray.Copy(_data, index, v._data, index + v1.Length, Length - index);return v;}
验证:
// 插入(末尾增加,就相当于在末尾插入)Console.WriteLine("================");v3[2] = 3;Vector v4 = new Vector(5, 6);Console.WriteLine("被插入数据:" + v3);Console.WriteLine("插入的数据:" + v4);Console.WriteLine("插入后的结果:" + v3.Insert(v4, 3));Console.WriteLine("插入后的结果:" + v3.Insert(new Vector(5, 6), 3));
结果:
================ 被插入数据:[1, 2, 3, 4] 插入的数据:[5, 6] 插入后的结果:[1, 2, 3, 5, 6, 4] 插入后的结果:[1, 2, 3, 5, 6, 4] |
Step044:运算符重载
Vector向量符重载;Vector向量常见的运算符有向量之间相加、减,数乘向量、对应元素相乘、向量的数量积a·b=|a|·|b|·cos〈a,b〉、向量的向量积a×b;
Step0441:“+”正号重载
默认的正号拷贝的方法,不是深度拷贝,一个修改,也会影响另一个,因此需要重写修改“+”正号的操作运算符方法;
public Vector Copy(){// 返回的是一个元素完全相同的新变量return new Vector(_data);}/// <summary>/// 方法:向量的正号拷贝,返回当前向量的深度拷贝的向量/// </summary>/// <param name="v1">需要拷贝的向量</param>/// <returns>返回当前向量的深度拷贝的向量</returns>public static Vector operator +(Vector v1) // 运算符重载必须加static{return v1.Copy();}
注:Copy()方法的作用是用来深度拷贝的,直接赋值,会导致它俩的地址一样
验证:
// 向量的拷贝,旧方法拷贝不是深度拷贝,修改其中的一个也会修改另一个,因为他俩地址一样Vector v7 = v6;v7[0] = 0;Console.WriteLine(v6 + "旧方法拷贝修改" + v7);// 新的拷贝方法(正号-深度拷贝),深度拷贝;Vector v8 = +v6;v8[0] = -1;Console.WriteLine(v6 + "新方法拷贝修改" + v8);
结果:
[0, 4, 3, 2, 3, 1, 2, 3]旧方法拷贝修改[0, 4, 3, 2, 3, 1, 2, 3] [0, 4, 3, 2, 3, 1, 2, 3]新方法拷贝修改[-1, 4, 3, 2, 3, 1, 2, 3] |
Step0442:“-”负号重载
默认的负号拷贝的方法,不是深度拷贝,一个修改,也会影响另一个,因此需要重写修改“-”正号的操作运算符方法;
/// <summary>/// 方法:向量的负号拷贝,返回当前向量的相反向量/// </summary>/// <param name="v1">需要拷贝的向量</param>/// <returns>返回当前向量的相反向量</returns>public static Vector operator -(Vector v1) // 运算符重载必须加static{Vector v2 = new Vector(v1.Length);for(int i = 0; i < v1.Length; i++){v2[i] = -v1[i];}return v2;}
验证:
// 负号Vector v9 = -v8;Console.WriteLine(v8 + "相反向量" + v9);
结果:
[-1, 4, 3, 2, 3, 1, 2, 3]相反向量[1, -4, -3, -2, -3, -1, -2, -3] |
Step0443:“+”向量相加
向量之间“+”加法不同于数字方面的加法,它是对应索引的元素之间进行算术相加,因此需要重载;
/// <summary>/// 方法:+加号运算符重载/// </summary>/// <param name="v1">第1个向量</param>/// <param name="v2">第2个向量</param>/// <returns>相加后的向量</returns>public static Vector operator +(Vector v1, Vector v2) // 运算符重载必须加static{Vector v = new Vector(v1.Length);if(v1.Length != v2.Length){throw new Exception("向量长度不一样,不能相加");}for (int i = 0; i < v1.Length; i++){v[i] = v2[i] + v1[i];}return v;}
验证:
// 向量相加Console.WriteLine(v8 + v9);
结果:
[0, 0, 0, 0, 0, 0, 0, 0] |
Step0444:“-”向量相减
向量之间“-”减法不同于数字方面的相减,它是对应索引的元素之间进行算术相减,因此需要重载;
/// <summary>/// 方法:+加号运算符重载/// </summary>/// <param name="v1">向量1</param>/// <param name="v2">向量1</param>/// <returns>向量1+向量2 的结果,返回一个Vcetor向量</returns>public static Vector operator -(Vector v1, Vector v2) // 运算符重载必须加static{Vector v = new Vector(v1.Length);if (v1.Length != v2.Length){throw new Exception("向量长度不一样,不能相加");}for (int i = 0; i < v1.Length; i++){v[i] = v2[i] - v1[i];}return v;//return v1 + (-v2);}
注:由于已有“+”加法和“-”负号重载符,因此可以将向量A-向量B看成A + (-B),即A向量+B向量的相反向量,因此代码可以改为
/// <summary>/// 方法:+加号运算符重载/// </summary>/// <param name="v1"></param>/// <param name="v2"></param>/// <returns></returns>public static Vector operator -(Vector v1, Vector v2) // 运算符重载必须加static{return v1 + (-v2);}
验证:
// 向量相加Console.WriteLine("第一个向量" + v9 + "第2个向量" + v10);Console.WriteLine("向量相加" + (v9 + v10));// 向量相减Console.Write("向量相减" + (v9 - v10));
注;向量除了相加减,还有一些特殊要求,比如说向量+/-一个数;因此,对于加法、减法还需要在添加一个运算符重载,比如说向量A+5,可以看出向量A加上5*全1的向量,因此整个会在后面补充;
Step0445:“*”向量相乘
向量相乘方法不同于数字相乘,它是对应位置的元素进行相乘(向量长度必须一致);
/// <summary>/// 方法:*向量乘法运算符重载/// </summary>/// <param name="v1">向量1</param>/// <param name="v2">向量2</param>/// <returns>向量1×向量2 的结果,返回一个Vcetor向量</returns>public static Vector operator *(Vector v1, Vector v2){Vector v = new Vector(v1.Length);if (v1.Length != v2.Length){throw new Exception("向量长度不一样,不能相乘");}for (int i = 0; i < v1.Length; i++){v[i] = v2[i] * v1[i];}return v;}
验证:
// 向量相乘Console.WriteLine("向量相乘:" + (v9 * v10));
Step0446:Dot向量点乘
向量点乘的运算方法为对应元素相乘,然后在总的相加,成一个数,比如所可以计算A点的距离,及x2+y2+z2=R2;这个就相当于计算A向量与A向量相乘,然后计算元素之和;因此在此之前,需要添加一个求向量和的算法;
因此,需要在写Dot方法之前,先写一个Sum求和方法;
/// <summary>/// 方法:向量求和/// </summary>/// <returns>返回向量元素之和</returns>public double Sum(){double sum = 0;for(int i = 0; i < Length; i++){sum += _data[i];}return sum;}
验证:
// 向量求和Console.WriteLine("向量求和:" + v9.Sum());
Dot方法代码如下:
/// <summary>/// 方法:向量点乘/// </summary>/// <param name="v1"></param>/// <returns></returns>public double Dot(Vector v1){if(Length != v1.Length){throw new Exception("长度不一样,不能点乘");}double sum = 0;for(int i = 0; i < Length; i++){sum = sum + this._data[i] * v1[i];}return sum;}
也可以直接写成:
/// 方法:向量点乘/// </summary>/// <param name="v1"></param>/// <returns></returns>public double Dot(Vector v1){// 调用重写*乘号算法Vector v = this * v1;// 调用求和Sum方法return v.Sum();}
验证:
// 向量点乘Console.WriteLine("向量点乘:" + v9.Dot(v10));
结果:
向量点乘:-83 |
Step044:向量元素常见处理方法
向量元素的常见处理算法有:求和、均值、最大、最小值等等;
Step0441:Sum求和
向量求和是将所有元素进行求和,具体参考“Step0445:Dot向量点乘”;
/// <summary>/// 方法:向量求和/// </summary>/// <returns>返回向量元素之和</returns>public double Sum(){double sum = 0;for(int i = 0; i < Length; i++){sum += _data[i];}return sum;}
Step0442:Mean求均值
向量求解元素之间的均值是在求和的基础上,在除以元素的个数,因此可以写成:
/// <summary>/// 方法:求解向量的均值/// </summary>/// <returns></returns>public Double Mean(){if(Length == 0){throw new Exception("向量元素个数为0");}// 调用求和double sum = this.Sum();double mean = sum / Length;return mean;}
验证:
// 向量求均值Console.WriteLine("向量均值:" + v9.Mean());
结果:
向量均值:-2.125 |
Step0443:Max求最大值
通过循环对比,找出最大值,代码如下:
/// <summary>/// 方法:求向量的最大值/// </summary>/// <returns>返回向量的最大值</returns>public double Max(){if (Length == 0){throw new Exception("向量元素个数为0");}double maxValue = _data[0];for(int i = 0; i < Length; i++){if(maxValue < _data[i]){maxValue = _data[i];}}return maxValue;}
验证:
// 向量的最大值Console.WriteLine("向量最大值:" + v9.Max());
结果:
向量最大值:1 |
Step0444:Min求最小值
通过循环对比,找出最小值,代码如下:
/// <summary>/// 方法:求向量的最小值/// </summary>/// <returns>返回向量的最小值</returns>public double Min(){if(Length == 0){throw new Exception("向量元素个数为0");}double minValue = _data[0];for (int i = 0; i < Length; i++){if (minValue > _data[i]){minValue = _data[i];}}return minValue;}
验证:
// 向量的最小值Console.WriteLine("向量最小值:" + v9.Min());
Step045:向量常见的判断方法
判断方法有:是否全部为0,向量是否相等;
Step0451:IsZero判断是否为全0向量
循环判断每个元素是否等于0,若存在不等于0情况,则返回false,否则返回true;
/// <summary>/// 判断是否为全0向量/// </summary>/// <returns>全0返回true,反之,返回false</returns>public bool IsZero(){for(int i = 0; i < Length; i++){if(_data[i] != 0){return false;}}return true;}
验证:
// 判断是否为全0向量Vector v11 = new Vector(5);Console.WriteLine("判断是否为全0向量" + v11.IsZero());v11[2] = 5;Console.WriteLine("判断是否为全0向量" + v11.IsZero());
Step0452:IsOne判断是否为全1向量
循环判断每个元素是否等于1,若存在不等于1情况,则返回false,否则返回true;
/// <summary>/// 方法:判断是否为全1向量/// </summary>/// <returns>全1返回true,反之,返回false</returns>public bool IsOne(){for (int i = 0; i < Length; i++){if (_data[i] != 1){return false;}}return true;}
验证:
// 判断是否为全1向量Vector v12 = new Vector(5, true);Console.WriteLine("判断是否为全1向量" + v12.IsOne());v12[2] = 0;Console.WriteLine("判断是否为全1向量" + v12.IsOne());
结果:
判断是否为全1向量True 判断是否为全1向量False |
Step0453:Equal判断向量是否相等
判断两个向量是都相等,可以循环判断每个元素差是否为0;也可以转换一下思路,通过判断两个向量的差,来判断两个向量是否相等;
/// <summary>/// 判断两个向量是否相等/// </summary>/// <param name="v1">需要被判断的向量</param>/// <returns>相等返回true,反之则为false</returns>public bool Equal(Vector v1){if(Length != v1.Length){return false;}return (this - v1).IsZero();}
验证:
// 判断两个向量是否相等Vector v13 = new Vector(1, 2, 3, 4, 5, 6, 7, 8);Vector v14 = new Vector(0, 2, 3, 4, 5, 6, 7, 8);Console.WriteLine("判断向量是否相等" + v13.Equal(v13));Console.WriteLine("判断向量是否相等" + v13.Equal(v14));
结果:
判断向量是否相等True 判断向量是否相等False |
Step046:其他向量操作方法
除此之外,向量还有截取、拼接等算法
Step0461:向量的拼接
Step0462:向量的截取
完整的代码请到我的博客中下载
https://download.csdn.net/download/weixin_41649786/87674833https://download.csdn.net/download/weixin_41649786/87674833
不足之处,敬请斧正!
路漫漫其修远兮,吾将上下而求索!