Консультация № 160240
11.02.2009, 21:53
0.00 руб.
0 3 1
помогите реализовать такой алгоритм кластеризации k-середных
для матрицы на С# или С++Builder

алгоритм представляет собой итерационную процедуру, в которой выполняються следующие шаги.
1.Выбирается число кластеров k
Из исходного множества данных случайным образом выбираются k записей,которые будут служить начальными центрами кластером
2.ля каждой записи исходной выборки определяется ближайший к ней центр кластера.
При етом записи, "притянутые"определенным центром образуют начальные кластеры
3.Вычисляются центроиды - центры тяжести кластеров. Каждый центроид - это вектор, элементы которого представлят собой средние значения признаков, вычисленные по всем записям кластера.
4.Затем центр кластера смещается в его центроид.
затем 3-й и 4-й шаги итеративно повторяются. Очевидно,что на каждой итерации происходит изменение границ кластеров и смещение их
центров.В результате минимизируется расстояние между элементами внутри кластеров. Остановка алгоритма производится тогда, когда
границы кластеров и расположения центроидов не перестанут изменятся отитерации к итерации, т.е.на каждой итерации в каждом кластере будет
оставаться один и тот же набор записей.

алгоритм реализовать в процедуре(функции), входные данные -матриця, результат - число кластеров(и содержание кластеров)

Обсуждение

Неизвестный
16.02.2009, 15:26
общий
17.02.2009, 00:49
это ответ
Здравствуйте, Иванка!
Сразу разъясню некоторые моменты:
1)В программе матрица генерируется автоматически при помощи System.Random. Т.е. это самый тяжелый случай для кластеризации. На кореллирующихся данных результат будет соответственно лучше.
2)Заранее число кластеров неизвестно и выбирать его случайным образом смысла нет. Это не соответствует практике. Вместо этого вводится допустимое максимальное отклонение записи от центра кластера и начинается перебор кластеров от 1 и до того момента пока максимальное отклонение не станет меньше заданного.
3)Начальные значения для центроидов выбираются не совсем случайно. Для упорядоченных матриц случайный выбор это плохой вариант, а для неупорядоченных разницы нет никакой. Но если же Вам нужно именно случайный выбор то измените функцию InitialCentroids() на свое усмотрение.
4)Результат работы метода кластеризации это класс с определенными полями, а именно:Size-к-во кластеров;AverageDeviation,MaxDeviation-среднее и максимальное отклонение от записей от центра кластера. Кроме этого добавлен индексатор аргументом которого является номер кластера, а результат список номеров записей в матрице(чтоб постоянно не копировать всю матрицу)
5)Элементом кластера является запись(строка) матрицы, а не отдельный элемент. Иначе нет смысла в матрице вообще. Можно обойтись одномерным массивом.

Малый объем допустимого приложения не позволяет здесь выложить код. Поэтому Вы можете скачать весь проект на http://rapidshare.com/files/198745652/160240.rar

Желаю удачи!

Добавлена информация из мини-форума по просьбе эксперта:
Исправил некоторые ошибки. Пожалуй, это можно считать окончательным вариантом.
Кроме того. Если к примеру начальные центроиды расположены близко друг к другу или совсем равны(например все строки матрицы одинаковые), а Вы пытаетесь разбить на много кластеров то реально используется только один центроид, но расчет в первой программе шел все равно по всем центроидам. Это не влияло на результат, но было совершенно излишне. Здесь ситуация исправлена.
Проверялась на .NET 3.5
Код:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ClusterAnalyze;

