mapminmax c语言

99ANYc3cd6
预计阅读时长 29 分钟
位置: 首页 C语言 正文

mapminmax 是一个在数据预处理(尤其是在机器学习和深度学习领域)中非常常见的函数,它的主要作用是将一组数据线性地映射到一个指定的区间,通常是 [0, 1][-1, 1]

mapminmax 的数学原理

假设我们有一组数据 X,我们想将其映射到新的区间 [new_min, new_max]

  1. 找到原始数据的最小值和最大值

    • X_min = min(X)
    • X_max = max(X)
  2. 计算原始数据的范围

    • X_range = X_max - X_min
    • 注意X_range 为 0(即所有数据都相同),为了避免除以零的错误,通常会将所有数据映射到区间的中点,(new_min + new_max) / 2
  3. 应用线性变换公式: 对于原始数据中的每一个值 x,其映射后的新值 y 计算如下:

    y = (x - X_min) / X_range * (new_max - new_min) + new_min

    这个公式的含义是:

    • (x - X_min): 将数据平移,使其最小值变为 0。
    • / X_range: 将数据缩放,使其范围变为 0 到 1。
    • * (new_max - new_min): 将数据缩放,使其范围与目标区间 [new_min, new_max] 的宽度相匹配。
    • + new_min: 将数据平移,使其最小值对齐到 new_min

C 语言实现

下面我们将分步实现这个功能,并提供一个完整的、可运行的 C 语言示例。

步骤 1:定义函数原型

我们将创建一个函数,它接收原始数据数组、数组大小、目标区间的最小值和最大值,以及一个用于存储结果的数组。

/**
 * @brief 将数据线性映射到指定的 [new_min, new_max] 区间
 * 
 * @param data       原始数据数组
 * @param size       数组大小
 * @param new_min    目标区间的最小值
 * @param new_max    目标区间的最大值
 * @param result     用于存储结果的数组
 */
void mapminmax(const double data[], int size, double new_min, double new_max, double result[]);

步骤 2:实现函数逻辑

在函数内部,我们需要完成以下操作:

  1. 遍历数组找到 data_mindata_max
  2. 计算 data_range
  3. 处理 data_range 为 0 的特殊情况。
  4. 遍历原始数组,应用公式计算每个新值并存入 result 数组。
#include <stdio.h> // 用于 printf
void mapminmax(const double data[], int size, double new_min, double new_max, double result[]) {
    if (size <= 0) {
        printf("错误:数组大小必须大于 0,\n");
        return;
    }
    // 1. 找到原始数据的最小值和最大值
    double data_min = data[0];
    double data_max = data[0];
    for (int i = 1; i < size; i++) {
        if (data[i] < data_min) {
            data_min = data[i];
        }
        if (data[i] > data_max) {
            data_max = data[i];
        }
    }
    // 2. 计算原始数据的范围
    double data_range = data_max - data_min;
    // 3. 处理所有数据都相同的情况 (data_range == 0)
    if (data_range == 0.0) {
        double target_value = (new_min + new_max) / 2.0;
        for (int i = 0; i < size; i++) {
            result[i] = target_value;
        }
        return;
    }
    // 4. 应用线性变换公式
    double new_range = new_max - new_min;
    for (int i = 0; i < size; i++) {
        result[i] = (data[i] - data_min) / data_range * new_range + new_min;
    }
}

步骤 3:创建一个完整的示例程序

下面是一个完整的 C 程序,它定义了数据,调用 mapminmax 函数,并打印出结果。

#include <stdio.h>
/**
 * @brief 将数据线性映射到指定的 [new_min, new_max] 区间
 * 
 * @param data       原始数据数组
 * @param size       数组大小
 * @param new_min    目标区间的最小值
 * @param new_max    目标区间的最大值
 * @param result     用于存储结果的数组
 */
void mapminmax(const double data[], int size, double new_min, double new_max, double result[]);
// 辅助函数:打印数组
void print_array(const double arr[], int size, const char* label) {
    printf("%s: [", label);
    for (int i = 0; i < size; i++) {
        printf("%.4f", arr[i]);
        if (i < size - 1) {
            printf(", ");
        }
    }
    printf("]\n");
}
int main() {
    // 示例 1: 映射到 [0, 1]
    double data1[] = {10.0, 20.0, 30.0, 40.0, 50.0};
    int size1 = sizeof(data1) / sizeof(data1[0]);
    double result1[size1];
    printf("--- 示例 1: 映射到 [0, 1] ---\n");
    print_array(data1, size1, "原始数据");
    mapminmax(data1, size1, 0.0, 1.0, result1);
    print_array(result1, size1, "映射结果");
    printf("\n");
    // 示例 2: 映射到 [-1, 1]
    double data2[] = {-5.0, 0.0, 5.0, 10.0, 15.0};
    int size2 = sizeof(data2) / sizeof(data2[0]);
    double result2[size2];
    printf("--- 示例 2: 映射到 [-1, 1] ---\n");
    print_array(data2, size2, "原始数据");
    mapminmax(data2, size2, -1.0, 1.0, result2);
    print_array(result2, size2, "映射结果");
    printf("\n");
    // 示例 3: 处理所有数据都相同的情况
    double data3[] = {7.0, 7.0, 7.0, 7.0};
    int size3 = sizeof(data3) / sizeof(data3[0]);
    double result3[size3];
    printf("--- 示例 3: 所有数据相同,映射到 [0, 1] ---\n");
    print_array(data3, size3, "原始数据");
    mapminmax(data3, size3, 0.0, 1.0, result3);
    print_array(result3, size3, "映射结果 (应为 0.5)");
    return 0;
}

