Merge pull request #39 from ZaMaZaN4iK/feature_branch/apply_permutation

Apply_permutation
This commit is contained in:
Marshall Clow
2017-11-01 12:57:31 -07:00
committed by GitHub
7 changed files with 464 additions and 1 deletions

View File

@ -68,6 +68,7 @@ Thanks to all the people who have reviewed this library and made suggestions for
[include hex.qbk]
[include is_palindrome.qbk]
[include is_partitioned_until.qbk]
[include apply_permutation.qbk]
[endsect]

96
doc/apply_permutation.qbk Normal file
View File

@ -0,0 +1,96 @@
[/ File apply_permutation.qbk]
[section:apply_permutation apply_permutation]
[/license
Copyright (c) 2017 Alexander Zaitsev
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
The header file 'apply_permutation.hpp' contains two algorithms, apply_permutation and apply_reverse_permutation. Also there are range-based versions.
The algorithms transform the item sequence according to index sequence order.
The routine `apply_permutation` takes a item sequence and a order sequence. It reshuffles item sequence according to order sequence. Every value in order sequence means where the item comes from. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed).
The routine `apply_reverse_permutation` takes a item sequence and a order sequence. It will reshuffle item sequence according to order sequence. Every value in order sequence means where the item goes to. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed).
Implementations are based on these articles:
https://blogs.msdn.microsoft.com/oldnewthing/20170102-00/?p=95095
https://blogs.msdn.microsoft.com/oldnewthing/20170103-00/?p=95105
https://blogs.msdn.microsoft.com/oldnewthing/20170104-00/?p=95115
https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145
https://blogs.msdn.microsoft.com/oldnewthing/20170110-00/?p=95155
https://blogs.msdn.microsoft.com/oldnewthing/20170111-00/?p=95165
The routines come in 2 forms; the first one takes two iterators to define the item range and one iterator to define the beginning of index range. The second form takes range to define the item sequence and range to define index sequence.
[heading interface]
There are two versions of algorithms:
1) takes four iterators.
2) takes two ranges.
``
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end);
template<typename Range1, typename Range2>
void apply_permutation(Range1& item_range, Range2& ind_range);
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end);
template<typename Range1, typename Range2>
void apply_reverse_permutation(Range1& item_range, Range2& ind_range);
``
[heading Examples]
Given the containers:
std::vector<int> emp_vec, emp_order,
std::vector<int> one{1}, one_order{0},
std::vector<int> two{1,2}, two_order{1,0},
std::vector<int> vec{1, 2, 3, 4, 5},
std::vector<int> order{4, 2, 3, 1, 0}, then
``
apply_permutation(emp_vec, emp_order)) --> no changes
apply_reverse_permutation(emp_vec, emp_order)) --> no changes
apply_permutation(one, one_order) --> no changes
apply_reverse_permutation(one, one_order) --> no changes
apply_permutation(two, two_order) --> two:{2,1}
apply_reverse_permutation(two, two_order) --> two:{2,1}
apply_permutation(vec, order) --> vec:{5, 3, 4, 2, 1}
apply_reverse_permutation(vec, order) --> vec:{5, 4, 2, 3, 1}
``
[heading Iterator Requirements]
`apply_permutation` and 'apply_reverse_permutation' work only on RandomAccess iterators. RandomAccess iterators required both for item and index sequences.
[heading Complexity]
All of the variants of `apply_permutation` and `apply_reverse_permutation` run in ['O(N)] (linear) time.
More
[heading Exception Safety]
All of the variants of `apply_permutation` and `apply_reverse_permutation` take their parameters by iterators or reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.
[heading Notes]
* If ItemSequence and IndexSequence are not equal, behavior is undefined.
* `apply_permutation` and `apply_reverse_permutation` work also on empty sequences.
* Order sequence must be zero-indexed.
* Order sequence gets permuted.
[endsect]
[/ File apply_permutation.qbk
Copyright 2017 Alexander Zaitsev
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt).
]

View File

@ -22,4 +22,4 @@ exe clamp_example : clamp_example.cpp ;
exe search_example : search_example.cpp ;
exe is_palindrome_example : is_palindrome_example.cpp;
exe is_partitioned_until_example : is_partitioned_until_example.cpp;
exe apply_permutation_example : apply_permutation_example.cpp;

View File

@ -0,0 +1,69 @@
/*
Copyright (c) Alexander Zaitsev <zamazan4ik@gmail.com>, 2017
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
See http://www.boost.org/ for latest version.
*/
#include <vector>
#include <iostream>
#include <boost/algorithm/apply_permutation.hpp>
namespace ba = boost::algorithm;
int main ( int /*argc*/, char * /*argv*/ [] )
{
// WARNING: Example require C++11 or newer compiler
{
std::cout << "apply_permutation with iterators:\n";
std::vector<int> vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0};
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
for (const auto& x : vec)
{
std::cout << x << ", ";
}
std::cout << std::endl;
}
{
std::cout << "apply_reverse_permutation with iterators:\n";
std::vector<int> vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0};
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
for (const auto& x : vec)
{
std::cout << x << ", ";
}
std::cout << std::endl;
}
{
std::cout << "apply_reverse_permutation with ranges:\n";
std::vector<int> vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0};
ba::apply_reverse_permutation(vec, order);
for (const auto& x : vec)
{
std::cout << x << ", ";
}
std::cout << std::endl;
}
{
std::cout << "apply_permutation with ranges:\n";
std::vector<int> vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0};
ba::apply_permutation(vec, order);
for (const auto& x : vec)
{
std::cout << x << ", ";
}
std::cout << std::endl;
}
return 0;
}

