Лидеры рейтинга

ID: 401284

Михаил Александров

Советник

377

Россия, Санкт-Петербург


ID: 259041

Алексеев Владимир Николаевич

Мастер-Эксперт

358

Россия, пос. Теплоозёрск, ЕАО


ID: 401888

puporev

Профессор

215

Россия, Пермский край


ID: 405338

vovaromanov.jr

1-й класс

126


ID: 400669

epimkin

Профессионал

111


ID: 242862

Hunter7007

Мастер-Эксперт

29

Россия, Омск


ID: 137394

Megaloman

Мастер-Эксперт

25

Беларусь, Гомель


8.10.2

13.10.2021

JS: 2.10.2
CSS: 4.6.0
jQuery: 3.6.0
DataForLocalStorage: 2021-10-20 21:46:11-standard


Создание программ на программной платформе .NET Framework и языках С# и Java.

Администратор раздела: Коцюрбенко Алексей Владимирович (Старший модератор)

Консультация онлайн # 160240

Раздел: .NET Framework / C# / Java
Автор вопроса: Иванка
Дата: 11.02.2009, 21:53 Консультация закрыта
Поступило ответов: 1

помогите реализовать такой алгоритм кластеризации k-середных
для матрицы на С# или С++Builder

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

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

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

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

Желаю удачи! smile

Добавлена информация из мини-форума по просьбе эксперта:
Исправил некоторые ошибки. Пожалуй, это можно считать окончательным вариантом.
Кроме того. Если к примеру начальные центроиды расположены близко друг к другу или совсем равны(например все строки матрицы одинаковые), а Вы пытаетесь разбить на много кластеров то реально используется только один центроид, но расчет в первой программе шел все равно по всем центроидам. Это не влияло на результат, но было совершенно излишне. Здесь ситуация исправлена.
Проверялась на .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://rapidshar...615/160240-2.rar

Последнее редактирование 17.02.2009, 00:49 deepTeNk (Мастер-Эксперт)


Micren

Посетитель
16.02.2009, 15:26
Мини-форум консультации # 160240
Micren

1

= общий =    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

q_id

deepTeNk

Мастер-Эксперт

ID: 5157

2

= общий =    17.02.2009, 00:50

Добавил, как просили.

=====
Детям в интернет нельзя, интернет от них тупеет

Возможность оставлять сообщения в мини-форумах консультаций доступна только после входа в систему.
Воспользуйтесь кнопкой входа вверху страницы, если Вы зарегистрированы или пройдите простую процедуру регистрации на Портале.

Лучшие эксперты раздела

Зенченко Константин Николаевич

Старший модератор

Рейтинг: 164

Коцюрбенко Алексей Владимирович

Старший модератор

Рейтинг: 47

solowey

Академик

Рейтинг: 3

CradleA

Мастер-Эксперт

Рейтинг: 1

Лысков Игорь Витальевич

Мастер-Эксперт

Рейтинг: 0

Асмик Гаряка

Советник

Рейтинг: 0