C++代码实现MATLAB中的normalize函数功能

cpp 复制代码
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <algorithm>
#include <stdexcept>
#include <string>
#include <cassert>

// ============================================================
// 辅助函数
// ============================================================

// 计算算术平均值
double computeMean(const std::vector<double>& v) {
    if (v.empty()) return 0.0;
    double sum = std::accumulate(v.begin(), v.end(), 0.0);
    return sum / static_cast<double>(v.size());
}

// 计算样本标准差(除以 N-1,与 MATLAB normalize 默认行为一致)
double computeStd(const std::vector<double>& v, double mean) {
    if (v.size() < 2) return 0.0;
    double sqSum = 0.0;
    for (double x : v) {
        double diff = x - mean;
        sqSum += diff * diff;
    }
    return std::sqrt(sqSum / static_cast<double>(v.size() - 1));
}

// 计算中位数
double computeMedian(std::vector<double> v) {
    if (v.empty()) return 0.0;
    std::sort(v.begin(), v.end());
    size_t n = v.size();
    if (n % 2 == 0) {
        return (v[n / 2 - 1] + v[n / 2]) / 2.0;
    } else {
        return v[n / 2];
    }
}

// 计算 MAD(中位数绝对偏差)
double computeMAD(const std::vector<double>& v, double median) {
    if (v.empty()) return 0.0;
    std::vector<double> absDev;
    absDev.reserve(v.size());
    for (double x : v) {
        absDev.push_back(std::abs(x - median));
    }
    return computeMedian(absDev);
}

// 计算 L2 范数(欧几里得范数)
double computeNormL2(const std::vector<double>& v) {
    double sqSum = 0.0;
    for (double x : v) {
        sqSum += x * x;
    }
    return std::sqrt(sqSum);
}

// 计算 IQR(四分位距)
double computeIQR(std::vector<double> v) {
    if (v.size() < 2) return 0.0;
    std::sort(v.begin(), v.end());
    size_t n = v.size();
    auto percentile = [&](double p) -> double {
        double idx = p * (static_cast<double>(n) - 1.0);
        size_t lo = static_cast<size_t>(std::floor(idx));
        size_t hi = static_cast<size_t>(std::ceil(idx));
        if (lo == hi) return v[lo];
        double frac = idx - static_cast<double>(lo);
        return v[lo] * (1.0 - frac) + v[hi] * frac;
    };
    return percentile(0.75) - percentile(0.25);
}

// ============================================================
// 核心归一化函数(对一维向量)
// ============================================================

// zscore:中心化为均值 0,缩放为标准差 1
std::vector<double> normalizeZscore(const std::vector<double>& v) {
    double mean = computeMean(v);
    double sd = computeStd(v, mean);
    std::vector<double> result(v.size());
    for (size_t i = 0; i < v.size(); ++i) {
        result[i] = (sd != 0.0) ? (v[i] - mean) / sd : 0.0;
    }
    return result;
}

// norm:按 L2 范数归一化
std::vector<double> normalizeNorm(const std::vector<double>& v) {
    double normVal = computeNormL2(v);
    std::vector<double> result(v.size());
    for (size_t i = 0; i < v.size(); ++i) {
        result[i] = (normVal != 0.0) ? v[i] / normVal : 0.0;
    }
    return result;
}

// range:重缩放到 [0, 1]
std::vector<double> normalizeRange(const std::vector<double>& v) {
    if (v.empty()) return {};
    auto [minIt, maxIt] = std::minmax_element(v.begin(), v.end());
    double minVal = *minIt, maxVal = *maxIt;
    double range = maxVal - minVal;
    std::vector<double> result(v.size());
    for (size_t i = 0; i < v.size(); ++i) {
        result[i] = (range != 0.0) ? (v[i] - minVal) / range : 0.0;
    }
    return result;
}

// medianiqr:中心化为中位数,缩放为 IQR
std::vector<double> normalizeMedianIQR(const std::vector<double>& v) {
    double med = computeMedian(v);
    double iqr = computeIQR(v);
    std::vector<double> result(v.size());
    for (size_t i = 0; i < v.size(); ++i) {
        result[i] = (iqr != 0.0) ? (v[i] - med) / iqr : 0.0;
    }
    return result;
}

// center+scale 通用形式(可自定义中心化值和缩放值)
std::vector<double> normalizeCenterScale(const std::vector<double>& v,
                                          double center, double scale) {
    std::vector<double> result(v.size());
    for (size_t i = 0; i < v.size(); ++i) {
        result[i] = (scale != 0.0) ? (v[i] - center) / scale : 0.0;
    }
    return result;
}

// ============================================================
// 矩阵归一化(按列处理,与 MATLAB normalize(A) 默认行为一致)
// ============================================================

class Matrix {
public:
    size_t rows, cols;
    std::vector<double> data; // 列优先存储

    Matrix(size_t r, size_t c) : rows(r), cols(c), data(r * c, 0.0) {}

    double& operator()(size_t i, size_t j) { return data[i + j * rows]; }
    double operator()(size_t i, size_t j) const { return data[i + j * rows]; }

    // 提取第 j 列
    std::vector<double> getColumn(size_t j) const {
        std::vector<double> col(rows);
        for (size_t i = 0; i < rows; ++i) {
            col[i] = (*this)(i, j);
        }
        return col;
    }

