098. 使用C语言实现简单的人工智能算法

在C语言中实现一个简单的人工智能算法可以帮助你理解人工智能的基本概念。这里我将展示一个简单的遗传算法(Genetic Algorithm, GA),这是一种基于自然选择和遗传学原理的搜索和优化技术。遗传算法通过模拟生物进化过程来解决优化问题。

遗传算法简介

遗传算法的核心思想是通过选择、交叉(重组)和变异操作来逐步改进候选解的质量。算法的基本步骤如下:

  1. 初始化种群:随机生成一组候选解(称为种群)。
  2. 适应度评估:计算每个候选解的适应度(fitness)。
  3. 选择:根据适应度选择候选解来生成下一代。
  4. 交叉(重组):将两个父代候选解组合生成新的子代。
  5. 变异:随机改变一些候选解的某些部分。
  6. 重复:重复上述步骤,直到达到终止条件(如最大迭代次数或适应度阈值)。

示例代码:简单的遗传算法

用C语言实现的简单遗传算法,用于解决一个简单的优化问题:最大化一个函数 f(x) 的值。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define POPULATION_SIZE 100
#define CHROMOSOME_LENGTH 10
#define MAX_GENERATIONS 1000
#define MUTATION_RATE 0.01

// 适应度函数:最大化 f(x) = sin(x) * cos(x)
double fitnessFunction(int chromosome[CHROMOSOME_LENGTH]) {
    double x = 0.0;
    for (int i = 0; i < CHROMOSOME_LENGTH; i++) {
        x += chromosome[i] * pow(2, i);
    }
    x = x / pow(2, CHROMOSOME_LENGTH) * 2 * PI; // 将二进制映射到 [0, 2π]
    return sin(x) * cos(x);
}

// 初始化种群
void initializePopulation(int population[POPULATION_SIZE][CHROMOSOME_LENGTH]) {
    for (int i = 0; i < POPULATION_SIZE; i++) {
        for (int j = 0; j < CHROMOSOME_LENGTH; j++) {
            population[i][j] = rand() % 2;
        }
    }
}

// 选择操作:轮盘赌选择
void selection(int population[POPULATION_SIZE][CHROMOSOME_LENGTH], int selectedPopulation[POPULATION_SIZE][CHROMOSOME_LENGTH]) {
    double totalFitness = 0.0;
    double fitness[POPULATION_SIZE];
    for (int i = 0; i < POPULATION_SIZE; i++) {
        fitness[i] = fitnessFunction(population[i]);
        totalFitness += fitness[i];
    }

    for (int i = 0; i < POPULATION_SIZE; i++) {
        double r = (rand() / (double)RAND_MAX) * totalFitness;
        double sum = 0.0;
        for (int j = 0; j < POPULATION_SIZE; j++) {
            sum += fitness[j];
            if (sum >= r) {
                for (int k = 0; k < CHROMOSOME_LENGTH; k++) {
                    selectedPopulation[i][k] = population[j][k];
                }
                break;
            }
        }
    }
}

// 交叉操作:单点交叉
void crossover(int population[POPULATION_SIZE][CHROMOSOME_LENGTH]) {
    for (int i = 0; i < POPULATION_SIZE; i += 2) {
        if (rand() / (double)RAND_MAX < 0.7) { // 交叉概率为 70%
            int crossoverPoint = rand() % CHROMOSOME_LENGTH;
            for (int j = crossoverPoint; j < CHROMOSOME_LENGTH; j++) {
                int temp = population[i][j];
                population[i][j] = population[i + 1][j];
                population[i + 1][j] = temp;
            }
        }
    }
}

// 变异操作
void mutation(int population[POPULATION_SIZE][CHROMOSOME_LENGTH]) {
    for (int i = 0; i < POPULATION_SIZE; i++) {
        for (int j = 0; j < CHROMOSOME_LENGTH; j++) {
            if (rand() / (double)RAND_MAX < MUTATION_RATE) {
                population[i][j] = 1 - population[i][j];
            }
        }
    }
}

int main() {
    int population[POPULATION_SIZE][CHROMOSOME_LENGTH];
    int selectedPopulation[POPULATION_SIZE][CHROMOSOME_LENGTH];

    srand(time(NULL));

    // 初始化种群
    initializePopulation(population);

    for (int generation = 0; generation < MAX_GENERATIONS; generation++) {
        // 选择
        selection(population, selectedPopulation);

        // 交叉
        crossover(selectedPopulation);

        // 变异
        mutation(selectedPopulation);

        // 更新种群
        for (int i = 0; i < POPULATION_SIZE; i++) {
            for (int j = 0; j < CHROMOSOME_LENGTH; j++) {
                population[i][j] = selectedPopulation[i][j];
            }
        }

        // 打印当前最佳适应度
        double bestFitness = 0.0;
        for (int i = 0; i < POPULATION_SIZE; i++) {
            double fitness = fitnessFunction(population[i]);
            if (fitness > bestFitness) {
                bestFitness = fitness;
            }
        }
        printf("Generation %d: Best Fitness = %.4f\n", generation, bestFitness);
    }

    return 0;
}