namespace _160240
{
class Program
{
static void Main(string[] args)
{
uint rows, cols;
rows = InputUInt("Введите количество строк матрицы:");
cols = InputUInt("Введите количество столбцов матрицы:");
double[,] matrix;
try
{
matrix = new double[rows, cols];
Random rndGen = new Random();
for (uint i = 0; i < rows; i++)
{
for (uint j = 0; j < cols; j++)
{
matrix[i, j] = rndGen.NextDouble() * 100 - 50;
}
}
Console.WriteLine("Исходная матрица:");
for (uint i = 0; i < rows; i++)
{
for (uint j = 0; j < cols; j++)
{
Console.Write("{0,7:F3} ", matrix[i, j]);
}
Console.WriteLine();
}
double deviation = InputDouble("Введите желаемую погрешность:");
try
{
ClusterAnalyze.ClusterResult result = KMeans.Solve(matrix, rows, deviation);
uint clusterNo = 0;
foreach (ClusterAnalyze.ListOfRecords cluster in result)
{
Console.WriteLine("Кластер {0}", ++clusterNo);
foreach (uint line in cluster)
{
for (uint i = 0; i < cols; i++)
{
Console.Write("{0,7:F3} ", matrix[line, i]);
}
Console.WriteLine();
}
}
Console.WriteLine("Среднее отклонение:{0}", result.AverageDeviation);
Console.WriteLine("Максимальное отклонение:{0}", result.MaxDeviation);
}
catch (ClusterAnalyze.ClusterAnalyzeException ex)
{
ErrorMsg(ex.Message);
}
}
catch (OutOfMemoryException)
{
ErrorMsg("Не могу выделить память для матрицы");
}
Console.WriteLine("Нажмите любую клавишу для выхода");
Console.ReadKey();
}
/// <summary>
/// Ввод числа типа double
/// </summary>
/// <param name="msg">Сообщение</param>
/// <returns>Число</returns>
static double InputDouble(string msg)
{
while (true)
{
Console.Write(msg);
try
{
double val = Convert.ToDouble(Console.ReadLine());
return val;
}
catch (FormatException)
{ }
catch (OverflowException)
{ }
ErrorMsg("Неверный ввод: ожидается действительное число");
}
}
/// <summary>
/// Ввод числа типа uint
/// </summary>
/// <param name="msg">Сообщение</param>
/// <returns>Число</returns>
static uint InputUInt(string msg)
{
while (true)
{
Console.Write(msg);
try
{
uint val = Convert.ToUInt32(Console.ReadLine());
return val;
}
catch (FormatException)
{ }
catch (OverflowException)
{ }
ErrorMsg("Неверный ввод: ожидается целое положительное число");
}
}
/// <summary>
/// Ввод числа типа uint в заданном диапазоне
/// </summary>
/// <param name="msg">Сообщение</param>
/// <param name="lo">Нижняя граница</param>
/// <param name="high">Верхняя граница</param>
/// <returns>Число</returns>
static uint InputUInt(string msg, uint lo, uint high)
{
while (true)
{
uint val = InputUInt(msg);
if (val >= lo && val <= high) return val;
ErrorMsg(String.Format("Неверный ввод: ожидается значение в интервале {0}..{1}", lo, high));
}
}
/// <summary>
/// Выводит сообщение об ошибке
/// </summary>
/// <param name="msg">Сообщение</param>
static void ErrorMsg(string msg)
{
ConsoleColor color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg);
Console.ForegroundColor = color;
}
}
}

