From 871a7a82baa6aeef86eeb59c161394f7ed25735e Mon Sep 17 00:00:00 2001 From: Lexey Date: Sat, 9 Apr 2016 01:48:27 +0300 Subject: [PATCH 1/2] Added DisjointSets and DisjointSets collections --- Main/src/CodeJam.Main.csproj | 3 + Main/src/Collections/DisjointSets.cs | 30 +++++++ Main/src/Collections/DisjointSetsBase.cs | 95 ++++++++++++++++++++++ Main/src/Collections/DisjointSetsT.cs | 53 ++++++++++++ Main/tests/CodeJam.Main-Tests.csproj | 1 + Main/tests/Collections/DisjointSetsTest.cs | 65 +++++++++++++++ 6 files changed, 247 insertions(+) create mode 100644 Main/src/Collections/DisjointSets.cs create mode 100644 Main/src/Collections/DisjointSetsBase.cs create mode 100644 Main/src/Collections/DisjointSetsT.cs create mode 100644 Main/tests/Collections/DisjointSetsTest.cs diff --git a/Main/src/CodeJam.Main.csproj b/Main/src/CodeJam.Main.csproj index 739428b5e..73c2005b2 100644 --- a/Main/src/CodeJam.Main.csproj +++ b/Main/src/CodeJam.Main.csproj @@ -74,6 +74,9 @@ True DebugCode.tt + + + diff --git a/Main/src/Collections/DisjointSets.cs b/Main/src/Collections/DisjointSets.cs new file mode 100644 index 000000000..7d4a11f7b --- /dev/null +++ b/Main/src/Collections/DisjointSets.cs @@ -0,0 +1,30 @@ +namespace CodeJam.Collections +{ + /// Реализация классической структуры Disjoint sets + /// + /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// + public sealed class DisjointSets : DisjointSetsBase + { + /// Создает пустой DS + public DisjointSets() { } + + /// Создает DS с заданным количеством элементов + /// Количество элементов + public DisjointSets(int count) + { + Add(count); + } + + /// Добавление заданного количества элементов + /// Количество элементов + public void Add(int count) + { + for (var i = 0; i < count; ++i) + { + Nodes_.Add(new BasicNode { ParentIndex = -1, Rank = 0 }); + } + SetsCount += count; + } + } +} diff --git a/Main/src/Collections/DisjointSetsBase.cs b/Main/src/Collections/DisjointSetsBase.cs new file mode 100644 index 000000000..d2bbc7caf --- /dev/null +++ b/Main/src/Collections/DisjointSetsBase.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; + +namespace CodeJam.Collections +{ + /// Базовый класс для реализации Disjoint sets + /// + /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// + public class DisjointSetsBase where T : BasicNode + { + /// Список всех узлов + protected readonly List Nodes_ = new List(); + + /// Создает пустой DS + protected DisjointSetsBase() { } + + /// Число элементов + public int Count => Nodes_.Count; + + /// Число несвязанных множеств + public int SetsCount { get; protected set; } + + /// Поиск идентификатора множества, которому принадлежит элемент по заданному индексу + /// Индекс элемента + /// Идентификатор множества, которому принадлежит элемент + /// Идентификатор множества - это индекс элемента, представляющего множество + public int IndexToSetId(int index) + { + // сначала ищем индекс корневого элемента дерева, к которому принадлежит наш узел + var rootIndex = index; + for (;;) + { + var parentIndex = Nodes_[rootIndex].ParentIndex; + if (parentIndex == -1) + { + break; + } + rootIndex = parentIndex; + } + + // компрессия пути - идем от нашего элемента вверх к корню, обновляя ParentIndex на rootIndex + while (index != rootIndex) + { + var node = Nodes_[index]; + index = node.ParentIndex; + node.ParentIndex = rootIndex; + } + return rootIndex; + } + + /// Объединение двух множеств в одно + /// Индекс какого-то элемента первого множества + /// Индекс какого-то элемента второго можества + public void Union(int elementOfSet1Index, int elementOfSet2Index) + { + elementOfSet1Index = IndexToSetId(elementOfSet1Index); + elementOfSet2Index = IndexToSetId(elementOfSet2Index); + if (elementOfSet1Index == elementOfSet2Index) + { + return; // уже одно множество + } + + var set1Root = Nodes_[elementOfSet1Index]; + var set2Root = Nodes_[elementOfSet2Index]; + var rankDifference = set1Root.Rank - set2Root.Rank; + // Цепляем дерево с меньшим рангом к корню дерева с большим. В этом случае ранг получившегося дерева равен большему, кроме случая, когда ранги равны (тогда будет +1) + if (rankDifference > 0) // у 1-го ранг больше + { + set2Root.ParentIndex = elementOfSet1Index; + } + else if (rankDifference < 0) // у 2-го больше + { + set1Root.ParentIndex = elementOfSet2Index; + } + else // ранги равны. пофигу что к чему цеплять, но нужно увеличить ранг того, к чему прицепили + { + set2Root.ParentIndex = elementOfSet1Index; + ++set1Root.Rank; + } + + // поскольку слили 2 в одно, уменьшаем число сетов + --SetsCount; + } + } + + /// Базовый класс узла дерева + public class BasicNode + { + /// Индекс родителя (после компрессии пути указывает на корень) + public int ParentIndex; + + /// Примерный уровень ноды в дереве (с несжатыми путями), считая от корня + public int Rank; + } +} diff --git a/Main/src/Collections/DisjointSetsT.cs b/Main/src/Collections/DisjointSetsT.cs new file mode 100644 index 000000000..2be4f0493 --- /dev/null +++ b/Main/src/Collections/DisjointSetsT.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CodeJam.Collections +{ + /// + /// Реализация классической структуры Disjoint sets + /// Generic - версия, позволяющая хранить данные внутри + /// + /// + /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// + public sealed class DisjointSets : DisjointSetsBase.Node> + { + /// Создает пустой DS + public DisjointSets() { } + + /// Создает DS со значениями из перечисления + /// Перечисление значений для добавления + public DisjointSets(IEnumerable values) + { + Add(values); + } + + /// Получение значения элемента по индексу + /// Индекс элемента + public T this[int index] => Nodes_[index].Value; + + /// Добавление перечисления элементов + /// Элементы + public void Add(IEnumerable values) + { + var initialNodesCount = Nodes_.Count; + Nodes_.AddRange(values.Select(_ => new Node { Value = _, ParentIndex = -1, Rank = 0 })); + SetsCount += Nodes_.Count - initialNodesCount; + } + + /// Добавление одного элемента + /// Элемент + public void Add(T value) + { + Nodes_.Add(new Node { Value = value, ParentIndex = -1, Rank = 0 }); + ++SetsCount; + } + + /// Узел дерева + public class Node : BasicNode + { + /// Собственно полезные данные + public T Value; + } + } +} diff --git a/Main/tests/CodeJam.Main-Tests.csproj b/Main/tests/CodeJam.Main-Tests.csproj index 9e1a99331..988ecce31 100644 --- a/Main/tests/CodeJam.Main-Tests.csproj +++ b/Main/tests/CodeJam.Main-Tests.csproj @@ -59,6 +59,7 @@ + diff --git a/Main/tests/Collections/DisjointSetsTest.cs b/Main/tests/Collections/DisjointSetsTest.cs new file mode 100644 index 000000000..4c3dd846c --- /dev/null +++ b/Main/tests/Collections/DisjointSetsTest.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using CodeJam.Collections; + +using NUnit.Framework; + +namespace Tests +{ + [TestFixture] + public class DisjointSetsTest + { + private readonly Random random_ = new Random(); + private const int ElementsNumber = 10000; + private readonly List seq_ = Enumerable.Range(0, ElementsNumber).ToList(); + + [Test] + public void Test01NonGeneric() + { + for (var i = 1; i <= ElementsNumber; i += 1 + i / (10 + random_.Next(0, 10))) + { + Console.WriteLine("i = {0}", i); + var djs = new DisjointSets(ElementsNumber); + foreach (var el in RandomShuffle(seq_)) + { + djs.Union(el, el % i); + } + VerifySets(djs, i); + } + } + + [Test] + public void Test02Generic() + { + for (var i = 1; i <= ElementsNumber; i += 1 + i / (10 + random_.Next(0, 10))) + { + Console.WriteLine("i = {0}", i); + var rs = RandomShuffle(seq_).ToList(); + var djs = new DisjointSets(rs); + foreach (var el in rs) + { + djs.Union(el, el % i); + } + VerifySets(djs, i); + for (var j = 0; j < ElementsNumber; ++j) + { + Assert.That(djs[j], Is.EqualTo(rs[j])); + } + } + } + + private static void VerifySets(DisjointSetsBase djs, int mod) where T : BasicNode + { + Assert.That(djs.Count, Is.EqualTo(ElementsNumber)); + Assert.That(djs.SetsCount, Is.EqualTo(mod)); + for (var i = 0; i < ElementsNumber; ++i) + { + Assert.That(djs.IndexToSetId(i), Is.EqualTo(djs.IndexToSetId(i % mod)), "i = {0}, mod = {1}", i, mod); + } + } + + private IEnumerable RandomShuffle(IEnumerable en) => en.OrderBy(x => random_.Next()); + } +} From fa3ce5439bfd22f739ec30139bd4521fe59247c6 Mon Sep 17 00:00:00 2001 From: Lexey Date: Sat, 9 Apr 2016 23:03:05 +0300 Subject: [PATCH 2/2] Translated comments for DisjointSets --- Main/src/Collections/DisjointSets.cs | 14 +++--- Main/src/Collections/DisjointSetsBase.cs | 57 ++++++++++++++---------- Main/src/Collections/DisjointSetsT.cs | 27 ++++++----- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/Main/src/Collections/DisjointSets.cs b/Main/src/Collections/DisjointSets.cs index 7d4a11f7b..1bf5a12dc 100644 --- a/Main/src/Collections/DisjointSets.cs +++ b/Main/src/Collections/DisjointSets.cs @@ -1,23 +1,23 @@ namespace CodeJam.Collections { - /// Реализация классической структуры Disjoint sets + /// Disjoint sets without payload /// - /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure /// public sealed class DisjointSets : DisjointSetsBase { - /// Создает пустой DS + /// Creates an empty Disjoint sets public DisjointSets() { } - /// Создает DS с заданным количеством элементов - /// Количество элементов + /// Creates a Disjoint sets with the given number of elements + /// The initial number of elements public DisjointSets(int count) { Add(count); } - /// Добавление заданного количества элементов - /// Количество элементов + /// Appends the given number of new elements + /// The number of elements to add public void Add(int count) { for (var i = 0; i < count; ++i) diff --git a/Main/src/Collections/DisjointSetsBase.cs b/Main/src/Collections/DisjointSetsBase.cs index d2bbc7caf..6a1bef7a2 100644 --- a/Main/src/Collections/DisjointSetsBase.cs +++ b/Main/src/Collections/DisjointSetsBase.cs @@ -2,31 +2,35 @@ namespace CodeJam.Collections { - /// Базовый класс для реализации Disjoint sets + /// Disjoint sets implementation base /// - /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure /// public class DisjointSetsBase where T : BasicNode { - /// Список всех узлов + /// All nodes protected readonly List Nodes_ = new List(); - /// Создает пустой DS + /// Creates an empty base protected DisjointSetsBase() { } - /// Число элементов + /// The number of nodes public int Count => Nodes_.Count; - /// Число несвязанных множеств + /// The number of disjoint sets public int SetsCount { get; protected set; } - /// Поиск идентификатора множества, которому принадлежит элемент по заданному индексу - /// Индекс элемента - /// Идентификатор множества, которому принадлежит элемент - /// Идентификатор множества - это индекс элемента, представляющего множество + /// Finds a set identifier for the element + /// The element index + /// The identifier of the containing set + /// + /// The set identifier is the index of a single element representing the set. + /// The Union operation may lead to a choice of a different representative for a set. + /// In this case IndexToSetId(oldSetId) may be called to get the new set id. + /// public int IndexToSetId(int index) { - // сначала ищем индекс корневого элемента дерева, к которому принадлежит наш узел + // First, find a root element of a tree containing the passed element var rootIndex = index; for (;;) { @@ -38,7 +42,8 @@ public int IndexToSetId(int index) rootIndex = parentIndex; } - // компрессия пути - идем от нашего элемента вверх к корню, обновляя ParentIndex на rootIndex + // Then, do the path compression: + // walk from the passed element upto the root replacing the the ParentIndex with the root index while (index != rootIndex) { var node = Nodes_[index]; @@ -48,48 +53,52 @@ public int IndexToSetId(int index) return rootIndex; } - /// Объединение двух множеств в одно - /// Индекс какого-то элемента первого множества - /// Индекс какого-то элемента второго можества + /// Combines to distjoint sets into a single set + /// Index of an element of the first set + /// Index of an element of the second set public void Union(int elementOfSet1Index, int elementOfSet2Index) { elementOfSet1Index = IndexToSetId(elementOfSet1Index); elementOfSet2Index = IndexToSetId(elementOfSet2Index); if (elementOfSet1Index == elementOfSet2Index) { - return; // уже одно множество + return; // Already the single set } var set1Root = Nodes_[elementOfSet1Index]; var set2Root = Nodes_[elementOfSet2Index]; var rankDifference = set1Root.Rank - set2Root.Rank; - // Цепляем дерево с меньшим рангом к корню дерева с большим. В этом случае ранг получившегося дерева равен большему, кроме случая, когда ранги равны (тогда будет +1) - if (rankDifference > 0) // у 1-го ранг больше + // Attach the tree with a smaller rank to the tree with a higher rank. + // The resulting tree rank is equal to the higher rank + // except the case when initial ranks are equal. + // In the latter case the new rank will be increased by 1 + if (rankDifference > 0) // 1st has higher rank { set2Root.ParentIndex = elementOfSet1Index; } - else if (rankDifference < 0) // у 2-го больше + else if (rankDifference < 0) // 2nd has the higher rank { set1Root.ParentIndex = elementOfSet2Index; } - else // ранги равны. пофигу что к чему цеплять, но нужно увеличить ранг того, к чему прицепили + else // ranks are equal and the new root choice is arbitrary { set2Root.ParentIndex = elementOfSet1Index; ++set1Root.Rank; } - // поскольку слили 2 в одно, уменьшаем число сетов + // we have joined 2 sets, so we have to decrease the count --SetsCount; } } - /// Базовый класс узла дерева + /// Node base class public class BasicNode { - /// Индекс родителя (после компрессии пути указывает на корень) + /// Parent node index + /// Points to the root after a path compression public int ParentIndex; - /// Примерный уровень ноды в дереве (с несжатыми путями), считая от корня + /// Estimated height of the tree (i.e. maximum length of the path from the root to a node. Path compression is not taken into account) public int Rank; } } diff --git a/Main/src/Collections/DisjointSetsT.cs b/Main/src/Collections/DisjointSetsT.cs index 2be4f0493..3b46ad3ba 100644 --- a/Main/src/Collections/DisjointSetsT.cs +++ b/Main/src/Collections/DisjointSetsT.cs @@ -4,30 +4,29 @@ namespace CodeJam.Collections { /// - /// Реализация классической структуры Disjoint sets - /// Generic - версия, позволяющая хранить данные внутри + /// Generic implementation of the Disjoint sets /// /// - /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure + /// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure /// public sealed class DisjointSets : DisjointSetsBase.Node> { - /// Создает пустой DS + /// Creates an empty Disjoint sets public DisjointSets() { } - /// Создает DS со значениями из перечисления - /// Перечисление значений для добавления + /// Creates a Disjoint sets with the passed values + /// The values to store public DisjointSets(IEnumerable values) { Add(values); } - /// Получение значения элемента по индексу - /// Индекс элемента + /// Gets an element by its index + /// Elmement's index public T this[int index] => Nodes_[index].Value; - /// Добавление перечисления элементов - /// Элементы + /// Appends a list of values + /// The values to append public void Add(IEnumerable values) { var initialNodesCount = Nodes_.Count; @@ -35,18 +34,18 @@ public void Add(IEnumerable values) SetsCount += Nodes_.Count - initialNodesCount; } - /// Добавление одного элемента - /// Элемент + /// Appends a single element + /// The value to append public void Add(T value) { Nodes_.Add(new Node { Value = value, ParentIndex = -1, Rank = 0 }); ++SetsCount; } - /// Узел дерева + /// A sets node public class Node : BasicNode { - /// Собственно полезные данные + /// The node data public T Value; } }