代码说明

适应度函数

  • 定义了一个简单的适应度函数 f(x)=sin(x)⋅cos(x),目标是最大化这个函数的值。

  • 将二进制染色体映射到一个连续的实数值 x。

初始化种群:随机生成初始种群。

选择操作:使用轮盘赌选择方法,根据适应度选择候选解。

交叉操作:使用单点交叉方法,将两个父代候选解组合生成新的子代。

变异操作:随机改变一些候选解的某些部分,以一定的概率进行变异。

主函数:初始化种群,然后在每一世代中进行选择、交叉和变异操作,直到达到最大世代数。

示例运行

Generation 0: Best Fitness = 0.1234
Generation 1: Best Fitness = 0.2345
Generation 2: Best Fitness = 0.3456
...
Generation 999: Best Fitness = 0.9876

扩展功能

  1. 更复杂的适应度函数:使用更复杂的适应度函数,例如多目标优化问题。
  2. 其他选择方法:实现其他选择方法,如锦标赛选择或排名选择。
  3. 多点交叉:实现多点交叉或均匀交叉。
  4. 自适应变异率:根据当前世代的适应度动态调整变异率。
  5. 并行化:使用多线程或并行计算来加速遗传算法的运行。

使用C语言不同方式实现简单的人工智能算法

实现简单的线性回归算法

线性回归是机器学习中最基础的算法之一,适合用C语言实现。以下代码演示了如何使用梯度下降法训练线性回归模型:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define NUM_FEATURES 1
#define LEARNING_RATE 0.01
#define ITERATIONS 1000

double predict(double* features, double* weights) {
    double prediction = 0;
    for(int i=0; i<=NUM_FEATURES; i++) {
        prediction += weights[i] * (i==0 ? 1 : features[i-1]);
    }
    return prediction;
}

void train(double** X, double* y, double* weights, int m) {
    for(int iter=0; iter<ITERATIONS; iter++) {
        double gradients[NUM_FEATURES+1] = {0};
        
        for(int i=0; i<m; i++) {
            double error = predict(X[i], weights) - y[i];
            for(int j=0; j<=NUM_FEATURES; j++) {
                gradients[j] += error * (j==0 ? 1 : X[i][j-1]);
            }
        }
        
        for(int j=0; j<=NUM_FEATURES; j++) {
            weights[j] -= LEARNING_RATE * gradients[j] / m;
        }
    }
}

int main() {
    // 示例数据
    double X[5][1] = {{1}, {2}, {3}, {4}, {5}};
    double y[5] = {2, 4, 6, 8, 10};
    
    double* X_ptr[5];
    for(int i=0; i<5; i++) X_ptr[i] = X[i];
    
    double weights[NUM_FEATURES+1] = {0};
    train(X_ptr, y, weights, 5);
    
    printf("模型权重: 截距=%.2f, 斜率=%.2f\n", weights[0], weights[1]);
    
    // 预测新数据
    double test[1] = {6};
    printf("预测结果: %.2f\n", predict(test, weights));
    
    return 0;
}

实现K近邻分类算法

K近邻(KNN)是一种简单的分类算法,不需要训练过程:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define K 3
#define NUM_FEATURES 2

typedef struct {
    double features[NUM_FEATURES];
    int label;
} DataPoint;

double euclidean_distance(double* a, double* b) {
    double sum = 0;
    for(int i=0; i<NUM_FEATURES; i++) {
        sum += pow(a[i] - b[i], 2);
    }
    return sqrt(sum);
}

int knn_predict(DataPoint* dataset, int size, double* features) {
    double distances[size];
    int indices[size];
    
    for(int i=0; i<size; i++) {
        distances[i] = euclidean_distance(dataset[i].features, features);
        indices[i] = i;
    }
    
    // 简单排序
    for(int i=0; i<size-1; i++) {
        for(int j=i+1; j<size; j++) {
            if(distances[i] > distances[j]) {
                double temp_d = distances[i];
                distances[i] = distances[j];
                distances[j] = temp_d;
                
                int temp_i = indices[i];
                indices[i] = indices[j];
                indices[j] = temp_i;
            }
        }
    }
    
    int votes[2] = {0}; // 假设只有两个类别
    for(int i=0; i<K; i++) {
        votes[dataset[indices[i]].label]++;
    }
    
    return votes[0] > votes[1] ? 0 : 1;
}

