度量距离的一些实现

103 阅读1分钟

度量距离的基本理论和计算方法见链接

欧式距离

//n维欧式距离
double EuclideanDistance(vector<double> &data1,vector<double> &data2){
    double sum;
    for (int i=0;i<data1.size();i++){
        sum += pow((data1[i]-data2[i]),2);
    }
    return sqrt(sum);
}

规范化欧式距离

即在计算欧式距离之前先将数据规范化处理,普遍使用的方法有三种,最大最小规范化、零均值规范化和L2范数规范化。

//标准化欧式距离——数据标准化
void NormalizationMethod(vector<vector<double>> &dataset,vector<vector<double>> &Normalization_dataset,int normalizationFlag){
    //标准化数据范围
    
    vector<double> maxfeature;  //最大值
    vector<double> minfeature;  //最小值
    vector<double> meanfeature; //均值
    vector<double> stdevfeature; //标准差
    vector<double> l2feature; //L2范数
    for (int j=0;j<dataset[0].size();j++){
        double middle_max=0;
        double middle_min=0x7FFFFFFF;
        double sum=0;
        for (int i=0;i<dataset.size();i++){
            if (dataset[i][j] >= middle_max){
                middle_max = dataset[i][j];
            }
            if (dataset[i][j] <= middle_min){
                middle_min = dataset[i][j];
        }
        sum+=dataset[i][j];
        }
        meanfeature.push_back(sum / dataset[0].size());//计算均值
        maxfeature.push_back(middle_max);
        minfeature.push_back(middle_min);
    }
    
    //计算标准差和L2范数
        for (int j=0;j<dataset[0].size();j++){
            double stdev=0;
            double l2=0;
            for (int i=0;i<dataset.size();i++){
                stdev+=(dataset[i][j]-meanfeature[j])*(dataset[i][j]-meanfeature[j]); //计算方差
                l2+=dataset[i][j]*dataset[i][j];
            }
                stdev = stdev/(dataset[0].size()-1);
                stdev = sqrt(stdev);//计算标准差
                l2=sqrt(l2);//计算L2范数
                l2feature.push_back(l2);
                stdevfeature.push_back(stdev);
        }

    //数据转换到01区间
    for (int i =0;i<dataset.size();i++){
        vector<double> middleset;
        for (int j =0;j<dataset[0].size();j++){
            double middle;
            if (normalizationFlag == MIN_MAX){
                middle=(dataset[i][j]-minfeature[j])/(maxfeature[j]-minfeature[j]);
            }
            else if(normalizationFlag == Z_SCORE){
                middle = (dataset[i][j]-meanfeature[j]) / stdevfeature[j];

            }else if(normalizationFlag == L2Normalization){
                middle=dataset[i][j] / l2feature[j];
            }
            middleset.push_back(middle);
        }
        
        Normalization_dataset.push_back(middleset);
        middleset.clear();
    }
}

马氏距离

即在规范化欧式距离之前,先将旋转数据的坐标使其在各个维度上线性无关(即令不同维度间变量的协方差为0,转换协方差矩阵为对角阵)。为便捷计算用了Eigen库来实现。

马氏距离的缺点:

  • 计算复杂
  • 限制较大,必须总体样本数大于样本维数,不然无法求得协方差矩阵的逆矩阵(矩阵的秩<n,非满秩矩阵),小样本学习/迁移学习中可能难以使用。
  • 扩大了变化微小变量的作用
int main(){
    MatrixXd dataset(4,2);
    dataset<<7,4,3,2,1,2,3,5;
    //转置transpose()
    MatrixXd datasetT=dataset.transpose();
    //确定协方差矩阵的大小
    MatrixXd CovarianceMatrix(datasetT.rows(),datasetT.rows());
    //计算协方差矩阵值
    for (int i=0;i<CovarianceMatrix.rows();i++){
        for (int j=0;j<CovarianceMatrix.cols();j++){
            double cov = 0;
            for(int k=0;k<datasetT.cols();k++){
                cov += (datasetT(i,k)-datasetT.row(i).mean())*(datasetT(j,k)-datasetT.row(j).mean());
            }
            CovarianceMatrix(i,j)=cov/(datasetT.cols()-1);
        }
    }

    //协方差矩阵的逆矩阵 CovarianceMatrix.inverse()
    //计算两点(i,j)间的马氏距离
    //transpose()转置
    int i = 0;
    int j = 1;
    VectorXd middle = dataset.row(i)-dataset.row(j);
    double temp = middle.transpose()*CovarianceMatrix.inverse()*middle;
    std::cout<<sqrt(temp)<<std::endl;

    return 0;
    
}