namespace ClusterAnalyze
{
/// <summary>
/// Класс для кластерного анализа
/// </summary>
static class KMeans
{
/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="matrix">Исходная матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Результат анализа</returns>
public static ClusterResult Solve(double[,] matrix, uint numCluster)
{
return Clustering(Matrix2JadMatrix(matrix), numCluster);
}
/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="matrix">Исходная матрица</param>
/// <param name="maxNumCluster">Ограничение на количество кластеров сверху</param>
/// <param name="deviation">Допустимое отклонение записи от центроида</param>
/// <returns>Результат анализа</returns>
public static ClusterResult Solve(double[,] matrix, uint maxNumCluster, double deviation)
{
if (deviation == 0) deviation = 1e-10;
else deviation = deviation < 0 ? -deviation : deviation;
ClusterResult res;
double[][] jadMatrix = Matrix2JadMatrix(matrix);
uint clusters = 0;
do
{
res = Clustering(jadMatrix, ++clusters);
} while ((clusters < maxNumCluster) && (res.MaxDeviation > deviation));
GC.Collect();
return res;
}
/// <summary>
/// Собственно решение здесь
/// </summary>
/// <param name="matrix">Матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Результат анализа</returns>
private static ClusterResult Clustering(double[][] matrix, uint numCluster)
{
uint rows = (uint)matrix.Length;
// Количество кластеров должно быть не менее 1х и не более чем записей
if (numCluster > rows || numCluster < 1)
throw new ClusterAnalyzeException("ClusterResult Clustering(double[][] matrix, uint numCluster):\n" +
"Количество кластеров меньше 1 или больше чем число строк в матрице");
uint cols = (uint)matrix[0].Length;
// Задаем центроиды для начала
double[][] centroids = InitialCentroids(matrix, numCluster);
bool isCont;
// Словарь ключями которого являются номера центроидов, а значениями списки записей в матрице
Dictionary<uint, ListOfRecords> clustersDict;
double averageDeviation, maxDeviation;
do
{
clustersDict = new Dictionary<uint, ListOfRecords>();
// Отклонения от центроидов
averageDeviation = 0;
maxDeviation = 0;
// Для заданных центроидов определяем к какому кластеру относятся записи
for (uint i = 0; i < rows; i++)
{
// Индекс ближайшего
uint nearest = 0;
// Минимальная дистанция
double minDistance = Range(centroids[0], matrix[i]);
// Перебор центроидов
for (uint j = 1; j < numCluster; j++)
{
double distance = Range(centroids[j], matrix[i]);
if (distance < minDistance)
{
minDistance = distance;
nearest = j;
}
}
averageDeviation += minDistance / rows;
maxDeviation = maxDeviation < minDistance ? minDistance : maxDeviation;
// Добавляем в словарь
if (!clustersDict.ContainsKey(nearest))
{
clustersDict.Add(nearest, new ListOfRecords());
}
clustersDict[nearest].Add(i);
}
// Если есть не рабочие центроиды то избавимся от них
if (clustersDict.Count != numCluster)
{
numCluster = (uint)clustersDict.Count;
double[][] tmpCentroids = new double[numCluster][];
Dictionary<uint, ListOfRecords> tmpClusterDict = new Dictionary<uint, ListOfRecords>();
uint newNo = 0;
foreach (uint oldNo in clustersDict.Keys)
{
tmpCentroids[newNo] = centroids[oldNo];
tmpClusterDict.Add(newNo, clustersDict[oldNo]);
newNo++;
}
centroids = tmpCentroids;
clustersDict = tmpClusterDict;
}
// Флаг продолжения итераций
isCont = false;
// Считаем новые центроиды
double[][] newCentroids = new double[numCluster][];
foreach (uint i in clustersDict.Keys)
{
newCentroids[i] = new double[cols];
uint itemsInCluster = (uint)clustersDict[i].Count;
foreach (uint line in clustersDict[i])
{
for (uint j = 0; j < cols; j++)
{
newCentroids[i][j] += matrix[line][j] / itemsInCluster;
}
}
// Сравниваем со старым центроидом и копируем в него, если есть различие
for (uint j = 0; j < cols; j++)
{
if (centroids[i][j] != newCentroids[i][j])
{
centroids[i][j] = newCentroids[i][j];
isCont = true;
}
}
}
} while (isCont);
return new ClusterResult(clustersDict, averageDeviation, maxDeviation);
}
/// <summary>
/// Возвращает начальные значения для центроидов
/// </summary>
/// <param name="matrix">Матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Двухмерный массив центроидов</returns>
private static double[][] InitialCentroids(double[][] matrix, uint numCluster)
{
uint rows = (uint)matrix.Length,
cols = (uint)matrix[0].Length;
double[][] centroids = new double[numCluster][];
for (uint i = 0; i < numCluster; i++)
{
centroids[i] = new double[cols];
uint line = i * rows / numCluster;
for (uint j = 0; j < cols; j++)
{
centroids[i][j] = matrix[line][j];
}
}
return centroids;
}
/// <summary>
/// Вычисляет среднеквадратичное отклонение между двумя векторами
/// </summary>
/// <param name="array1">1й вектор</param>
/// <param name="array2">2й вектор</param>
/// <returns>Отклонение</returns>
private static double Range(double[] array1, double[] array2)
{
uint len;
if ((len = (uint)array1.Length) != array2.Length)
throw new ClusterAnalyzeException("double Range(double[] array1, double[] array2):\n" +
"Массивы имеют разную длину");
double sum = 0;
for (uint i = 0; i < len; i++) sum += Math.Pow(array1[i] - array2[i], 2);
return Math.Sqrt(sum / len);
}
/// <summary>
/// Преобразует массив вида [,] к [][]
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
private static double[][] Matrix2JadMatrix(double[,] matrix)
{
uint rows = (uint)matrix.GetLength(0),
cols = (uint)matrix.GetLength(1);
double[][] jadMatrix = new double[rows][];
for (uint i = 0; i < rows; i++)
{
jadMatrix[i] = new double[cols];
for (uint j = 0; j < cols; j++)
{
jadMatrix[i][j] = matrix[i, j];
}
}
return jadMatrix;
}
}
/// <summary>
/// Результат анализа возвращается в виде этого класса
/// </summary>
public class ClusterResult : IEnumerable
{
/// <summary>
/// Конструктор класса
/// </summary>
/// <param name="dict"></param>
/// <param name="averageDeviation">Среднее отклонение</param>
/// <param name="maxDeviation">Максимальное отклонение</param>
public ClusterResult(Dictionary<uint, ListOfRecords> dict, double averageDeviation, double maxDeviation)
{
AverageDeviation = averageDeviation;
MaxDeviation = maxDeviation;
data = dict.Values.ToArray();
}
/// <summary>
/// Список индексов записей в кластере
/// </summary>
/// <param name="Index">Номер кластера</param>
/// <returns>Список индексов записей</returns>
public ListOfRecords this[uint Index]
{
get
{
return data[Index];
}
}
/// <summary>
/// Количество кластеров
/// </summary>
public uint Size
{
get
{
return (uint)data.Length;
}
}
/// <summary>
/// Возвращает тип перечислителя
/// </summary>
/// <returns>Перечислитель</returns>
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
}
/// <summary>
/// Средне отклонение записей от центроидов кластеров
/// </summary>
public readonly double AverageDeviation;
/// <summary>
/// Максимальное отклонение от центроидов кластеров
/// </summary>
public readonly double MaxDeviation;
private readonly ListOfRecords[] data;
}
/// <summary>
/// Класс-исключение для перехвата ошибок возникающих в классе ClusterAnalyze
/// </summary>
public class ClusterAnalyzeException : ApplicationException
{
/// <summary>
/// Конструктор
/// </summary>
/// <param name="msg">Сообщение об ошибке</param>
public ClusterAnalyzeException(string msg)
: base(msg)
{ }
}
/// <summary>
/// Класс список номеров записей в файле. Служит исключительно как синоним базового класса
/// </summary>
public class ListOfRecords : List<uint>
{ }
}


