Консультация № 185939
01.05.2012, 19:24
138.87 руб.
04.05.2012, 00:30
0 4 2
Здравствуйте уважаемые эксперты! Нужно на C# запрограммировать алгоритм FOREL (Форель) и исследовать свойства этого алгоритма N=[$966$](r) где r это параметр алгоритма форель.

Вот последовательность алгоритма:

1) Случайно выбираем текущий объект из выборки
2) Помечаем объекты выборки, находящиеся на расстоянии менее, чем R от текущего
3) Вычисляем их центр тяжести, помечаем этот центр как новый текущий объект
4) Повторяем шаги 2-3, пока новый текущий объект не совпадет с прежним
5) Помечаем объекты внутри сферы радиуса R вокруг текущего объекта как кластеризованные, выкидываем их из выборки
6) Повторяем шаги 1-5, пока не будет кластеризована вся выборка

Обсуждение

Неизвестный
04.05.2012, 00:31
общий
Перенесла вопрос сюда, он ко мне по ошибке попал. Обратите внимание, пожалуйста.
Неизвестный
08.05.2012, 08:47
общий
это ответ
Здравствуйте, Константин!
Код:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Application
{
class MainClass
{
// Размер выборки
const int SIZE = 10;
// Радиус поиска
const double R_MIN = 0;
const double R_MAX = (MAX - MIN) / 2.0;
const double R_STEP = 1;
// Пределы для генерации массива случайных чисел
const double MIN = -10;
const double MAX = +10;

public static void Main (string[] args)
{
try {
new MainClass ().Run ();
} catch (Exception ex) {
Console.WriteLine ("Необработанное исключение:\n{0}", ex.Message);
}
}

/// <summary>
/// Запускает цикл тестирования
/// </summary>
void Run ()
{
double[] data = genArray ();

print ("Коллекция данных для тестирования:", data);

Dictionary<double, int> clusterCount = new Dictionary<double, int> ();

for (double r = R_MIN; r <= R_MAX; r += R_STEP) {
clusterCount.Add (r, testFOREL (data, r));
}

double maxClusters = clusterCount.Max (a => { return a.Value; });

Console.WriteLine ("График зависимости к-ва кластеров от R");
foreach (var item in clusterCount) {
Console.Write ("{0,5:0.00}:", item.Key);
string str = new string ('*', (int)(item.Value / maxClusters * (80 - 11)));
Console.WriteLine ("{0}{1,-5}", str, item.Value);
}
}

/// <summary>
/// Тестирует алгоритм FOREL
/// </summary>
/// <param name="array">
/// Коллекция данных
/// </param>
/// <param name="r">
/// Радиус поиска
/// </param>
/// <returns>
/// Количество кластеров
/// </returns>
int testFOREL (double[] array, double r)
{
double[][] clusters = Forel.Solve (array, r, (o1, o2) => { return o1 - o2; }, a =>
{
double s = 0.0;
foreach (double v in a)
s += v;
return s / a.Length;
});
print (string.Format ("Для параметра R={0} получены кластеры:", r), clusters);
Console.WriteLine ("Всего {0} кластеров", clusters.Length);
return clusters.Length;
}

// Генератор случайных чисел
static Random random = new Random ();

/// <summary>
/// Генерирует массив случайных чисел
/// </summary>
/// <returns>
/// Сгенерированный массив
/// </returns>
double[] genArray ()
{
double[] result = new double[SIZE];

for (int i = 0; i < SIZE; ++i) {
result[i] = random.NextDouble () * (MAX - MIN) + MIN;
}
return result;
}

/// <summary>
/// Выводит массив на консоль
/// </summary>
/// <param name="collection">
/// Коллекция данных с интерфейсом IEnumerable
/// </param>
void print<T> (T collection) where T : IEnumerable
{
Console.Write ("(");
foreach (var item in collection) {
IEnumerable tmp = item as IEnumerable;
if (tmp != null) {
print (tmp);
} else {
Console.Write ("{0} ", item);
}
}
Console.Write (")");
}

/// <summary>
/// Выводит массив на консоль
/// </summary>
/// <param name="message">
/// Сообщение перед выводом массива
/// </param>
/// <param name="collection">
/// Коллекция данных с интерфейсом IEnumerable
/// </param>
void print<T> (string message, T collection) where T : IEnumerable
{
Console.WriteLine ("{0}", message);
print (collection);
Console.WriteLine ();
}

}

/// <summary>
/// Делегат для функции, определяющей расстояние между объектами
/// </summary>
public delegate double DistanceFunction<T> (T o1, T o2);
/// <summary>
/// Делегат для функции, определяющей центр масс объектов
/// </summary>
public delegate T CenterOfMassFunction<T> (T[] arr);

/// <summary>
/// Реализует алгоритм FOREL
/// </summary>
public static class Forel
{
private static Random random = new Random ();

/// <summary>
/// Решает задачу кластеризации
/// </summary>
/// <param name="array">
/// Массив данных
/// </param>
/// <param name="r">
/// Радиус
/// </param>
/// <param name="distance">
/// Ф-я расчета дистанции между объектами
/// </param>
/// <param name="centerOfMass">
/// Ф-я расчета центра масс
/// </param>
/// <returns>
/// Массив кластеров
/// </returns>
public static T[][] Solve<T> (T[] array, double r, DistanceFunction<T> distance, CenterOfMassFunction<T> centerOfMass) where T : IEquatable<T>
{
r = Math.Abs (r);
// Результат расчета
List<T[]> result = new List<T[]> ();
// Копируем данные в динамический список
List<T> data = array.ToList ();
// Пока есть не кластеризованные элементы
while (data.Count > 0) {
// Значение элемента выбранного в качестве центра
T center;
T centerNew = data[random.Next (data.Count)];
// Получившийся кластер
T[] cluster = null;
do {
center = centerNew;
// Кластер собирается здесь
cluster = (from item in data
where distance (item, center) <= r
select item).ToArray ();
// Считаем центр тяжести
centerNew = centerOfMass (cluster);
} while (!centerNew.Equals (center));
result.Add (cluster);
// Удаление кластеризованных элементов из выборки
Array.ForEach (cluster, item => { data.Remove (item); });
}
return result.ToArray ();
}

}
}

