disjoint_subsets: add get_number_of_sets and get_size_of_set functions (#880)

* disjoint_subsets: make clear and sizde functions noexcept

* disjoint_subsets: add get_number_of_sets function, documentations and tests

* disjoint_subsets: add get_size_of_set function, documentation and tests

* Split disjoint_subsets in a hierarchy.

Modify disjoint_subsets to make it a valid base class.
Add new class disjoint_subsets_sized, with information about number of
subsets, and size of each subset.
Add necessary unit test for the new class.

* Replace tabs by spaces.

* Remove virtuals from disjoint_subsets, and modify
disjoint_subsets_sized.

Now disjoint_subsets_sized is implemented in terms of disjoint_subsets,
instead of inherit from it.
This commit is contained in:
Facundo Galán 2017-12-01 09:08:37 -03:00 committed by Davis E. King
parent 15c04ab224
commit f3ec2abb1b
8 changed files with 427 additions and 33 deletions

View File

@ -17,7 +17,7 @@ namespace dlib
public:
void clear (
)
) noexcept
{
items.clear();
}
@ -35,21 +35,21 @@ namespace dlib
}
unsigned long size (
) const
) const noexcept
{
return items.size();
}
unsigned long find_set (
unsigned long item
unsigned long item
) const
{
// make sure requires clause is not broken
DLIB_ASSERT(item < size(),
DLIB_ASSERT(item < size(),
"\t unsigned long disjoint_subsets::find_set()"
<< "\n\t item must be less than size()"
<< "\n\t item: " << item
<< "\n\t size(): " << size()
<< "\n\t item: " << item
<< "\n\t size(): " << size()
<< "\n\t this: " << this
);
@ -88,16 +88,16 @@ namespace dlib
// make sure requires clause is not broken
DLIB_ASSERT(a != b &&
a < size() &&
b < size() &&
b < size() &&
find_set(a) == a &&
find_set(b) == b,
"\t unsigned long disjoint_subsets::merge_sets(a,b)"
<< "\n\t invalid arguments were given to this function"
<< "\n\t a: " << a
<< "\n\t b: " << b
<< "\n\t size(): " << size()
<< "\n\t find_set(a): " << find_set(a)
<< "\n\t find_set(b): " << find_set(b)
<< "\n\t a: " << a
<< "\n\t b: " << b
<< "\n\t size(): " << size()
<< "\n\t find_set(a): " << find_set(a)
<< "\n\t find_set(b): " << find_set(b)
<< "\n\t this: " << this
);
@ -139,4 +139,3 @@ namespace dlib
}
#endif // DLIB_DISJOINT_SUBsETS_Hh_

View File

@ -20,13 +20,13 @@ namespace dlib
WHAT THIS OBJECT REPRESENTS
This object represents a set of integers which is partitioned into
a number of disjoint subsets. It supports the two fundamental operations
of finding which subset a particular integer belongs to as well as
of finding which subset a particular integer belongs to as well as
merging subsets.
!*/
public:
void clear (
);
) noexcept;
/*!
ensures
- #size() == 0
@ -45,28 +45,28 @@ namespace dlib
!*/
unsigned long size (
) const;
) const noexcept;
/*!
ensures
- returns the total number of integer elements represented
by this object.
by this object.
!*/
unsigned long find_set (
unsigned long item
unsigned long item
) const;
/*!
requires
- item < size()
ensures
- Each disjoint subset can be represented by any of its elements (since
the sets are all disjoint). In particular, for each subset we define
a special "representative element" which is used to represent it.
Therefore, this function returns the representative element for the
- Each disjoint subset can be represented by any of its elements (since
the sets are all disjoint). In particular, for each subset we define
a special "representative element" which is used to represent it.
Therefore, this function returns the representative element for the
set which contains item.
- find_set(find_set(item)) == find_set(item)
- Note that if A and B are both elements of the same subset then we always
have find_set(A) == find_set(B).
have find_set(A) == find_set(B).
!*/
unsigned long merge_sets (
@ -87,7 +87,6 @@ namespace dlib
(i.e. merges the set's containing a and b)
- returns #find_set(a)
!*/
};
// ----------------------------------------------------------------------------------------
@ -95,5 +94,3 @@ namespace dlib
}
#endif // DLIB_DISJOINT_SUBsETS_ABSTRACT_Hh_

