自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解 C++ 實(shí)現(xiàn) K-means 算法

開發(fā) 前端
K-means算法是一種非常經(jīng)典的聚類算法,其主要目的是將數(shù)據(jù)點(diǎn)劃分為K個(gè)集群,以使得每個(gè)數(shù)據(jù)點(diǎn)與其所屬集群的中心點(diǎn)(質(zhì)心)的平方距離之和最小。這種算法在數(shù)據(jù)挖掘、圖像處理、模式識(shí)別等領(lǐng)域有著廣泛的應(yīng)用。

一、K-means算法概述

K-means算法是一種非常經(jīng)典的聚類算法,其主要目的是將數(shù)據(jù)點(diǎn)劃分為K個(gè)集群,以使得每個(gè)數(shù)據(jù)點(diǎn)與其所屬集群的中心點(diǎn)(質(zhì)心)的平方距離之和最小。這種算法在數(shù)據(jù)挖掘、圖像處理、模式識(shí)別等領(lǐng)域有著廣泛的應(yīng)用。

二、K-means算法的基本原理

K-means算法的基本原理相對(duì)簡(jiǎn)單直觀。算法接受兩個(gè)輸入?yún)?shù):一是數(shù)據(jù)集,二是用戶指定的集群數(shù)量K。算法的輸出是K個(gè)集群,每個(gè)集群都有其中心點(diǎn)以及屬于該集群的數(shù)據(jù)點(diǎn)。

K-means算法的執(zhí)行過程如下:

  1. 初始化:隨機(jī)選擇K個(gè)點(diǎn)作為初始集群中心(質(zhì)心)。
  2. 分配數(shù)據(jù)點(diǎn)到最近的集群:對(duì)于數(shù)據(jù)集中的每個(gè)點(diǎn),計(jì)算其與各個(gè)質(zhì)心的距離,并將其分配到距離最近的質(zhì)心所對(duì)應(yīng)的集群中。
  3. 重新計(jì)算質(zhì)心:對(duì)于每個(gè)集群,計(jì)算其內(nèi)所有數(shù)據(jù)點(diǎn)的平均值,并將該平均值設(shè)為新的質(zhì)心。
  4. 迭代優(yōu)化:重復(fù)步驟2和3,直到滿足某個(gè)終止條件(如質(zhì)心的變化小于某個(gè)閾值,或者達(dá)到最大迭代次數(shù))。

圖解說(shuō)明:

圖a表示初始的數(shù)據(jù)集,在圖b中隨機(jī)找到兩個(gè)類別質(zhì)心,接著執(zhí)行上述的步驟二,得到圖c的兩個(gè)集群,但此時(shí)明顯不符合我們的要求,因此需要進(jìn)行步驟三,得到新的類別質(zhì)心(圖d),重復(fù)的進(jìn)行多次迭代(如圖e和f),直到達(dá)到不錯(cuò)的結(jié)果。

三、K-means算法的數(shù)學(xué)表達(dá)

K-means 算法是一種迭代求解的聚類分析算法,其目標(biāo)是將  個(gè)觀測(cè)值劃分為 ()個(gè)聚類,以使得每個(gè)觀測(cè)值屬于離它最近的均值(聚類中心或聚類質(zhì)心)對(duì)應(yīng)的聚類,以作為聚類的標(biāo)準(zhǔn)。

數(shù)學(xué)公式

1.數(shù)據(jù)表示

2.聚類中心

3.目標(biāo)函數(shù)

4.迭代更新

5.算法終止條件

迭代進(jìn)行分配步驟和更新步驟,直到聚類中心不再發(fā)生顯著變化,或者達(dá)到預(yù)設(shè)的最大迭代次數(shù)。

四、K-means算法的C++實(shí)現(xiàn)

首先是頭文件:

#include <iostream>  
#include <vector>  
#include <cmath>  
#include <limits>  
#include <algorithm>

第一步: 數(shù)據(jù)結(jié)構(gòu)定義

我們定義了一個(gè)Point結(jié)構(gòu)體來(lái)表示二維空間中的點(diǎn)。

struct Point {
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
};

這個(gè)結(jié)構(gòu)體很簡(jiǎn)單,只有兩個(gè)成員變量x和y,分別表示點(diǎn)在二維空間中的橫坐標(biāo)和縱坐標(biāo)。還有一個(gè)構(gòu)造函數(shù),用于創(chuàng)建點(diǎn)對(duì)象時(shí)初始化坐標(biāo)。

第二步: 輔助函數(shù)

距離計(jì)算函數(shù)

double distance(const Point& a, const Point& b) {
    return std::hypot(a.x - b.x, a.y - b.y);
}

這個(gè)函數(shù)計(jì)算兩個(gè)點(diǎn)之間的距離,使用了<cmath>庫(kù)中的std::hypot函數(shù),它接受兩個(gè)參數(shù)(橫坐標(biāo)和縱坐標(biāo)的差值),并返回這兩點(diǎn)之間的歐幾里得距離。