    // 设置第 j 列
    void setColumn(size_t j, const std::vector<double>& col) {
        for (size_t i = 0; i < rows; ++i) {
            (*this)(i, j) = col[i];
        }
    }

    // 提取第 i 行
    std::vector<double> getRow(size_t i) const {
        std::vector<double> row(cols);
        for (size_t j = 0; j < cols; ++j) {
            row[j] = (*this)(i, j);
        }
        return row;
    }

    // 设置第 i 行
    void setRow(size_t i, const std::vector<double>& row) {
        for (size_t j = 0; j < cols; ++j) {
            (*this)(i, j) = row[j];
        }
    }
};

// 按指定维度归一化矩阵
// dim=1: 按列归一化(默认),dim=2: 按行归一化
Matrix normalizeMatrix(const Matrix& A, int dim = 1,
                        const std::string& method = "zscore") {
    Matrix result(A.rows, A.cols);

    if (dim == 1) {
        // 按列处理
        for (size_t j = 0; j < A.cols; ++j) {
            std::vector<double> col = A.getColumn(j);
            std::vector<double> normCol;

            if (method == "zscore") {
                normCol = normalizeZscore(col);
            } else if (method == "norm") {
                normCol = normalizeNorm(col);
            } else if (method == "range") {
                normCol = normalizeRange(col);
            } else if (method == "medianiqr") {
                normCol = normalizeMedianIQR(col);
            } else {
                throw std::invalid_argument("Unknown method: " + method);
            }
            result.setColumn(j, normCol);
        }
    } else if (dim == 2) {
        // 按行处理
        for (size_t i = 0; i < A.rows; ++i) {
            std::vector<double> row = A.getRow(i);
            std::vector<double> normRow;

            if (method == "zscore") {
                normRow = normalizeZscore(row);
            } else if (method == "norm") {
                normRow = normalizeNorm(row);
            } else if (method == "range") {
                normRow = normalizeRange(row);
            } else if (method == "medianiqr") {
                normRow = normalizeMedianIQR(row);
            } else {
                throw std::invalid_argument("Unknown method: " + method);
            }
            result.setRow(i, normRow);
        }
    } else {
        throw std::invalid_argument("dim must be 1 or 2");
    }

    return result;
}

// ============================================================
// 使用示例
// ============================================================

int main() {
    // 示例 1:向量 zscore 归一化(对应 MATLAB: normalize(1:5))
    std::vector<double> v = {1, 2, 3, 4, 5};
    auto n1 = normalizeZscore(v);
    std::cout << "zscore: ";
    for (double x : n1) std::cout << x << " ";
    std::cout << "\n";

    // 示例 2:向量 L2 范数归一化
    auto n2 = normalizeNorm(v);
    std::cout << "norm:   ";
    for (double x : n2) std::cout << x << " ";
    std::cout << "\n";

    // 示例 3:range 归一化到 [0,1]
    auto n3 = normalizeRange(v);
    std::cout << "range:  ";
    for (double x : n3) std::cout << x << " ";
    std::cout << "\n";

    // 示例 4:矩阵按列 zscore 归一化
    Matrix A(3, 3);
    // [8 1 6]
    // [3 5 7]
    // [4 9 2]
    A(0,0)=8; A(0,1)=1; A(0,2)=6;
    A(1,0)=3; A(1,1)=5; A(1,2)=7;
    A(2,0)=4; A(2,1)=9; A(2,2)=2;

    Matrix N = normalizeMatrix(A, 1, "zscore");
    std::cout << "\nMatrix zscore (by column):\n";
    for (size_t i = 0; i < N.rows; ++i) {
        for (size_t j = 0; j < N.cols; ++j) {
            std::cout << N(i, j) << "\t";
        }
        std::cout << "\n";
    }

    // 示例 5:矩阵按行 zscore 归一化
    Matrix N2 = normalizeMatrix(A, 2, "zscore");
    std::cout << "\nMatrix zscore (by row):\n";
    for (size_t i = 0; i < N2.rows; ++i) {
        for (size_t j = 0; j < N2.cols; ++j) {
            std::cout << N2(i, j) << "\t";
        }
        std::cout << "\n";
    }

    return 0;
}
相关推荐
谢亮_vipxieliang1 小时前
Go WaitGroup与Once——并发同步的基石
开发语言·后端·golang
不会就选b1 小时前
算法日常・每日刷题--<动态规划>11
算法
(Charon)1 小时前
【C++面试】手写String类:同时实现拷贝构造与移动构造
c++·算法
程序员老陆1 小时前
通过std::unique_ptr初始化std::shared_ptr
开发语言·c++
liangshanbo12151 小时前
前端面试题:AI 对话中超长消息导致内存溢出,怎么解决?
java·开发语言·前端
迷途之人不知返1 小时前
算法系列6:模拟
算法
旺仔仔仔2 小时前
# 机器人项目实战(1):选相机、试深度,再到手眼标定
算法
CoderYanger2 小时前
A.每日一题:1614. 括号的最大嵌套深度
java·程序人生·算法·leetcode·面试·学习方法
yuki蜜语2 小时前
别只会调包跑模型!一文讲透大模型训练底层的“递推、递归、三明治”三重逻辑
算法