View File

@ -0,0 +1,130 @@
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_DISJOINT_SUBsETS_SIZED_Hh_
#define DLIB_DISJOINT_SUBsETS_SIZED_Hh_
#include "disjoint_subsets_sized_abstract.h"
#include "disjoint_subsets.h"
#include <vector>
#include "../algs.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class disjoint_subsets_sized
{
public:
void clear (
) noexcept
{
disjoint_subsets_.clear();
sets_size.clear();
number_of_sets = 0;
}
void set_size (
unsigned long new_size
)
{
disjoint_subsets_.set_size(new_size);
sets_size.assign(new_size, 1);
number_of_sets = new_size;
}
unsigned long size (
) const noexcept
{
return disjoint_subsets_.size();
}
unsigned long find_set (
unsigned long item
) const
{
// make sure requires clause is not broken
DLIB_ASSERT(item < size(),
"\t unsigned long disjoint_subsets::find_set()"
<< "\n\t item must be less than size()"
<< "\n\t item: " << item
<< "\n\t size(): " << size()
<< "\n\t this: " << this
);
return disjoint_subsets_.find_set(item);
}
unsigned long merge_sets (
unsigned long a,
unsigned long b
)
{
// make sure requires clause is not broken
DLIB_ASSERT(a != b &&
a < size() &&
b < size() &&
find_set(a) == a &&
find_set(b) == b,
"\t unsigned long disjoint_subsets::merge_sets(a,b)"
<< "\n\t invalid arguments were given to this function"
<< "\n\t a: " << a
<< "\n\t b: " << b
<< "\n\t size(): " << size()
<< "\n\t find_set(a): " << find_set(a)
<< "\n\t find_set(b): " << find_set(b)
<< "\n\t this: " << this
);
disjoint_subsets_.merge_sets(a, b);
if (find_set(a) == a) sets_size[a] += sets_size[b];
else sets_size[b] += sets_size[a];
--number_of_sets;
return find_set(a);
}
unsigned long get_number_of_sets (
) const noexcept
{
return number_of_sets;
}
unsigned long get_size_of_set(
unsigned long item
) const
{
// make sure requires clause is not broken
DLIB_ASSERT(item < size() &&
find_set(item) == item,
"\t unsigned long disjoint_subsets::get_size_of_set()"
<< "\n\t invalid arguments were given to this function"
<< "\n\t item: " << item
<< "\n\t size(): " << size()
<< "\n\t find_set(item): " << find_set(item)
<< "\n\t this: " << this
);
return sets_size[item];
}
private:
/*
See the book Introduction to Algorithms by Cormen, Leiserson, Rivest and Stein
for a discussion of how this algorithm works.
*/
mutable std::vector<unsigned long> sets_size;
unsigned long number_of_sets{0};
disjoint_subsets disjoint_subsets_;
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_DISJOINT_SUBsETS_SIZED_Hh_

View File

@ -0,0 +1,121 @@
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_DISJOINT_SUBsETS_SIZED_ABSTRACT_Hh_
#ifdef DLIB_DISJOINT_SUBsETS_SIZED_ABSTRACT_Hh_
#include <vector>
#include "../algs.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class disjoint_subsets_sized : public disjoint_subsets
{
/*!
INITIAL VALUE
- size() == 0
- get_number_of_sets() == 0
WHAT THIS OBJECT REPRESENTS
This object represents a set of integers which is partitioned into
a number of disjoint subsets. It supports the two fundamental operations
of finding which subset a particular integer belongs to as well as
merging subsets.
!*/
public:
virtual void clear (
) noexcept;
/*!
ensures
- #size() == 0
- #get_number_of_sets() == 0
- returns this object to its initial value
!*/
virtual void set_size (
unsigned long new_size
);
/*!
ensures
- #size() == new_size
- #get_number_of_sets() == new_size
- for all valid i:
- #find_set(i) == i
(i.e. this object contains new_size subsets, each containing exactly one element)
- #get_size_of_set(i) == 1
!*/
virtual unsigned long size (
) const noexcept;
/*!
ensures
- returns the total number of integer elements represented
by this object.
!*/
virtual unsigned long find_set (
unsigned long item
) const;
/*!
requires
- item < size()
ensures
- Each disjoint subset can be represented by any of its elements (since
the sets are all disjoint). In particular, for each subset we define
a special "representative element" which is used to represent it.
Therefore, this function returns the representative element for the
set which contains item.
- find_set(find_set(item)) == find_set(item)
- Note that if A and B are both elements of the same subset then we always
have find_set(A) == find_set(B).
!*/
virtual unsigned long merge_sets (
unsigned long a,
unsigned long b
);
/*!
requires
- a != b
- a < size()
- b < size()
- find_set(a) == a
(i.e. a is the representative element of some set)
- find_set(b) == b
(i.e. b is the representative element of some set)
ensures
- #find_set(a) == #find_set(b)
(i.e. merges the set's containing a and b)
- #get_size_of_set(#find_set(a)) == get_size_of_set(a) + get_size_of_set(b)
- #get_number_of_sets() == get_number_of_sets() - 1
- returns #find_set(a)
!*/
unsigned long get_number_of_sets (
) const noexcept;
/*!
ensures
- returns the current number of different subsets.
!*/
unsigned long get_size_of_set(
unsigned long item
) const;
/*!
requires
- item < size()
- find_set(item) == item
(i.e. item is the representative element of some set)
ensures
- returns the number of elements which belongs to the set where item is the representative element.
!*/
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_DISJOINT_SUBsETS_ABSTRACT_Hh_

View File

@ -0,0 +1,9 @@
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_DISJOINt_SUBSETS_SIZED_
#define DLIB_DISJOINt_SUBSETS_SIZED_
#include "disjoint_subsets/disjoint_subsets_sized.h"
#endif // DLIB_DISJOINt_SUBSETS_SIZED_

View File

@ -49,6 +49,7 @@ set (tests
directed_graph.cpp
discriminant_pca.cpp
disjoint_subsets.cpp
disjoint_subsets_sized.cpp
ekm_and_lisf.cpp
empirical_kernel_map.cpp
entropy_coder.cpp
@ -176,5 +177,3 @@ if (NOT DLIB_NO_GUI_SUPPORT)
add_subdirectory(examples)
add_subdirectory(tools)
endif()

View File

@ -10,7 +10,7 @@
#include "tester.h"
namespace
namespace
{
using namespace test;
@ -35,7 +35,6 @@ namespace
DLIB_TEST(s.find_set(3) == 3);
DLIB_TEST(s.find_set(4) == 4);
unsigned long id = s.merge_sets(1,3);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.find_set(1) == id);
@ -101,6 +100,3 @@ namespace
}

View File

@ -0,0 +1,143 @@
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <dlib/disjoint_subsets_sized.h>
#include "tester.h"
namespace
{
using namespace test;
using namespace dlib;
using namespace std;
logger dlog("test.disjoint_subsets_sized");
void test_disjoint_subsets_sized()
{
print_spinner();
disjoint_subsets_sized s;
DLIB_TEST(s.size() == 0);
DLIB_TEST(s.get_number_of_sets() == 0);
s.set_size(5);
DLIB_TEST(s.size() == 5);
DLIB_TEST(s.get_number_of_sets() == 5);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.find_set(1) == 1);
DLIB_TEST(s.find_set(2) == 2);
DLIB_TEST(s.find_set(3) == 3);
DLIB_TEST(s.find_set(4) == 4);
DLIB_TEST(s.get_size_of_set(0) == 1);
DLIB_TEST(s.get_size_of_set(1) == 1);
DLIB_TEST(s.get_size_of_set(2) == 1);
DLIB_TEST(s.get_size_of_set(3) == 1);
DLIB_TEST(s.get_size_of_set(4) == 1);
unsigned long id = s.merge_sets(1,3);
DLIB_TEST(s.get_number_of_sets() == 4);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.find_set(1) == id);
DLIB_TEST(s.find_set(2) == 2);
DLIB_TEST(s.find_set(3) == id);
DLIB_TEST(s.find_set(4) == 4);
DLIB_TEST(s.get_size_of_set(0) == 1);
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 2);
DLIB_TEST(s.get_size_of_set(2) == 1);
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 2);
DLIB_TEST(s.get_size_of_set(4) == 1);
id = s.merge_sets(s.find_set(1),4);
DLIB_TEST(s.get_number_of_sets() == 3);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.find_set(1) == id);
DLIB_TEST(s.find_set(2) == 2);
DLIB_TEST(s.find_set(3) == id);
DLIB_TEST(s.find_set(4) == id);
DLIB_TEST(s.get_size_of_set(0) == 1);
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 3);
DLIB_TEST(s.get_size_of_set(2) == 1);
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 3);
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 3);
unsigned long id2 = s.merge_sets(0,2);
DLIB_TEST(s.get_number_of_sets() == 2);
DLIB_TEST(s.find_set(0) == id2);
DLIB_TEST(s.find_set(1) == id);
DLIB_TEST(s.find_set(2) == id2);
DLIB_TEST(s.find_set(3) == id);
DLIB_TEST(s.find_set(4) == id);
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 2);
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 3);
DLIB_TEST(s.get_size_of_set(s.find_set(2)) == 2);
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 3);
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 3);
id = s.merge_sets(s.find_set(1),s.find_set(0));
DLIB_TEST(s.get_number_of_sets() == 1);
DLIB_TEST(s.find_set(0) == id);
DLIB_TEST(s.find_set(1) == id);
DLIB_TEST(s.find_set(2) == id);
DLIB_TEST(s.find_set(3) == id);
DLIB_TEST(s.find_set(4) == id);
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 5);
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 5);
DLIB_TEST(s.get_size_of_set(s.find_set(2)) == 5);
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 5);
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 5);
DLIB_TEST(s.size() == 5);
s.set_size(1);
DLIB_TEST(s.size() == 1);
DLIB_TEST(s.get_number_of_sets() == 1);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.get_size_of_set(0) == 1);
s.set_size(2);
DLIB_TEST(s.size() == 2);
DLIB_TEST(s.get_number_of_sets() == 2);
DLIB_TEST(s.find_set(0) == 0);
DLIB_TEST(s.find_set(1) == 1);
DLIB_TEST(s.get_size_of_set(0) == 1);
DLIB_TEST(s.get_size_of_set(1) == 1);
id = s.merge_sets(0,1);
DLIB_TEST(s.size() == 2);
DLIB_TEST(s.get_number_of_sets() == 1);
DLIB_TEST(id == s.find_set(0));
DLIB_TEST(id == s.find_set(1));
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 2);
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 2);
DLIB_TEST(s.size() == 2);
s.clear();
DLIB_TEST(s.size() == 0);
DLIB_TEST(s.get_number_of_sets() == 0);
}
class tester_disjoint_subsets_sized : public tester
{
public:
tester_disjoint_subsets_sized (
) :
tester ("test_disjoint_subsets_sized",
"Runs tests on the disjoint_subsets_sized component.")
{}
void perform_test (
)
{
test_disjoint_subsets_sized();
}
} a;
}