internal static ClusteringResult Multithreaded(IMathSet mathSet, ClusteringOptions options) { // выбор метрики для расчета: Func <IMathPoint, IMathPoint, Double> func = DataMiningMath.UsedMetrics(options.Distance); // построение всех medoids для входного набора данных: Double[][] allMedoids = new Double[mathSet.RowsCount][]; Parallel.For(0, mathSet.RowsCount, (Int32 row) => { allMedoids[row] = new Double[mathSet.RowsCount]; for (int col = 0; col < mathSet.RowsCount; col++) { if (row != col) { allMedoids[row][col] = func(mathSet[row], mathSet[col]); } } }); // выбор центров кластеризации (случайная инициализация): Random random = new Random(); Dictionary <String, Int32> cIndexes = new Dictionary <String, Int32>(options.ClusterCount); Dictionary <String, List <Int32> > cDistributions = new Dictionary <String, List <Int32> >(); for (Int32 i = 0; i < options.ClusterCount; i++) { String name = "Cluster " + (i + 1); cIndexes.Add(name, random.Next(mathSet.RowsCount)); cDistributions.Add(name, new List <Int32>()); } // первый этап: предварительное связывание точек с центрами: Double newCost = 0; Parallel.For(0, mathSet.RowsCount, (Int32 row) => { String name = ""; Double minDistance = Double.MaxValue; // обход центров кластеризации: foreach (KeyValuePair <String, Int32> cluster in cIndexes) { if (minDistance > allMedoids[row][cluster.Value]) { name = cluster.Key; minDistance = allMedoids[row][cluster.Value]; } } lock (cDistributions) { newCost += minDistance; // подсчет текущей стоймости cDistributions[name].Add(row); // добавить точку в распределение } }); // основной цикл обработки: Int32 iteration = 0; Double prevCost; while (iteration++ <= options.MaxIterations) { // выбрать новые центры из ранее полученного распределения: prevCost = newCost; newCost = 0; Dictionary <String, Int32> newIndexes = new Dictionary <string, int>(options.ClusterCount); Dictionary <String, List <Int32> > newDistribution = new Dictionary <String, List <Int32> >(); foreach (KeyValuePair <string, int> cIndex in cIndexes) { newIndexes.Add(cIndex.Key, cDistributions[cIndex.Key][random.Next(cDistributions[cIndex.Key].Count)]); newDistribution.Add(cIndex.Key, new List <Int32>()); } // повторное связывание: Parallel.For(0, mathSet.RowsCount, (Int32 row) => { String name = ""; Double minDistance = Double.MaxValue; // обход центров кластеризации: foreach (KeyValuePair <String, Int32> cluster in newIndexes) { if (minDistance > allMedoids[row][cluster.Value]) { name = cluster.Key; minDistance = allMedoids[row][cluster.Value]; } } lock (newDistribution) { newCost += minDistance; // подсчет текущей стоймости newDistribution[name].Add(row); // добавить точку в распределение } }); // проверка на выход из цикла: Double dCost = newCost - prevCost; if (dCost > 0) { break; } cDistributions = newDistribution; cIndexes = newIndexes; } // маркировка данных по кластера: Parallel.ForEach(cDistributions, (KeyValuePair <string, List <int> > distribution) => { foreach (Int32 index in distribution.Value) { mathSet[index].Cluster = distribution.Key; } }); // получение центро клстеризации: List <IMathPoint> centers = new List <IMathPoint>(); foreach (KeyValuePair <string, int> index in cIndexes) { centers.Add(mathSet[index.Value]); } return(new ClusteringResult(centers, mathSet, iteration)); }
internal static ClusteringResult Multithreaded(IMathSet mathSet, ClusteringOptions options) { // выбор метрики для расчета: Func <IMathPoint, IMathPoint, Double> func = DataMiningMath.UsedMetrics(options.Distance); // инициализация центров кластеров для текущего шага расчета: List <IMathPoint> currentCenters = InitCenters(mathSet, options); // инициализация центров кластеров для предыдущего шага расчета: List <IMathPoint> prevCenters = new List <IMathPoint>(options.ClusterCount); // основной цикл обработки: Int32 iteration = 0; while (iteration++ <= options.MaxIterations) { // вычисление самого удачного центра для кластеров на рассматриваемой иттерации: Parallel.For(0, mathSet.RowsCount, (Int32 r) => { // поиск ближайшего кластера к точке: Int32 closestIdx = -1; Double minDistance = Double.MaxValue; for (Int32 i = 0; i < currentCenters.Count; i++) { Double distance = func(mathSet[r], currentCenters[i]); if (minDistance > distance) { closestIdx = i; minDistance = distance; } } // обновляем центр выбраного кластера: lock (currentCenters) { currentCenters[closestIdx] = DataMiningMath.MeanPoint(mathSet[r], currentCenters[closestIdx]); } }); // кластеризация данных в соответствии с новыми центрами: Parallel.For(0, mathSet.RowsCount, (Int32 r) => { // поиск ближайшего кластера к точке: IMathPoint closestCluster = null; Double minDistance = Double.MaxValue; for (Int32 i = 0; i < currentCenters.Count; i++) { Double distance = func(mathSet[r], currentCenters[i]); if (minDistance > distance) { closestCluster = currentCenters[i]; minDistance = distance; } } // обновить кластер у точки: mathSet[r].Cluster = closestCluster.Cluster; }); // проверка на выход из цикла: if (prevCenters.Count != 0) { Boolean flag = true; // флаг прерывания расчета // обход центров кластеризации: for (Int32 i = 0; i < currentCenters.Count; i++) { // обход координат: for (Int32 c = 0; c < currentCenters[i].CoordinatesCount; c++) { if (currentCenters[i][c] != prevCenters[i][c]) { flag = false; break; } } } if (flag) { break; } } prevCenters = new List <IMathPoint>(currentCenters); } return(new ClusteringResult(currentCenters, mathSet, iteration)); }