http://rapidshare.com/files/198909615/160240-2.rar
Неизвестный
16.02.2009, 19:35
общий
Исправил некоторые ошибки. Пожалуй, это можно считать окончательным вариантом.
Кроме того. Если к примеру начальные центроиды расположены близко друг к другу или совсем равны(например все строки матрицы одинаковые), а Вы пытаетесь разбить на много кластеров то реально используется только один центроид, но расчет в первой программе шел все равно по всем центроидам. Это не влияло на результат, но было совершенно излишне. Здесь ситуация исправлена.
Проверялась на .NET 3.5
Код:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ClusterAnalyze;

namespace _160240
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Кластерный анализ методом K-средних";
uint rows, cols;
rows = InputUInt("Введите количество строк матрицы:");
cols = InputUInt("Введите количество столбцов матрицы:");
double[,] matrix;
try
{
matrix = new double[rows, cols];
Random rndGen = new Random();
for (uint i = 0; i < rows; i++)
{
for (uint j = 0; j < cols; j++)
{
matrix[i, j] = rndGen.NextDouble() * 100 - 50;
//matrix[i, j] = InputDouble(String.Format("[{0},{1}]=", i + 1, j + 1));
}
}
Console.WriteLine("Исходная матрица:");
for (uint i = 0; i < rows; i++)
{
for (uint j = 0; j < cols; j++)
{
Console.Write("{0,7:F3} ", matrix[i, j]);
}
Console.WriteLine();
}
double deviation = InputDouble("Введите желаемую погрешность:");
try
{
DateTime sTime = DateTime.Now;
ClusterAnalyze.ClusterResult result = KMeans.Solve(matrix, rows, deviation, show);
DateTime eTime = DateTime.Now;
TimeSpan runTime = eTime - sTime;
uint clusterNo = 0;
foreach (ClusterAnalyze.ListOfRecords cluster in result)
{
Console.WriteLine("Кластер {0}", ++clusterNo);
foreach (uint line in cluster)
{
for (uint i = 0; i < cols; i++)
{
Console.Write("{0,7:F3} ", matrix[line, i]);
}
Console.WriteLine();
}
}
Console.WriteLine("Среднее отклонение:{0}", result.AverageDeviation);
Console.WriteLine("Максимальное отклонение:{0}", result.MaxDeviation);
Console.WriteLine("Время работы:{0}(сек)", runTime.TotalSeconds);
}
catch (ClusterAnalyze.ClusterAnalyzeException ex)
{
ErrorMsg(ex.Message);
}
}
catch (OutOfMemoryException)
{
ErrorMsg("Не могу выделить память для матрицы");
}
Console.WriteLine("Нажмите любую клавишу для выхода");
Console.ReadKey();
}
static int propeller = 0;
static void show(double x)
{
Console.Write("\r{0} {1}%", "\\|/-"[propeller = (propeller + 1) % 4], (int)(x * 100));
if (x < 0) Console.Write("\r \r");
}
/// <summary>
/// Ввод числа типа double
/// </summary>
/// <param name="msg">Сообщение</param>
/// <returns>Число</returns>
static double InputDouble(string msg)
{
while (true)
{
Console.Write(msg);
try
{
double val = Convert.ToDouble(Console.ReadLine());
return val;
}
catch (FormatException)
{ }
catch (OverflowException)
{ }
ErrorMsg("Неверный ввод: ожидается действительное число");
}
}
/// <summary>
/// Ввод числа типа uint
/// </summary>
/// <param name="msg">Сообщение</param>
/// <returns>Число</returns>
static uint InputUInt(string msg)
{
while (true)
{
Console.Write(msg);
try
{
uint val = Convert.ToUInt32(Console.ReadLine());
return val;
}
catch (FormatException)
{ }
catch (OverflowException)
{ }
ErrorMsg("Неверный ввод: ожидается целое положительное число");
}
}
/// <summary>
/// Ввод числа типа uint в заданном диапазоне
/// </summary>
/// <param name="msg">Сообщение</param>
/// <param name="lo">Нижняя граница</param>
/// <param name="high">Верхняя граница</param>
/// <returns>Число</returns>
static uint InputUInt(string msg, uint lo, uint high)
{
while (true)
{
uint val = InputUInt(msg);
if (val >= lo && val <= high) return val;
ErrorMsg(String.Format("Неверный ввод: ожидается значение в интервале {0}..{1}", lo, high));
}
}
/// <summary>
/// Выводит сообщение об ошибке
/// </summary>
/// <param name="msg">Сообщение</param>
static void ErrorMsg(string msg)
{
ConsoleColor color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg);
Console.ForegroundColor = color;
}
}
}

