やっとこさ、ソースコードをうp
とりあえず、ひどいコードながら完成。javaからC++へ直すの面倒くさかった~
だけどまだまだ、直せるところいっぱいあるよ~w
しかし思ったのだが、これは本当にk-means法なのだろうか?
さてといきなりだが、ちょっとクラスタリングの説明。
今回やっているのは、ハード面のクラスタリングではないです。
簡単に言うと、データ解析手法の一種です。(以下、クラスタリング→クラスター分析)
ただ、普通のデータ解析と違ってクラスター分析は、外的な基準をなしに自動的に分類します。
基準なしといっても結局ある程度はプログラム上で与えるけど・・・
んで、その代表的なやり方にウォード法とk-means法があります。
その中のk-means法を実装したわけです。
実装の理由?・・・理由なんてありません。
そこにアルゴリズムがあるから実装しただけです。
(まぁ、できてるのか知らないけど・・・)
// K_Means.cpp
// author Takeshi Miyamoto
#include <cstdlib> // srand, rand使用
#include <ctime> // time使用
#include <iostream> // cout, endl使用
#include <vector> // vectorを使用
#include <sstream> // ostringstream使用
// クラス定義
// 人間クラス
class Person
{
private:
// 体重
float weight_;
// 身長
float height_;
public:
// コンストラクタ
Person()
:weight_( 0 )
,height_( 0 )
{
}
// コンストラクタ
Person( float weight, float height )
:weight_( weight )
,height_( height )
{
}
// 体重の取得
float get_weight()
{
return weight_;
}
// 身長の取得
float get_height()
{
return height_;
}
// toStringメソッド
const std::string toString()
{
std::ostringstream oss;
oss << "weight=" << weight_ << ", height=" << height_;
return oss.str();
}
};
// 補助関数群クラス
class Utility
{
public:
// 最小値
static float minimum( float a, float b )
{
return ((a < b) ? a : b);
}
// 最大値
static float maxmum( float a, float b )
{
return ((a > b) ? a : b);
}
};
// メイン処理
int main()
{
// 乱数の種を設定
srand( (unsigned)time( NULL ) );
// 体重、身長の表
Person persons[] =
{
// 数値は適当
// 体重 身長
Person( 45.0f, 160.0f ),
Person( 46.0f, 161.0f ),
Person( 47.0f, 163.0f ),
Person( 48.0f, 166.0f ),
Person( 49.0f, 170.0f ),
Person( 50.0f, 175.0f ),
Person( 45.0f, 171.0f ),
Person( 46.0f, 161.0f ),
Person( 47.0f, 177.0f ),
Person( 48.0f, 168.0f ),
Person( 49.0f, 164.0f ),
Person( 49.0f, 173.0f ),
Person( 55.0f, 171.0f ),
Person( 60.0f, 178.0f ),
Person( 65.0f, 180.0f ),
Person( 70.0f, 181.0f ),
Person( 75.0f, 185.0f ),
Person( 55.0f, 183.0f ),
Person( 60.0f, 186.0f ),
Person( 65.0f, 189.0f ),
Person( 70.0f, 165.0f ),
Person( 65.0f, 170.0f ),
};
const int person_count = sizeof( persons ) / sizeof( persons[0] );
std::cout << "person count=" << person_count << std::endl;
// 内容表示
std::cout << "体重、身長を表示する" << std::endl;
for( int i = 0; i < person_count; ++i )
{
std::cout << "[" << i << "]=(" << persons[i].toString() << ")" << std::endl;
}
std::cout << std::endl;
// クラスタリング[ここから]
// ランダムに仮のクラスター値(cluster_count)を決定する(今回は適当に3としとく)
// 本当はこのクラスター値(cluster_count)の決定はかなり重要で、色々やんなきゃいけないらしい(今回は端折るw)
const int cluster_count = 3;
int seed[cluster_count];
int index = 0;
while( index != cluster_count )
{
// ランダム値を取得
int rand_number = rand() % (person_count - 1);
// クラスター値に同一値が存在するか調べる
bool judge = false;
for( int i = 0; i < cluster_count; ++i )
{
if( seed[i] == rand_number ) { judge = true; }
}
// 同一値が存在するか?
if( judge == false )
{
seed[index++] = rand_number;
}
}
// ランダム値を表示する
std::cout << "ランダム値を表示する" << std::endl;
for( int i = 0; i < cluster_count; ++i )
{
std::cout << seed[i] << std::endl;
}
std::cout << std::endl;
// クラスタリング後の格納vector
std::vector< Person > vec[cluster_count];
vec->clear();
// 一回目のクラスタリング
for( int i = 0; i < person_count; ++i )
{
// 各クラスター(seed[?])のどこに一番近いか調べる為、二点間の距離を算出する
float weight0 =
(persons[seed[0]].get_weight() - persons[i].get_weight()) * (persons[seed[0]].get_weight() - persons[i].get_weight());
float height0 =
(persons[seed[0]].get_height() - persons[i].get_height()) * (persons[seed[0]].get_height() - persons[i].get_height());;
float p0 = weight0 + height0;
float weight1 =
(persons[seed[1]].get_weight() - persons[i].get_weight()) * (persons[seed[1]].get_weight() - persons[i].get_weight());
float height1 =
(persons[seed[1]].get_height() - persons[i].get_height()) * (persons[seed[1]].get_height() - persons[i].get_height());;
float p1 = weight1 + height1;
float weight2 =
(persons[seed[2]].get_weight() - persons[i].get_weight()) * (persons[seed[2]].get_weight() - persons[i].get_weight());
float height2 =
(persons[seed[2]].get_height() - persons[i].get_height()) * (persons[seed[2]].get_height() - persons[i].get_height());;
float p2 = weight2 + height2;
// 二点間の処理と距離の最小値を表示する
std::cout << "[" << i << "]=(p1=" << p0 << "), (p2=" << p1 << "), (p3=" << p2 << ")" << std::endl;
std::cout << Utility::minimum( p0, Utility::minimum( p1, p2 ) ) << std::endl;
// 最小値をvectorへ格納する(う~ん、とても美しくない糞コードだ・・・)
// p0とp1のどちらが小さいか?
if( p0 < p1 )
{
// p0とp2はどちらが小さいか?
if( p0 < p2 )
{
vec[0].push_back( persons[i] );
}
else
{
vec[2].push_back( persons[i] );
}
}
else
{
// p1とp2はどちらが小さいか?
if( p1 < p2 )
{
vec[1].push_back( persons[i] );
}
else
{
vec[2].push_back( persons[i] );
}
}
}
std::cout << std::endl;
// vectorの中身を確認する
for( unsigned int i = 0; i < cluster_count; ++i )
{
unsigned int size = vec[i].size();
for( unsigned int j = 0; j < size; ++j )
{
std::cout << "(" << vec[i][j].toString() << ") ";
}
std::cout << std::endl;
}
// 各クラスター(vector)から新しい重心を求める
Person avg_person[cluster_count];
for( unsigned int i = 0; i < cluster_count; ++i )
{
// 平均を求める
unsigned int size = vec[i].size();
float avg_weight = 0, avg_height = 0;
for( unsigned int j = 0; j < size; ++j )
{
avg_weight += vec[i][j].get_weight();
avg_height += vec[i][j].get_height();
}
avg_weight /= size;
avg_height /= size;
avg_person[i] = Person( avg_weight, avg_height );
}
// 重心の数値を確認する
for( int i = 0; i < cluster_count; ++i )
{
std::cout << "[" << i << "]=(" << avg_person[i].toString() << ")" << std::endl;
}
std::cout << std::endl;
// 前回と同じ重心の平均値になるまで繰り返す(あ~、なんて冗長なプログラム・・・同じことを二回もやってる)
// 今回は、必ずループが終わるように最高でも10回目までしか平均を取らない(一応)
bool is_equals_avg = false;
int ten_count = 0;
do
{
// クラスタリング後の格納vectorをクリアする
for( int i = 0; i < cluster_count; ++i )
{
vec[i].clear();
}
// クラスタリングする(以下しばらく同じことやってる冗長部分)
for( int i = 0; i < person_count; ++i )
{
// 各クラスター(seed[?])のどこに一番近いか調べる為、二点間の距離を算出する
float weight0 =
(avg_person[0].get_weight() - persons[i].get_weight()) * (avg_person[0].get_weight() - persons[i].get_weight());
float height0 =
(avg_person[0].get_height() - persons[i].get_height()) * (avg_person[0].get_height() - persons[i].get_height());;
float p0 = weight0 + height0;
float weight1 =
(avg_person[1].get_weight() - persons[i].get_weight()) * (avg_person[1].get_weight() - persons[i].get_weight());
float height1 =
(avg_person[1].get_height() - persons[i].get_height()) * (avg_person[1].get_height() - persons[i].get_height());;
float p1 = weight1 + height1;
float weight2 =
(avg_person[2].get_weight() - persons[i].get_weight()) * (avg_person[2].get_weight() - persons[i].get_weight());
float height2 =
(avg_person[2].get_height() - persons[i].get_height()) * (avg_person[2].get_height() - persons[i].get_height());;
float p2 = weight2 + height2;
// 最小値はどれか?
// p0とp1のどちらが小さいか?
if( p0 < p1 )
{
// p0とp2はどちらが小さいか?
if( p0 < p2 )
{
vec[0].push_back( persons[i] );
}
else
{
vec[2].push_back( persons[i] );
}
}
else
{
// p1とp2はどちらが小さいか?
if( p1 < p2 )
{
vec[1].push_back( persons[i] );
}
else
{
vec[2].push_back( persons[i] );
}
}
}
// リストの中を確認する
for( unsigned int i = 0; i < cluster_count; ++i )
{
unsigned int size = vec[i].size();
for( unsigned int j = 0; j < size; ++j )
{
std::cout << "(" << vec[i][j].toString() << ") ";
}
std::cout << std::endl;
}
// 前の平均値を格納
Person prev_avg_person[cluster_count];
for( int i = 0; i < cluster_count; ++i )
{
prev_avg_person[i] = avg_person[i];
}
// 重心の再計算
for( unsigned int i = 0; i < cluster_count; ++i )
{
// 平均を求める
unsigned int size = vec[i].size();
float avg_weight = 0, avg_height = 0;
for( unsigned int j = 0; j < size; ++j )
{
avg_weight += vec[i][j].get_weight();
avg_height += vec[i][j].get_height();
}
avg_weight /= size;
avg_height /= size;
avg_person[i] = Person( avg_weight, avg_height );
}
// 重心の数値を確認する
for( int i = 0; i < cluster_count; ++i )
{
std::cout << "[" << i << "]=(" << avg_person[i].toString() << ")" << std::endl;
}
std::cout << std::endl;
// 前と同じ重心の平均値か?(どれか一つでも相違があればtrue)
is_equals_avg = false;
for( int i = 0; i < cluster_count; ++i )
{
if( (avg_person[i].get_weight() != prev_avg_person[i].get_weight()) ||
(avg_person[i].get_height() != prev_avg_person[i].get_height()) )
{
is_equals_avg = true;
}
}
std::cout << "is equals avg=" << ((is_equals_avg == 0 ? "true" : "false")) << std::endl;
// カウントアップ
std::cout << "loop count=" << ten_count << std::endl;
++ten_count;
// 改行
std::cout << std::endl;
} while( is_equals_avg == true && ten_count < 10 );
// クラスタリング[ここまで]
return 0;
}
最終更新:2009年11月23日 19:17