質(zhì)心計(jì)算函數(shù)

Point centroid(const std::vector<Point>& cluster) {
    double sum_x = 0, sum_y = 0;
    for (const auto& point : cluster) {
        sum_x += point.x;
        sum_y += point.y;
    }
    return Point(sum_x / cluster.size(), sum_y / cluster.size());
}

這個(gè)函數(shù)計(jì)算一個(gè)點(diǎn)集的質(zhì)心。質(zhì)心是所有點(diǎn)的坐標(biāo)平均值。函數(shù)遍歷點(diǎn)集,累加所有點(diǎn)的x坐標(biāo)和y坐標(biāo),然后分別除以點(diǎn)的數(shù)量,得到質(zhì)心的坐標(biāo)。

第三步: K-means算法主體

K-means算法的主體部分可以進(jìn)一步拆分為幾個(gè)小的步驟:初始化、分配點(diǎn)、重新計(jì)算質(zhì)心和檢查收斂性。

初始化

在K-means算法中,我們需要首先選擇K個(gè)初始質(zhì)心。在這個(gè)簡(jiǎn)單的實(shí)現(xiàn)中,我們隨機(jī)選擇數(shù)據(jù)集中的K個(gè)點(diǎn)作為初始質(zhì)心。

std::vector<Point> centroids(k);
for (int i = 0; i < k; ++i) {
    centroids[i] = data[rand() % data.size()];
}

分配點(diǎn)

對(duì)于數(shù)據(jù)集中的每個(gè)點(diǎn),我們需要找到最近的質(zhì)心,并將其分配給該質(zhì)心對(duì)應(yīng)的集群。

std::vector<std::vector<Point>> clusters(k);
for (const auto& point : data) {
    double min_distance = std::numeric_limits<double>::max();
    int cluster_index = 0;
    for (int i = 0; i < k; ++i) {
        double dist = distance(point, centroids[i]);
        if (dist < min_distance) {
            min_distance = dist;
            cluster_index = i;
        }
    }
    clusters[cluster_index].push_back(point);
}

重新計(jì)算質(zhì)心

分配完點(diǎn)后,我們需要重新計(jì)算每個(gè)集群的質(zhì)心。

std::vector<Point> new_centroids(k);
for (int i = 0; i < k; ++i) {
    new_centroids[i] = centroid(clusters[i]);
}

檢查收斂性

如果新舊質(zhì)心之間的變化很?。ㄔ谝粋€(gè)很小的閾值以內(nèi)),則算法收斂,可以停止迭代。

bool converged = true;
for (int i = 0; i < k; ++i) {
    if (distance(centroids[i], new_centroids[i]) > 1e-6) {
        converged = false;
        break;
    }
}

如果算法未收斂,則更新質(zhì)心并繼續(xù)迭代。

第四步: 主函數(shù)和數(shù)據(jù)準(zhǔn)備

在主函數(shù)中,我們準(zhǔn)備了一個(gè)簡(jiǎn)單的數(shù)據(jù)集(整體代碼見最后),并設(shè)置了K值和最大迭代次數(shù)。然后調(diào)用kmeans函數(shù)進(jìn)行聚類。

這就是K-means算法的一個(gè)基本實(shí)現(xiàn)。在實(shí)際應(yīng)用中,可能還需要考慮更多的優(yōu)化和異常情況處理,比如處理空集群、改進(jìn)初始質(zhì)心的選擇方法、添加對(duì)異常值的魯棒性等。

結(jié)果輸出:

Cluster 1 centroid: (3.5, 3.9)
(1, 0.6) (8, 5) (1, 4) (4, 6) 
Cluster 2 centroid: (5.41667, 9.06667)
(2, 10) (2.5, 8.4) (5, 8) (8, 8) (9, 11) (6, 9)

五、K-means算法的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 算法簡(jiǎn)單直觀,易于理解和實(shí)現(xiàn)。
  • 對(duì)于大數(shù)據(jù)集,K-means算法是相對(duì)高效的,因?yàn)樗膹?fù)雜度是線性的,即O(n)。
  • 當(dāng)集群之間的區(qū)別明顯且數(shù)據(jù)分布緊湊時(shí),K-means算法表現(xiàn)良好。

缺點(diǎn):

  • 需要預(yù)先指定集群數(shù)量K,這在實(shí)際應(yīng)用中可能是一個(gè)挑戰(zhàn)。
  • 對(duì)初始質(zhì)心的選擇敏感,不同的初始質(zhì)心可能導(dǎo)致完全不同的結(jié)果。
  • 只能發(fā)現(xiàn)球形的集群,對(duì)于非球形或復(fù)雜形狀的集群效果不佳。
  • 對(duì)噪聲和異常值敏感,因?yàn)樗鼈儠?huì)影響質(zhì)心的計(jì)算。

六、源代碼如下

#include <iostream>  
#include <vector>  
#include <cmath>  
#include <limits>  
#include <algorithm>  
  