namespace ClusterAnalyze
{
/// <summary>
/// Класс для кластерного анализа
/// </summary>
static class KMeans
{
/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="matrix">Исходная матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Результат анализа</returns>
public static ClusterResult Solve(double[,] matrix, uint numCluster)
{
return Clustering(Matrix2JadMatrix(matrix), numCluster);
}
/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="matrix">Исходная матрица</param>
/// <param name="maxNumCluster">Ограничение на количество кластеров сверху</param>
/// <param name="deviation">Допустимое отклонение записи от центроида</param>
/// <returns>Результат анализа</returns>
public static ClusterResult Solve(double[,] matrix, uint maxNumCluster, double deviation)
{
if (deviation == 0) deviation = 1e-10;
else deviation = deviation < 0 ? -deviation : deviation;
ClusterResult res;
double[][] jadMatrix = Matrix2JadMatrix(matrix);
uint clusters = 0;
do
{
res = Clustering(jadMatrix, ++clusters);
} while ((clusters < maxNumCluster) && (res.MaxDeviation > deviation));
GC.Collect();
return res;
}
/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="matrix">Исходная матрица</param>
/// <param name="maxNumCluster">Ограничение на количество кластеров сверху</param>
/// <param name="deviation">Допустимое отклонение записи от центроида</param>
/// <param name="statusFunc">Делегат вида void func(double x) для показа статуса выполнения</param>
/// <returns>Результат анализа</returns>
public static ClusterResult Solve(double[,] matrix, uint maxNumCluster, double deviation, ShowStatus statusFunc)
{
if (deviation == 0) deviation = 1e-10;
else deviation = deviation < 0 ? -deviation : deviation;
ClusterResult res;
double[][] jadMatrix = Matrix2JadMatrix(matrix);
uint clusters = 0;
do
{
res = Clustering(jadMatrix, ++clusters);
if (res.MaxDeviation > 0) statusFunc(deviation / res.MaxDeviation);
} while ((clusters < maxNumCluster) && (res.MaxDeviation > deviation));
statusFunc(-1);
GC.Collect();
return res;
}
/// <summary>
/// Собственно решение здесь
/// </summary>
/// <param name="matrix">Матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Результат анализа</returns>
private static ClusterResult Clustering(double[][] matrix, uint numCluster)
{
uint rows = (uint)matrix.Length;
// Количество кластеров должно быть не менее 1х и не более чем записей
if (numCluster > rows || numCluster < 1)
throw new ClusterAnalyzeException("ClusterResult Clustering(double[][] matrix, uint numCluster):\n" +
"Количество кластеров меньше 1 или больше чем число строк в матрице");
uint cols = (uint)matrix[0].Length;
// Задаем центроиды для начала
double[][] centroids = InitialCentroids(matrix, numCluster);
bool isCont;
// Словарь ключями которого являются номера центроидов, а значениями списки записей в матрице
Dictionary<uint, ListOfRecords> clustersDict;
double averageDeviation, maxDeviation;
do
{
clustersDict = new Dictionary<uint, ListOfRecords>();
// Отклонения от центроидов
averageDeviation = 0;
maxDeviation = 0;
// Для заданных центроидов определяем к какому кластеру относятся записи
for (uint i = 0; i < rows; i++)
{
// Индекс ближайшего
uint nearest = 0;
// Минимальная дистанция
double minDistance = Range(centroids[0], matrix[i]);
// Перебор центроидов
for (uint j = 1; j < numCluster; j++)
{
double distance = Range(centroids[j], matrix[i]);
if (distance < minDistance)
{
minDistance = distance;
nearest = j;
}
}
averageDeviation += minDistance / rows;
maxDeviation = maxDeviation < minDistance ? minDistance : maxDeviation;
// Добавляем в словарь
if (!clustersDict.ContainsKey(nearest))
{
clustersDict.Add(nearest, new ListOfRecords());
}
clustersDict[nearest].Add(i);
}
// Если есть не рабочие центроиды то избавимся от них
if (clustersDict.Count != numCluster)
{
numCluster = (uint)clustersDict.Count;
double[][] tmpCentroids = new double[numCluster][];
Dictionary<uint, ListOfRecords> tmpClusterDict = new Dictionary<uint, ListOfRecords>();
uint newNo = 0;
foreach (uint oldNo in clustersDict.Keys)
{
tmpCentroids[newNo] = centroids[oldNo];
tmpClusterDict.Add(newNo, clustersDict[oldNo]);
newNo++;
}
centroids = tmpCentroids;
clustersDict = tmpClusterDict;
}
// Флаг продолжения итераций
isCont = false;
// Считаем новые центроиды
double[][] newCentroids = new double[numCluster][];
for (uint i = 0; i < numCluster; ++i)
{
newCentroids[i] = new double[cols];
uint itemsInCluster = (uint)clustersDict[i].Count;
foreach (uint line in clustersDict[i])
{
for (uint j = 0; j < cols; j++)
{
newCentroids[i][j] += matrix[line][j] / itemsInCluster;
}
}
if (!isCont)
{
// Сравниваем со старым центроидом и устанавливаем флаг если есть различие
for (uint j = 0; j < cols; j++)
{
if (centroids[i][j] != newCentroids[i][j])
{
isCont = true;
break;
}
}
}
}
centroids = newCentroids;
} while (isCont);
return new ClusterResult(clustersDict, averageDeviation, maxDeviation);
}
/// <summary>
/// Возвращает начальные значения для центроидов
/// </summary>
/// <param name="matrix">Матрица</param>
/// <param name="numCluster">Количество кластеров</param>
/// <returns>Двухмерный массив центроидов</returns>
private static double[][] InitialCentroids(double[][] matrix, uint numCluster)
{
uint rows = (uint)matrix.Length,
cols = (uint)matrix[0].Length;
double[][] centroids = new double[numCluster][];
for (uint i = 0; i < numCluster; i++)
{
uint line = i * rows / numCluster;
for (uint j = 0; j < cols; j++)
{
centroids[i] = (double[])matrix[line].Clone();
}
}
return centroids;
}
/// <summary>
/// Вычисляет среднеквадратичное отклонение между двумя векторами
/// </summary>
/// <param name="array1">1й вектор</param>
/// <param name="array2">2й вектор</param>
/// <returns>Отклонение</returns>
private static double Range(double[] array1, double[] array2)
{
uint len;
if ((len = (uint)array1.Length) != array2.Length)
throw new ClusterAnalyzeException("double Range(double[] array1, double[] array2):\n" +
"Массивы имеют разную длину");
double sum = 0;
for (uint i = 0; i < len; i++) sum += Math.Pow(array1[i] - array2[i], 2);
return Math.Sqrt(sum / len);
}
/// <summary>
/// Преобразует массив вида [,] к [][]
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
private static double[][] Matrix2JadMatrix(double[,] matrix)
{
uint rows = (uint)matrix.GetLength(0),
cols = (uint)matrix.GetLength(1);
double[][] jadMatrix = new double[rows][];
for (uint i = 0; i < rows; i++)
{
jadMatrix[i] = new double[cols];
for (uint j = 0; j < cols; j++)
{
jadMatrix[i][j] = matrix[i, j];
}
}
return jadMatrix;
}
}
/// <summary>
/// Результат анализа возвращается в виде этого класса
/// </summary>
public class ClusterResult : IEnumerable
{
/// <summary>
/// Конструктор класса
/// </summary>
/// <param name="dict"></param>
/// <param name="averageDeviation">Среднее отклонение</param>
/// <param name="maxDeviation">Максимальное отклонение</param>
internal ClusterResult(Dictionary<uint, ListOfRecords> dict, double averageDeviation, double maxDeviation)
{
AverageDeviation = averageDeviation;
MaxDeviation = maxDeviation;
data = dict.Values.ToArray();
}
/// <summary>
/// Список индексов записей в кластере
/// </summary>
/// <param name="Index">Номер кластера</param>
/// <returns>Список индексов записей</returns>
public ListOfRecords this[uint Index]
{
get
{
return data[Index];
}
}
/// <summary>
/// Количество кластеров
/// </summary>
public uint Size
{
get
{
return (uint)data.Length;
}
}
/// <summary>
/// Возвращает тип перечислителя
/// </summary>
/// <returns>Перечислитель</returns>
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
}
/// <summary>
/// Средне отклонение записей от центроидов кластеров
/// </summary>
public readonly double AverageDeviation;
/// <summary>
/// Максимальное отклонение от центроидов кластеров
/// </summary>
public readonly double MaxDeviation;
private readonly ListOfRecords[] data;
}
/// <summary>
/// Класс-исключение для перехвата ошибок возникающих в классе ClusterAnalyze
/// </summary>
public class ClusterAnalyzeException : ApplicationException
{
/// <summary>
/// Конструктор
/// </summary>
/// <param name="msg">Сообщение об ошибке</param>
public ClusterAnalyzeException(string msg)
: base(msg)
{ }
}
/// <summary>
/// Класс список номеров записей в файле. Служит исключительно как синоним базового класса
/// </summary>
public class ListOfRecords : List<uint>
{ }
delegate void ShowStatus(double status);
}