如何编译和运行

  1. 将上述代码保存为文件,mapminmax_example.c
  2. 打开终端或命令提示符。
  3. 使用 GCC 编译器进行编译:
    gcc mapminmax_example.c -o mapminmax_example -lm
    • -o mapminmax_example:指定输出的可执行文件名。
    • -lm:链接数学库(虽然这个例子没有直接使用 math.h,但这是一个好习惯,以防未来需要)。
  4. 运行生成的可执行文件:
    ./mapminmax_example

预期输出

--- 示例 1: 映射到 [0, 1] ---
原始数据: [10.0000, 20.0000, 30.0000, 40.0000, 50.0000]
映射结果: [0.0000, 0.2500, 0.5000, 0.7500, 1.0000]
--- 示例 2: 映射到 [-1, 1] ---
原始数据: [-5.0000, 0.0000, 5.0000, 10.0000, 15.0000]
映射结果: [-1.0000, -0.6000, -0.2000, 0.2000, 0.6000]
--- 示例 3: 所有数据相同,映射到 [0, 1] ---
原始数据: [7.0000, 7.0000, 7.0000, 7.0000]
映射结果: [0.5000, 0.5000, 0.5000, 0.5000]

高级变种:反向变换

在很多应用场景中,你可能还需要将已经归一化的数据还原回原始范围,这可以通过记录原始的 data_min, data_max, new_min, 和 new_max 来实现。

我们可以修改函数,让它返回一个包含这些参数的结构体,以便后续进行反向变换。

#include <stdio.h>
#include <stdlib.h> // 用于 malloc 和 free
// 结构体用于存储归一化所需的参数
typedef struct {
    double original_min;
    double original_max;
    double new_min;
    double new_max;
} NormalizationParams;
// 归一化函数,返回参数结构体
NormalizationParams mapminmax_with_params(const double data[], int size, double new_min, double new_max, double result[]);
// 反向变换函数
void inverse_mapminmax(const NormalizationParams params, const double normalized_data[], int size, double original_data[]);
// ... (print_array 函数保持不变) ...
int main() {
    double data[] = {10.0, 20.0, 30.0, 40.0, 50.0};
    int size = sizeof(data) / sizeof(data[0]);
    double normalized_data[size];
    double restored_data[size];
    printf("--- 带参数的归一化和反向变换 ---\n");
    print_array(data, size, "原始数据");
    NormalizationParams params = mapminmax_with_params(data, size, 0.0, 1.0, normalized_data);
    printf("归一化参数: min_orig=%.2f, max_orig=%.2f, min_new=%.2f, max_new=%.2f\n",
           params.original_min, params.original_max, params.new_min, params.new_max);
    print_array(normalized_data, size, "归一化数据");
    inverse_mapminmax(params, normalized_data, size, restored_data);
    print_array(restored_data, size, "恢复后的数据");
    return 0;
}
NormalizationParams mapminmax_with_params(const double data[], int size, double new_min, double new_max, double result[]) {
    NormalizationParams params;
    params.new_min = new_min;
    params.new_max = new_max;
    // ... (查找 data_min, data_max 的逻辑与之前相同) ...
    double data_min = data[0];
    double data_max = data[0];
    for (int i = 1; i < size; i++) {
        if (data[i] < data_min) data_min = data[i];
        if (data[i] > data_max) data_max = data[i];
    }
    params.original_min = data_min;
    params.original_max = data_max;
    double data_range = data_max - data_min;
    if (data_range == 0.0) {
        double target_value = (new_min + new_max) / 2.0;
        for (int i = 0; i < size; i++) result[i] = target_value;
        return params;
    }
    double new_range = new_max - new_min;
    for (int i = 0; i < size; i++) {
        result[i] = (data[i] - data_min) / data_range * new_range + new_min;
    }
    return params;
}
void inverse_mapminmax(const NormalizationParams params, const double normalized_data[], int size, double original_data[]) {
    double orig_range = params.original_max - params.original_min;
    double new_range = params.new_max - params.new_min;
    if (new_range == 0.0) { // 如果目标区间宽度为0,则无法反向变换
        printf("错误:目标区间宽度为0,无法进行反向变换,\n");
        return;
    }
    for (int i = 0; i < size; i++) {
        original_data[i] = (normalized_data[i] - params.new_min) / new_range * orig_range + params.original_min;
    }
}

这个高级版本更加实用,因为它完整地封装了归一化过程和其逆过程,是构建数据处理管道时的常用模式。

-- 展开阅读全文 --
头像
dede如何给栏目添加缩略图功能?
« 上一篇 今天
织梦模板修改视频教程在哪看?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]