/*尊重别人劳动成果,转发请注明出处并删除本说明!毛毛一一快跑 原创。
在比较两条曲线是否相同或相近时(相似度),可能会遇到两条曲线在坐标系中的起始位置不一样、两条曲线的点数比较多的情况。
本算法运用余弦相似度与距离差相结合的方法比较相似度,并且对两条曲线在坐标系中的起始位置不一致的情况提出了“位移比较”的概念.
“余弦相似度”属于大学课程《解析几何》(或者说《笛卡尔空间》,非数学专业的课程《高等数学》中也有提及)内容,简单说就是将两条直线
看成两条同方向的向量,用向量之间的夹角作为方向相似参数。而向量的余弦角可由向量的数量积(也叫“点乘积”)求得:设向量a(a1,a2,a3),向量b(b1,b2,b3)则
cos(ab)=(a.b)/(|a||b|)=(a1b1+a2b2+a3b3)/sqrt(a1^2+a2^2+a3^2)sqrt(b1^2+b2^2+b3^2)。(《解析几何》王敬赓版 第二章第6节有述)。
“距离差”就是两直线垂直方向上的距离差,可取中间点的距离差。
“位移比较”由本人提出, 所谓“位移比较”就是将一条曲线在坐标系中整体左右平移一定距离,每次移动后均与另一条曲线比较相似度,最终得出相似度最大的位置作为统一起始位置。
对于两曲线起始位置最大左右各相差不大于30个点时均能取得教满意结果,可根据电脑性能更改两曲线起始位置相差的最大值(算法中改
出现“30”、“60”处)以提高算法处理范围。
原理讲的很明白了,还不懂就拖出去撸了,,,
*/
public double Similarity(int n, double[] y1, double[] y2) //int n:曲线y1,y2的点个数,
{
double[] tempy1 = { 0, 0 }; //线段转换为向量的坐标
double[] tempy2 = { 0, 0 };
double[] resule=new double[n]; //存相似度
double[] distance=new double [n]; //存距离差比
double[] k1 = { 0, 0 }; //临时
double count1=0; //统计异常线段个数
double max1=0, maxX1=0, max2=0, maxX2=0,min1=1000,min2=1000,minX2=1000,minX1=1000; //max1 线1的最大值, maxX1 线1最大值的下角标
for (int i = 0; i < n; i++) //两条线的最大最小值及下角标
{
if (max1 < y1[i])
{
max1 = y1[i];
maxX1 = i;
}
if (min1 > y1[i])
{
min1 = y1[i];
minX1 = i;
}
if (max2 < y2[i])
{
max2 = y2[i];
maxX2 = i;
}
if (min2 > y2[i])
{
min2 = y2[i];
minX2 = i;
}
}
if ((max1 - min1) < 4&&(max2-min2)<4) //两条线均满足直线要求则返回1,否则继续判断
{
//if (Math.Abs(max1 - max2) < 4)
// return 1;
//else
// return 2;
return 1;
}
double k3 = y2[0] - y1[0];
for (int i = 0; i < n; i++) //y1上下平移
{
y1[i] = y1[i] + k3;
}
double[] y3 = new double[n + 60]; //扩展数组存放线1
double[] y4 = new double[n + 60]; //扩展数组存放线2
double[] resule1 = new double[60]; //存放60次比较的结果
for (int i = 0; i < n + 60; i++) //线1存入数组y3 左右各补齐30个0
{
if (i >= 30 && i < n + 30)
y3[i] = y1[i - 30];
else
y3[i] = 0;
}
for (int i = 0; i < 60; i++)
{
int j = 0, t = 0;
for (; j <= i; j++)
{
y4[j] = 0;
}
for (int k = 0; k < n; k++)
{
y4[j + k] = y2[k];
t = j + k;
}
t++;
for (; t < 210; t++)
{
y4[t] = 0;
}
resule1[i] = Simil(n + 60, y3, y4);
}
double k2 = 1000; //临时
for (int i = 0; i < 60; i++)
{
if (k2 > resule1[i])
k2 = resule1[i];
}
for (int i = 0; i < n; i++) //恢复y1
{
y1[i] = y1[i] - k3;
}
if (k2 <= 0.2)
return 1;
if (k2 > 0.2 && k2 <= 0.3)
return 2;
if (k2 > 0.3)
return 0;
return 3;
}
double Simil(int n, double[] y1, double[] y2)
{
double[] tempy1 = { 0, 0 }; //线段转换为向量的坐标
double[] tempy2 = { 0, 0 };
double[] resule = new double[n]; //存相似度
double[] distance = new double[n]; //存距离差比
double[] k1 = { 0, 0 }; //临时
double count1 = 0; //统计异常线段个数
for (int i = 0; i < n - 2; ) //比较n-1条线段的相似度及距离差比,并统计
{
tempy1[0] = 1;
tempy2[0] = 1;
tempy1[1] = y1[i + 2] - y1[i];
tempy2[1] = y2[i + 2] - y2[i];
k1[0] = tempy1[0] * tempy2[0] + tempy1[1] * tempy2[1];
k1[1] = (Math.Sqrt(tempy1[0] * tempy1[0] + tempy1[1] * tempy1[1])) * (Math.Sqrt(tempy2[0] * tempy2[0] + tempy2[1] * tempy2[1]));
resule[i] = k1[0] / k1[1];
k1[0] = ((y1[i] + y1[i + 2]) / 2) - ((y2[i] + y2[i + 2]) / 2);
k1[0] = k1[0] / ((y1[i] + y2[i] + y1[i + 2] + y2[i + 2]) / 4);
distance[i] = Math.Abs(k1[0]);
resule[i] = resule[i] * 0.5 + (1 - distance[i]) * 0.5;
if (resule[i] < 0.95) //统计异常点的个数
count1++;
i += 2;
}
//if (count1 / (n / 2) <= 0.2) //满足异常条件的线段数占总线段树n-1的比值 范围:[0,0.2]正常、(0.2,0.4]警告、(0.4,1]异常
// return 1;
//if (count1 / (n / 2) <= 0.3 && count1 / n > 0.2)
// return 2;
//if (count1 / (n / 2) > 0.3)
// return 0;
//return 0;
return count1 / (n / 2);
}