/// <summary> /// Получить вершину на удаление из клики /// </summary> /// <param name="graph">Граф</param> /// <param name="clique">Клика</param> /// <param name="oneMissing">Список возможных вершин на удаление</param> /// <returns></returns> static int GetNodeToDrop(MyGraph graph, List <int> clique, List <int> oneMissing) { //Если в клике всего одна вершина - ее и вернем if (clique.Count == 1) { return(clique[0]); } int maxCount = 0; for (int i = 0; i < clique.Count; ++i) { int currCliqueNode = clique[i]; int countNotAdjacent = 0; //Подсчитаем количество не связанных между собой вершин for (int j = 0; j < oneMissing.Count; ++j) { int currOneMissingNode = oneMissing[j]; if (graph.AreAdjacent(currCliqueNode, currOneMissingNode) == false) { ++countNotAdjacent; } } if (countNotAdjacent > maxCount) { maxCount = countNotAdjacent; } } List <int> candidates = new List <int>(); for (int i = 0; i < clique.Count; ++i) { int currCliqueNode = clique[i]; int countNotAdjacent = 0; for (int j = 0; j < oneMissing.Count; ++j) { int currOneMissingNode = oneMissing[j]; if (graph.AreAdjacent(currCliqueNode, currOneMissingNode) == false) { ++countNotAdjacent; } } //Если количество пропусков ребер у данной вершины равно максимуму по списку вершин на удаление - добавим эту вершину в список кандидатов на удаление if (countNotAdjacent == maxCount) { candidates.Add(currCliqueNode); } } //Вернем случайную вершину из списка кандидатов на удаление return(candidates[random.Next(0, candidates.Count)]); } // GetNodeToDrop
/// <summary> /// Получить вершину на добавление к клике /// </summary> /// <param name="graph"></param> /// <param name="possibleAdd">Список возможных вершин на добавление</param> /// <returns></returns> static int GetNodeToAdd(MyGraph graph, List <int> possibleAdd, List <int> clique) { //Если возможная вершина всего одна - ее и вернем if (possibleAdd.Count == 1) { return(possibleAdd[0]); } int maxDegree = 0; List <int> candidates = new List <int>(); possibleAdd.ForEach(c => { int degreeOfCurrentNode = 0; //Вычислим, со сколькими вершинами из списка возможных связана текущая выбранная вершина possibleAdd.ForEach(v => { if (graph.AreAdjacent(c, v) == true) { ++degreeOfCurrentNode; } }); //Таким образом получим максимальное значение связанности для вершин из списка возможных if (degreeOfCurrentNode > maxDegree) { maxDegree = degreeOfCurrentNode; } }); //Пройдем по списку возможных вершин и добавим в список кандидатов те из них, для которых степень связанности равна максимальной связанности в данном списке possibleAdd.ForEach(c => { int degreeOfCurrentNode = 0; possibleAdd.ForEach(v => { if (graph.AreAdjacent(c, v) == true) { ++degreeOfCurrentNode; } }); if ((degreeOfCurrentNode == maxDegree) && !(clique.Exists(x => x == c))) { candidates.Add(c); } }); if (candidates.Count().Equals(0)) { return(-1); } //Вернем случайную вершину из кандидатов на добавление return(candidates[random.Next(0, candidates.Count)]); }
static List <int> MakeOneMissing(MyGraph graph, List <int> clique) { int count; List <int> result = new List <int>(); for (int i = 0; i < graph.NumberNodes; ++i) { count = 0; //Если число соседей данной вершины меньше размера клики - перейдем к рассмотрению следующей вершины if (graph.NumberNeighbors(i) < clique.Count - 1) { continue; } //Если данная вершина уже есть в клике - перейдем к следующей вершине if (clique.BinarySearch(i) >= 0) { continue; } //Подсчитаем количество связей данной вершины с вершинами клики for (int j = 0; j < clique.Count; ++j) { if (graph.AreAdjacent(i, clique[j])) { ++count; } } if (count == clique.Count - 1) { result.Add(i); } } return(result); }
static void Main(string[] args) { try { graphFile = args[0]; int timeLimit = Convert.ToInt32(args[1]); Timer myTimer = new Timer(); myTimer.Start(); myTimer.Interval = timeLimit * 1000; startTime = DateTime.Now; myTimer.Elapsed += MyTimer_Elapsed; MyGraph graph = new MyGraph(graphFile, "DIMACS"); int maxTime = 100; int targetCliqueSize = graph.NumberNodes; //Эвристически найдем хотя бы что-то похожее на максимальную клику (в пределах 5% ошибки - чтобы вернуть, если не успеет обсчитаться основной обход) maxClique = FindMaxClique(graph, maxTime, targetCliqueSize); int ub = maxClique.Count; Bound = ub; List <List <int> > clique = new List <List <int> >(); //Сортируем вершины по числу соседей, будем вызывать алгоритм для тех вершин, у которых количество соседей наибольшее Dictionary <int, int> nodeAndNeighbors = new Dictionary <int, int>(); for (int i = 0; i < graph.NumberNodes; ++i) { int numberNeighbors = graph.NumberNeighbors(i); nodeAndNeighbors.Add(i, numberNeighbors); } //Сортируем вершины по уменьшению количества соседей nodeAndNeighbors = nodeAndNeighbors.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value); List <int> colors = new List <int>() { 1 }; //Раскраска графа int top = (from v in nodeAndNeighbors.Keys.ToList() select v).ToList <int>()[0]; Dictionary <int, int> colorizedGraph = new Dictionary <int, int>(); //Раскрасим граф colorizedGraph = colorize(nodeAndNeighbors.Keys.ToList <int>(), graph); int cntr = 0; //Зададим базовую модель Cplex cplex = new Cplex(); IRange[][] rng = new IRange[1][]; INumVar[][] var = new INumVar[1][]; rng[0] = new IRange[graph.NumberNodes * graph.NumberNodes]; // add the objective function double[] objvals = new double[graph.NumberNodes]; string[] varname = new string[graph.NumberNodes]; for (int i = 0; i < graph.NumberNodes; i++) { objvals[i] = 1.0; varname[i] = "x" + (i + 1); } INumVar[] x = cplex.NumVarArray(graph.NumberNodes, 0.0, 1.0, varname); var[0] = x; //Ограничение, что х лежит от нуля до единицы задали при инициализации cplex.AddMaximize(cplex.ScalProd(x, objvals)); //Получим номер максимального цвета = это количество цветов, в которые окрашен граф //Будем иметь в виду, что количество цветов - это верхняя оценка на размер клики, а найденная эвристически клика на первом этапе - нижняя оценка. int colorCount = colorizedGraph.Values.Max(); List <int> colorizedNodes = new List <int>(); int pointer = 1; //Добавим ограничение, что вершины, входящие в один цветовой класс, не связаны между собой for (int i = 1; i <= colorCount; ++i) { colorizedNodes = (from t in colorizedGraph where t.Value == i select t.Key).ToList <int>(); if (colorizedNodes.Count() != 1) { INumExpr[] constraint = new INumExpr[colorizedNodes.Count()]; int counter = 0; colorizedNodes.ForEach(node => { constraint[counter] = cplex.Prod(1.0, x[node]); counter++; }); rng[0][pointer] = cplex.AddLe(cplex.Sum(constraint), 1.0, "c" + (pointer)); pointer++; } } for (int i = 0; i < graph.NumberNodes; i++) { for (int j = i + 1; j < graph.NumberNodes; j++) { if (!graph.AreAdjacent(i, j)) { rng[0][pointer] = cplex.AddLe(cplex.Sum(cplex.Prod(1.0, x[i]), cplex.Prod(1.0, x[j])), 1.0, "c" + (pointer)); pointer++; } } } //------------------------------------------------------------------------ //-----Пробуем решать задачу ровно до тех пор, пока не получим клику------ //-----Помним про ограничения на размер клики----------------------------- int countOfConstraint = colorCount; globalClick = maxClique; Branching(cplex, x); cplex.End(); ////Максимальная клика, которую можно найти для вершины - это количество различных цветов, в которые окрашены все ее соседи плюс она сама //foreach (KeyValuePair<int,int> pair in nodeAndNeighbors) //{ // List<int> neighbors = graph.GetNeighbors(pair.Key); // neighbors.Add(pair.Key); // var cols = (from n in colorizedGraph where neighbors.Exists(t => t == n.Key) select n.Value).Distinct().ToList<int>(); // if (cols.Count() >= Bound && cols.Count() >= globalClick.Count()) // { // clique.Add(new List<int>()); // List<int> cur = new List<int>(); // RecursiveSearch(pair.Key, ref cur, ref neighbors, graph); // clique[cntr] = cur; // cntr++; // } //} TimeSpan time = (DateTime.Now - startTime); Console.WriteLine("Time to find " + time); Console.WriteLine(globalClick.Count()); Console.WriteLine(String.Join(" ", globalClick)); WriteResults(time, globalClick, false); } catch (System.Exception ex) { Console.WriteLine("Fatal: " + ex.Message); Console.WriteLine(ex.StackTrace); Console.ReadKey(); } }