struct Point {  
    double x, y;  
    Point(double x = 0, double y = 0) : x(x), y(y) {}  
};  
  
double distance(const Point& a, const Point& b) {  
    return std::hypot(a.x - b.x, a.y - b.y);  
}  
  
Point centroid(const std::vector<Point>& cluster) {  
    double sum_x = 0, sum_y = 0;  
    for (const auto& point : cluster) {  
        sum_x += point.x;  
        sum_y += point.y;  
    }  
    return Point(sum_x / cluster.size(), sum_y / cluster.size());  
}  
  
void kmeans(std::vector<Point>& data, int k, int max_iterations) {  
    std::vector<Point> centroids(k);  
    std::vector<std::vector<Point>> clusters(k);  
      
    // 隨機(jī)化第一個(gè)質(zhì)點(diǎn)
    for (int i = 0; i < k; ++i) {  
        centroids[i] = data[rand() % data.size()];  
    }  
      
    for (int iter = 0; iter < max_iterations; ++iter) {   
        for (const auto& point : data) {  
            double min_distance = std::numeric_limits<double>::max();  
            int cluster_index = 0;  
            for (int i = 0; i < k; ++i) {  
                double dist = distance(point, centroids[i]);  
                if (dist < min_distance) {  
                    min_distance = dist;  
                    cluster_index = i;  
                }  
            }  
            clusters[cluster_index].push_back(point);  
        }  
          
        // 清除前一個(gè)的質(zhì)點(diǎn)
        for (auto& cluster : clusters) {  
            cluster.clear();  
        }  
          
        // 重新計(jì)算質(zhì)點(diǎn) 
        for (int i = 0; i < data.size(); ++i) {  
            int cluster_index = 0;  
            double min_distance = std::numeric_limits<double>::max();  
            for (int j = 0; j < k; ++j) {  
                double dist = distance(data[i], centroids[j]);  
                if (dist < min_distance) {  
                    min_distance = dist;  
                    cluster_index = j;  
                }  
            }  
            clusters[cluster_index].push_back(data[i]);  
        }  
          
        std::vector<Point> new_centroids(k);  
        for (int i = 0; i < k; ++i) {  
            new_centroids[i] = centroid(clusters[i]);  
        }  
           
        bool converged = true;  
        for (int i = 0; i < k; ++i) {  
            if (distance(centroids[i], new_centroids[i]) > 1e-6) {  
                converged = false;  
                break;  
            }  
        }  
          
        if (converged) {  
            break;  
        }  
          
        centroids = new_centroids;  
    }  
      
    // 輸出結(jié)果  
    for (int i = 0; i < k; ++i) {  
        std::cout << "Cluster " << i + 1 << " centroid: (" << centroids[i].x << ", " << centroids[i].y << ")" << std::endl;  
        for (const auto& point : clusters[i]) {  
            std::cout << "(" << point.x << ", " << point.y << ") ";  
        }  
        std::cout << std::endl;  
    }  
}  
  
int main() {  
    srand(time(nullptr)); // 隨機(jī)數(shù)種子,可以使用隨機(jī)數(shù)生成數(shù)據(jù)集
      
    std::vector<Point> data = {  
        // 數(shù)據(jù)集 
        {2.0, 10.0}, {2.5, 8.4}, {5.0, 8.0}, {8.0, 8.0}, {1.0, 0.6},  
        {9.0, 11.0}, {8.0, 5.0}, {1.0, 4.0}, {4.0, 6.0}, {6.0, 9.0}  
    };  
      
    int k = 2; // 集群數(shù)量 
    int max_iterations = 5; // 迭代次數(shù)
      
    kmeans(data, k, max_iterations);  
      
    return 0;  
}


責(zé)任編輯:華軒 來(lái)源: 鯊魚編程
相關(guān)推薦

2012-08-09 09:57:54

K-means

2017-09-12 16:57:43

機(jī)器學(xué)習(xí)K-means算法Python

2012-05-08 16:29:32

K-meansJava算法

2021-03-03 10:08:40

數(shù)據(jù)算法技術(shù)

2012-06-14 09:46:55

數(shù)據(jù)分析

2018-04-25 08:10:50

算法k-means代碼

2011-04-11 17:08:16

階乘算法C++

2010-02-04 11:23:25

C++反射機(jī)制

2021-12-21 15:31:10

C++語(yǔ)言指針

2011-04-20 09:50:45

Virtual

2010-02-01 14:21:24

C++初始化列表

2010-02-06 13:42:36

C++單件模式

2010-02-02 11:16:28

C++異常

2023-11-09 23:56:21

2010-01-18 16:17:53

C++代碼

2024-01-22 10:49:55

C++for循環(huán)

2023-12-24 12:56:14

C++函數(shù)語(yǔ)言

2010-02-01 17:02:53

C++產(chǎn)生隨機(jī)數(shù)

2024-01-18 10:27:30

C++引用函數(shù)

2011-03-30 17:20:18

C++引用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)