Пример работы:
Код:
Коллекция данных для тестирования:
(-5,44374301817442 8,90650349618238 -2,73375119675591 -9,10588378045051 9,66046832486078 4,22250842406531 -6,14473116404597 -3,7516542401871 5,14795034897884 7,19218952450537 )
Для параметра R=0 получены кластеры:
((-9,10588378045051 )(-6,14473116404597 )(-5,44374301817442 )(-3,7516542401871 )(-2,73375119675591 )(4,22250842406531 )(5,14795034897884 )(7,19218952450537 )(8,90650349618238 )(9,66046832486078 ))
Всего 10 кластеров
Для параметра R=1 получены кластеры:
((-9,10588378045051 )(-5,44374301817442 -6,14473116404597 )(-2,73375119675591 -3,7516542401871 )(4,22250842406531 5,14795034897884 )(8,90650349618238 7,19218952450537 )(9,66046832486078 ))
Всего 6 кластеров
Для параметра R=2 получены кластеры:
((-5,44374301817442 -9,10588378045051 -6,14473116404597 )(-2,73375119675591 -3,7516542401871 )(4,22250842406531 5,14795034897884 7,19218952450537 )(8,90650349618238 9,66046832486078 ))
Всего 4 кластеров
Для параметра R=3 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 -6,14473116404597 -3,7516542401871 )(8,90650349618238 9,66046832486078 4,22250842406531 5,14795034897884 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=4 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 -6,14473116404597 -3,7516542401871 )(8,90650349618238 9,66046832486078 4,22250842406531 5,14795034897884 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=5 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 -6,14473116404597 -3,7516542401871 )(8,90650349618238 9,66046832486078 4,22250842406531 5,14795034897884 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=6 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 -6,14473116404597 -3,7516542401871 )(8,90650349618238 9,66046832486078 4,22250842406531 5,14795034897884 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=7 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 -6,14473116404597 -3,7516542401871 )(8,90650349618238 9,66046832486078 4,22250842406531 5,14795034897884 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=8 получены кластеры:
((-5,44374301817442 -2,73375119675591 -9,10588378045051 4,22250842406531 -6,14473116404597 -3,7516542401871 5,14795034897884 )(8,90650349618238 9,66046832486078 7,19218952450537 ))
Всего 2 кластеров
Для параметра R=9 получены кластеры:
((-5,44374301817442 8,90650349618238 -2,73375119675591 -9,10588378045051 9,66046832486078 4,22250842406531 -6,14473116404597 -3,7516542401871 5,14795034897884 7,19218952450537 ))
Всего 1 кластеров
Для параметра R=10 получены кластеры:
((-5,44374301817442 8,90650349618238 -2,73375119675591 -9,10588378045051 9,66046832486078 4,22250842406531 -6,14473116404597 -3,7516542401871 5,14795034897884 7,19218952450537 ))
Всего 1 кластеров
График зависимости к-ва кластеров от R
0,00:*********************************************************************10
1,00:*****************************************6
2,00:***************************4
3,00:*************2
4,00:*************2
5,00:*************2
6,00:*************2
7,00:*************2
8,00:*************2
9,00:******1
10,00:******1
Неизвестный
08.05.2012, 23:05
общий
Немного подправил решение.
Неизвестный
09.05.2012, 02:11
общий
это ответ
Здравствуйте, Константин!

Я прикрутил этот алгоритм к своему простенькому симулятору для изучения точек. Можно сохранить или загрузить точки из файла, а установив флажок - добавлять новые точки.

Сам алгоритм находится в файле FOREL.cs

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

Недостатки: фиксированный радиус для кластера и фиксированная форма границы кластера (окружность). При слишком большом радиусе в кластер попадут точки, которые к нему не должны относиться. При слишком маленьком или если поле почти равномерно заполнено - мы получаем не кластеры, а просто кучу кружочков и ошметков от кружочков, что лишает смысла кластеризацию. Кроме того, алгоритм не так легко распараллелить. Архив программы я выложил на сайте: скачать файл WpfFOREL.rar [10.7 кб]

Текст FOREL.cs дополнительно выставляю здесь:
Код:
using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfFOREL
{
class FOREL
{
/// <summary>Разбивает список точек на несколько кластеров, которые тоже хранятся как списки.</summary>
/// <param name="Points">Список точек</param>
/// <param name="Radius">Радиус кластеров</param>
/// <returns>Список кластеров (список списков точек)</returns>
public static List<List<Point>> Recluster(List<Point> Points, double Radius)
{
List<List<Point>> Result = new List<List<Point>>();
Random RND = new Random();
double R2 = Radius * Radius;
List<Point> Unclustered = new List<Point>(Points);

while (Unclustered.Count > 0)
{
Point NewCenter = Unclustered[RND.Next(Unclustered.Count)];
Point Center;
do //Двигаем центр, пока он не устаканится
{
Center = NewCenter;
double x = 0.0, y = 0.0;
int Cnt = 0;
foreach (Point P in Unclustered)//Считаем новый центр масс
if (Dist2(Center, P) < R2)
{
x += P.X;
y += P.Y;
Cnt++;
}
NewCenter = new Point(x / Cnt, y / Cnt);
}
while (Center != NewCenter);

List<Point> Cluster = new List<Point>(); //Переносим точки в новый кластер
for (int i = Unclustered.Count - 1; i >= 0; i--)
if (Dist2(Center, Unclustered[i]) < R2)
{
Cluster.Add(Unclustered[i]);
Unclustered.RemoveAt(i);
}
Result.Add(Cluster);
}

return Result;
}

/// <summary>Квадрат расстояния между точками</summary>
private static double Dist2(Point P1, Point P2)
{
double dx = P1.X - P2.X;
double dy = P1.Y - P2.Y;
return dx * dx + dy * dy;
}
}
}
Форма ответа