int main() {
    DataPoint dataset[4] = {
        {{1,1}, 0},
        {{1,2}, 0},
        {{3,3}, 1},
        {{4,4}, 1}
    };
    
    double test[2] = {2, 2};
    printf("预测类别: %d\n", knn_predict(dataset, 4, test));
    
    return 0;
}

实现简单的神经网络

以下是一个单层神经网络的实现,使用Sigmoid激活函数:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>

#define INPUT_SIZE 2
#define HIDDEN_SIZE 3
#define OUTPUT_SIZE 1
#define LEARNING_RATE 0.1
#define EPOCHS 10000

double sigmoid(double x) {
    return 1 / (1 + exp(-x));
}

double sigmoid_derivative(double x) {
    return x * (1 - x);
}

void forward(double input[INPUT_SIZE],
             double hidden[HIDDEN_SIZE],
             double output[OUTPUT_SIZE],
             double w1[INPUT_SIZE][HIDDEN_SIZE],
             double w2[HIDDEN_SIZE][OUTPUT_SIZE]) {
    
    // 输入层到隐藏层
    for(int h=0; h<HIDDEN_SIZE; h++) {
        hidden[h] = 0;
        for(int i=0; i<INPUT_SIZE; i++) {
            hidden[h] += input[i] * w1[i][h];
        }
        hidden[h] = sigmoid(hidden[h]);
    }
    
    // 隐藏层到输出层
    for(int o=0; o<OUTPUT_SIZE; o++) {
        output[o] = 0;
        for(int h=0; h<HIDDEN_SIZE; h++) {
            output[o] += hidden[h] * w2[h][o];
        }
        output[o] = sigmoid(output[o]);
    }
}

void train(double input[INPUT_SIZE],
           double target[OUTPUT_SIZE],
           double w1[INPUT_SIZE][HIDDEN_SIZE],
           double w2[HIDDEN_SIZE][OUTPUT_SIZE]) {
    
    double hidden[HIDDEN_SIZE] = {0};
    double output[OUTPUT_SIZE] = {0};
    
    forward(input, hidden, output, w1, w2);
    
    // 反向传播
    double output_error = target[0] - output[0];
    double output_delta = output_error * sigmoid_derivative(output[0]);
    
    double hidden_error[HIDDEN_SIZE] = {0};
    double hidden_delta[HIDDEN_SIZE] = {0};
    
    for(int h=0; h<HIDDEN_SIZE; h++) {
        hidden_error[h] = output_delta * w2[h][0];
        hidden_delta[h] = hidden_error[h] * sigmoid_derivative(hidden[h]);
    }
    
    // 更新权重
    for(int h=0; h<HIDDEN_SIZE; h++) {
        w2[h][0] += LEARNING_RATE * output_delta * hidden[h];
    }
    
    for(int i=0; i<INPUT_SIZE; i++) {
        for(int h=0; h<HIDDEN_SIZE; h++) {
            w1[i][h] += LEARNING_RATE * hidden_delta[h] * input[i];
        }
    }
}

int main() {
    srand(time(NULL));
    
    // 初始化权重
    double w1[INPUT_SIZE][HIDDEN_SIZE];
    double w2[HIDDEN_SIZE][OUTPUT_SIZE];
    
    for(int i=0; i<INPUT_SIZE; i++) {
        for(int h=0; h<HIDDEN_SIZE; h++) {
            w1[i][h] = (double)rand() / RAND_MAX * 2 - 1;
        }
    }
    
    for(int h=0; h<HIDDEN_SIZE; h++) {
        w2[h][0] = (double)rand() / RAND_MAX * 2 - 1;
    }
    
    // XOR训练数据
    double inputs[4][2] = {{0,0}, {0,1}, {1,0}, {1,1}};
    double targets[4] = {0, 1, 1, 0};
    
    // 训练
    for(int e=0; e<EPOCHS; e++) {
        for(int i=0; i<4; i++) {
            train(inputs[i], &targets[i], w1, w2);
        }
    }
    
    // 测试
    for(int i=0; i<4; i++) {
        double hidden[HIDDEN_SIZE];
        double output[OUTPUT_SIZE];
        forward(inputs[i], hidden, output, w1, w2);
        printf("%d XOR %d = %.2f\n", (int)inputs[i][0], (int)inputs[i][1], output[0]);
    }
    
    return 0;
}

这些实现展示了如何在C语言中构建基本的人工智能算法。线性回归适用于数值预测,KNN用于分类任务,而神经网络可以解决非线性问题。每个实现都包含了必要的数学运算和数据结构,可以进一步扩展用于更复杂的应用场景。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