...Visual Studio 2017Projectspermutepermutepermute.cpp 1
/**
* @mainpage
* @anchor mainpage
* @brief
* @details
* @copyright Russell John Childs, PhD, 2019
* @author Russell John Childs, PhD
* @date 2019-07-14
*
*
* This is an O(n) algorithm that generates the kth lexicographically ordered
* permutation of an n-element array from the integer k.
* Example, for a three-element array:
* 0 --> 0 1 2
* 1 --> 0 2 1
* 2 --> 1 0 2
* 3 --> 1 2 0
* 4 --> 2 0 1
* 5 --> 2 1 0
*
* It utilises the factoradic representation of k. Algorithms generally require
* O(n^2) decoding of the factoradic into a permutation, due to the need to
* delete elements, O(n), from the array n times as they are moved to the
* permutation.
*
* This algorithm decodes the factoradic in O(n), by using a hash
* function, invented by the author, instead of deletions.
*
* This algorithm has a better order of complexity than std::next_permutation,
* which is O(n), but would require O(k*n) to iterate to the kth permutation.
*
* Limitations: O(n) is achieved through a hash function that is limited to
* built-in types up to 128-bit integers. This limits n to 128,
* i.e. the array cannot be larger than 128. This can be extended using
* multiprecision integers or std::bitset, but the order-of-complexity will be,
* at best, O(n * log2 n).
*
* The code is undocumented and questions should be directed to the author.
*
* @file permute.cpp
* @see
* @ref mainpage
*/
#include <vector>
#include <algorithm>
#include <numeric>
#include <limits>
#include <iostream>
#include <iomanip>
#include <bitset>
template<typename T = unsigned long long>
unsigned hash(T h, T i)
{
auto c = [](T u)
...Visual Studio 2017Projectspermutepermutepermute.cpp 2
{
const unsigned char_bit = std::numeric_limits<unsigned char>::digits;
u = u - ((u >> 1) & (T)~(T)0 / 3);
u = (u & (T)~(T)0 / 15 * 3) + ((u >> 2) & (T)~(T)0 / 15 * 3);
u = (u + (u >> 4)) & (T)~(T)0 / 255 * 15;
return (T)(u * ((T)~(T)0 / 255)) >> (sizeof(T) - 1) * char_bit;
};
unsigned ret_val = i;
i = (((T)(1) << (i + 1)) - 1) & h;
auto j = ret_val + c(i);
auto prev = j+1;
while (j != 0 && j != prev)
{
prev = j;
i = h >> j;
i = static_cast<T>(std::log2((~i) & (i + 1)));
j += i;
j = ret_val + c(((h << (32 - j)) >> (32 - j)));
}
return j;
}
template<typename T = unsigned long long>
std::vector<T>
factoradic(unsigned size, T lehmer)
{
std::vector<unsigned> ret_val(size, 0);
for (unsigned i = 1; lehmer != 0; ++i)
{
ret_val[ret_val.size() - i] = lehmer % i;
lehmer /= i;
}
return ret_val;
}
template<typename T = unsigned long long>
std::vector<unsigned> permute(unsigned size, T i)
{
std::vector<unsigned> ret_val(size);
std::vector<unsigned> start(size);
std::iota(start.begin(), start.end(), 0);
auto lehmer = factoradic(size, i);
unsigned h = 0;
for (unsigned j = 0; j < lehmer.size(); ++j)
{
auto ind = hash(h, lehmer[j]);
ret_val[j] = start[ind];
h |= 1UL << ind;
}
return ret_val;
}
void test_permute(unsigned size)
{
unsigned factorial = static_cast<unsigned>(std::tgamma(size + 1));
for (unsigned i = 0; i < std::min<unsigned>(factorial, 120); ++i)
...Visual Studio 2017Projectspermutepermutepermute.cpp 3
{
for (auto& elem : permute(size, i)) std::cout << elem << " ";
std::cout << std::endl;
}
}
int main(void)
{
test_permute(5);
}

Permute

  • 1.
    ...Visual Studio 2017Projectspermutepermutepermute.cpp1 /** * @mainpage * @anchor mainpage * @brief * @details * @copyright Russell John Childs, PhD, 2019 * @author Russell John Childs, PhD * @date 2019-07-14 * * * This is an O(n) algorithm that generates the kth lexicographically ordered * permutation of an n-element array from the integer k. * Example, for a three-element array: * 0 --> 0 1 2 * 1 --> 0 2 1 * 2 --> 1 0 2 * 3 --> 1 2 0 * 4 --> 2 0 1 * 5 --> 2 1 0 * * It utilises the factoradic representation of k. Algorithms generally require * O(n^2) decoding of the factoradic into a permutation, due to the need to * delete elements, O(n), from the array n times as they are moved to the * permutation. * * This algorithm decodes the factoradic in O(n), by using a hash * function, invented by the author, instead of deletions. * * This algorithm has a better order of complexity than std::next_permutation, * which is O(n), but would require O(k*n) to iterate to the kth permutation. * * Limitations: O(n) is achieved through a hash function that is limited to * built-in types up to 128-bit integers. This limits n to 128, * i.e. the array cannot be larger than 128. This can be extended using * multiprecision integers or std::bitset, but the order-of-complexity will be, * at best, O(n * log2 n). * * The code is undocumented and questions should be directed to the author. * * @file permute.cpp * @see * @ref mainpage */ #include <vector> #include <algorithm> #include <numeric> #include <limits> #include <iostream> #include <iomanip> #include <bitset> template<typename T = unsigned long long> unsigned hash(T h, T i) { auto c = [](T u)
  • 2.
    ...Visual Studio 2017Projectspermutepermutepermute.cpp2 { const unsigned char_bit = std::numeric_limits<unsigned char>::digits; u = u - ((u >> 1) & (T)~(T)0 / 3); u = (u & (T)~(T)0 / 15 * 3) + ((u >> 2) & (T)~(T)0 / 15 * 3); u = (u + (u >> 4)) & (T)~(T)0 / 255 * 15; return (T)(u * ((T)~(T)0 / 255)) >> (sizeof(T) - 1) * char_bit; }; unsigned ret_val = i; i = (((T)(1) << (i + 1)) - 1) & h; auto j = ret_val + c(i); auto prev = j+1; while (j != 0 && j != prev) { prev = j; i = h >> j; i = static_cast<T>(std::log2((~i) & (i + 1))); j += i; j = ret_val + c(((h << (32 - j)) >> (32 - j))); } return j; } template<typename T = unsigned long long> std::vector<T> factoradic(unsigned size, T lehmer) { std::vector<unsigned> ret_val(size, 0); for (unsigned i = 1; lehmer != 0; ++i) { ret_val[ret_val.size() - i] = lehmer % i; lehmer /= i; } return ret_val; } template<typename T = unsigned long long> std::vector<unsigned> permute(unsigned size, T i) { std::vector<unsigned> ret_val(size); std::vector<unsigned> start(size); std::iota(start.begin(), start.end(), 0); auto lehmer = factoradic(size, i); unsigned h = 0; for (unsigned j = 0; j < lehmer.size(); ++j) { auto ind = hash(h, lehmer[j]); ret_val[j] = start[ind]; h |= 1UL << ind; } return ret_val; } void test_permute(unsigned size) { unsigned factorial = static_cast<unsigned>(std::tgamma(size + 1)); for (unsigned i = 0; i < std::min<unsigned>(factorial, 120); ++i)
  • 3.
    ...Visual Studio 2017Projectspermutepermutepermute.cpp3 { for (auto& elem : permute(size, i)) std::cout << elem << " "; std::cout << std::endl; } } int main(void) { test_permute(5); }