Пример работы программы:
Код:

Введите количество строк матрицы:20
Введите количество столбцов матрицы:4
Исходная матрица:
-15,769 28,240 30,709 -16,743
23,706 22,215 34,025 44,498
-34,650 -7,589 -39,562 49,038
-45,471 -15,300 7,146 11,542
37,899 -29,163 -44,043 -35,843
-27,469 11,701 -7,248 20,660
44,507 45,424 -30,283 -36,959
6,021 -9,420 10,197 -40,719
38,323 -0,363 39,633 25,625
-3,503 25,883 -7,936 6,627
4,357 21,285 -15,251 27,002
-13,598 -49,260 17,893 4,770
-23,164 -44,754 -24,756 28,277
32,301 8,660 43,355 22,530
-14,512 -39,951 -11,250 28,282
2,499 20,984 -11,523 24,770
32,214 -48,843 -39,285 4,892
-4,933 31,522 -34,955 -37,983
28,773 -0,671 9,801 1,246
-23,047 15,750 -15,742 43,767
Введите желаемую погрешность:15
Кластер 1
-15,769 28,240 30,709 -16,743
6,021 -9,420 10,197 -40,719
Кластер 2
23,706 22,215 34,025 44,498
32,301 8,660 43,355 22,530
Кластер 3
-34,650 -7,589 -39,562 49,038
-23,164 -44,754 -24,756 28,277
Кластер 4
-45,471 -15,300 7,146 11,542
-13,598 -49,260 17,893 4,770
-14,512 -39,951 -11,250 28,282
Кластер 5
37,899 -29,163 -44,043 -35,843
32,214 -48,843 -39,285 4,892
Кластер 6
-27,469 11,701 -7,248 20,660
-3,503 25,883 -7,936 6,627
4,357 21,285 -15,251 27,002
2,499 20,984 -11,523 24,770
-23,047 15,750 -15,742 43,767
Кластер 7
44,507 45,424 -30,283 -36,959
Кластер 8
38,323 -0,363 39,633 25,625
28,773 -0,671 9,801 1,246
Кластер 9
-4,933 31,522 -34,955 -37,983
Среднее отклонение:9,58363489925735
Максимальное отклонение:14,4731417982541


Кому интересно может скачать Windows приложение на эту тему пройдя по ссылке: http://rusfaq.ru/upload/1476
давно
Мастер-Эксперт
5157
1914
17.02.2009, 00:50
общий
Добавил, как просили.
Об авторе:
Детям в интернет нельзя, интернет от них тупеет
Форма ответа