View File

@ -0,0 +1,125 @@
/*
Copyright (c) Alexander Zaitsev <zamazan4ik@gmail.com>, 2017
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
See http://www.boost.org/ for latest version.
Based on https://blogs.msdn.microsoft.com/oldnewthing/20170104-00/?p=95115
*/
/// \file apply_permutation.hpp
/// \brief Apply permutation to a sequence.
/// \author Alexander Zaitsev
#ifndef BOOST_ALGORITHM_APPLY_PERMUTATION_HPP
#define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP
#include <algorithm>
#include <type_traits>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
namespace boost { namespace algorithm
{
/// \fn apply_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_begin The start of the item sequence
/// \param item_end One past the end of the item sequence
/// \param ind_begin The start of the index sequence.
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void
apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end)
{
using Diff = typename std::iterator_traits<RandomAccessIterator1>::difference_type;
using std::swap;
Diff size = std::distance(item_begin, item_end);
for (Diff i = 0; i < size; i++)
{
auto current = i;
while (i != ind_begin[current])
{
auto next = ind_begin[current];
swap(item_begin[current], item_begin[next]);
ind_begin[current] = current;
current = next;
}
ind_begin[current] = current;
}
}
/// \fn apply_reverse_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_begin The start of the item sequence
/// \param item_end One past the end of the item sequence
/// \param ind_begin The start of the index sequence.
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void
apply_reverse_permutation(
RandomAccessIterator1 item_begin,
RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin,
RandomAccessIterator2 ind_end)
{
using Diff = typename std::iterator_traits<RandomAccessIterator2>::difference_type;
using std::swap;
Diff length = std::distance(item_begin, item_end);
for (Diff i = 0; i < length; i++)
{
while (i != ind_begin[i])
{
Diff next = ind_begin[i];
swap(item_begin[i], item_begin[next]);
swap(ind_begin[i], ind_begin[next]);
}
}
}
/// \fn apply_permutation ( Range1 item_range, Range2 ind_range )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_range The item sequence
/// \param ind_range The index sequence
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename Range1, typename Range2>
void
apply_permutation(Range1& item_range, Range2& ind_range)
{
apply_permutation(boost::begin(item_range), boost::end(item_range),
boost::begin(ind_range), boost::end(ind_range));
}
/// \fn apply_reverse_permutation ( Range1 item_range, Range2 ind_range )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_range The item sequence
/// \param ind_range The index sequence
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename Range1, typename Range2>
void
apply_reverse_permutation(Range1& item_range, Range2& ind_range)
{
apply_reverse_permutation(boost::begin(item_range), boost::end(item_range),
boost::begin(ind_range), boost::end(ind_range));
}
}}
#endif //BOOST_ALGORITHM_APPLY_PERMUTATION_HPP

View File

@ -83,6 +83,9 @@ alias unit_test_framework
# Is_partitioned_until tests
[ run is_partitioned_until_test.cpp unit_test_framework : : : : is_partitioned_until_test ]
# Apply_permutation tests
[ run apply_permutation_test.cpp unit_test_framework : : : : apply_permutation_test ]
;
}

View File

@ -0,0 +1,169 @@
/*
Copyright (c) Alexander Zaitsev <zamazan4ik@gmail.com>, 2017
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
See http://www.boost.org/ for latest version.
*/
#include <vector>
#include <boost/algorithm/apply_permutation.hpp>
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
namespace ba = boost::algorithm;
void test_apply_permutation()
{
//Empty
{
std::vector<int> vec, order, result;
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//1 element
{
std::vector<int> vec, order, result;
vec.push_back(1);
order.push_back(0);
result = vec;
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//2 elements, no changes
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2);
order.push_back(0); order.push_back(1);
result = vec;
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//2 elements, changed
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2);
order.push_back(1); order.push_back(0);
result.push_back(2); result.push_back(1);
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Multiple elements, no changes
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4);
result = vec;
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Multiple elements, changed
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0);
result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1);
ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Just test range interface
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4);
result = vec;
ba::apply_permutation(vec, order);
BOOST_CHECK(vec == result);
}
}
void test_apply_reverse_permutation()
{
//Empty
{
std::vector<int> vec, order, result;
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//1 element
{
std::vector<int> vec, order, result;
vec.push_back(1);
order.push_back(0);
result = vec;
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//2 elements, no changes
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2);
order.push_back(0); order.push_back(1);
result = vec;
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//2 elements, changed
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2);
order.push_back(1); order.push_back(0);
result.push_back(2); result.push_back(1);
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Multiple elements, no changes
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4);
result = vec;
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Multiple elements, changed
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0);
result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1);
ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end());
BOOST_CHECK(vec == result);
}
//Just test range interface
{
std::vector<int> vec, order, result;
vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5);
order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4);
result = vec;
ba::apply_reverse_permutation(vec, order);
BOOST_CHECK(vec == result);
}
}
BOOST_AUTO_TEST_CASE(test_main)
{
test_apply_permutation();
test_apply_reverse_permutation();
}