Here is another attempt. Green teases my eyes, so I'll explain the idea.
Permutations of arbitrary items correspond to permutation of numbers 1 through n. One can encode a permutation with a vector. One way to do this is to simply store the permuted numbers in the vector and then use them as indices when applying the permutation. Another approach is to store one transposition (essentially a swap operation) in every element. If the element with index i holds value j (j <= i), then the transposition is (i, j). Elements at index i and index j must be swapped. All transpositions are applied one after another by interchanging elements in the permuted array.
For the vector of transpositions: [0, 0, 2, 1],
the transpositions are (0, 0), (1, 0), (2, 2), (3, 1). The corresponding permutation of [0, 1, 2, 3] is [1, 3, 2, 0]. (Swap 0 with 0, 1 with 0,..) The first transposition can be omitted, because of the constraint (i, j), j <= i. This implies that it is always (0, 0). I skip it entirely and my indexes in the code look funnier.
What I do at each step (for generating the next permutation using transpositions vector) is to increment the value stored at every index i with 1. The resulting value j is taken modulo (i+1) (i.e. j = j % (i+1)). This guarantees that j <= i and the prefix part of the vector is independent from the suffix part. That is, the leading transpositions can be applied on the beginning portion of the target array independently of the trailing ones.
The problem is that every so often a transposition will wrap-around, because (j + 1) % (i + 1) == 0. The indices i are generally not co-prime and this can happen simultaneously for the entire vector, even before every permutation has been generated. For large number of elements there is long sequence before this occurs. But anyways, this is why I maintain another vector with offsets. I increase the offsets vector every time a total wrap-around occurs (the transpositions simultaneously reset) as if it is one big numeral (i.e. perform increment with carry). This starts another unique sequence of permutations with those offsets applied. Only prime indices should have offsets (my observation).
All permutations will be generated, and because every step alters (almost) all transpositions, the overall appearance is that the data is shuffled. Of course, this sequence is deterministic and can be hacked. One can always start from random permutation in the target array, but this will only hide the fact from the unsuspecting eye.
Here is the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <ctime>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
using namespace std;
using namespace boost;
using namespace boost::lambda;
class PermutationIndex {
public:
PermutationIndex(size_t size) :
m_transpositions(size - 1), m_offsets(size - 1), m_primes(size - 1, true)
{
for (int i = 1; i < (int)m_primes.size(); ++i)
{
for (int j = 0; j < i; ++j)
{
if (m_primes[i] &&
((i + 2) % (j + 2)) == 0)
{
m_primes[i] = false;
break;
}
}
}
}
void nextShuffle()
{
int non_reset = m_transpositions.size();
for (int i = 0; i < (int)m_transpositions.size(); ++i)
{
m_transpositions[i] = (m_transpositions[i] + 1) % (i + 2);
if (m_transpositions[i] == m_offsets[i])
{
--non_reset;
}
}
if (non_reset == 0)
{
for (int i = 0; i < (int)m_transpositions.size(); ++i)
{
if (m_primes[i]) { continue; }
else
{
int result = m_offsets[i] + 1;
if (result < i + 2)
{
m_transpositions[i] = m_offsets[i] = result;
break;
}
else
{
m_transpositions[i] = m_offsets[i] = 0;
}
}
}
}
}
template <class RandomAccessIterator>
void apply(RandomAccessIterator first, RandomAccessIterator last)
{
int size = distance(first, last);
for (int i = 1; i < size; ++i)
{
int j = m_transpositions[i - 1];
if (i != j) swap(first[i], first[j]);
}
}
void print()
{
function <void (int)> printer = cout << _1 << ' ';
cout << "m_transpositions:\n";
for_each(m_transpositions.begin(), m_transpositions.end(), printer);
cout << '\n' << endl;
cout << "m_offsets:\n";
for_each(m_offsets.begin(), m_offsets.end(), printer);
cout << '\n' << endl;
cout << "m_primes:\n";
for_each(m_primes.begin(), m_primes.end(), printer);
cout << '\n' << endl;
}
private:
vector<unsigned int> m_transpositions;
vector<unsigned int> m_offsets;
vector<bool> m_primes;
};
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
template <typename T, size_t N>
inline size_t array_size(T(& array)[N]) { return N; }
template <typename T, size_t N>
inline T* array_begin(T(& array)[N]) { return array; }
template <typename T, size_t N>
inline T* array_end(T(& array)[N]) { return &array[N]; }
template <typename T>
inline void fill_sequence(T* begin, T* end, const T& start = T())
{
*begin = start;
transform(begin, end - 1, begin + 1, _1 + 1);
}
int main()
{
int data[3];
int permutation[ARRAY_SIZE(data)];
srand(time(NULL));
fill_sequence(array_begin(data), array_end(data));
random_shuffle(array_begin(data), array_end(data), bind(rand) % _1);
PermutationIndex p_index(array_size(data));
cout << "data:\n";
for_each(array_begin(data), array_end(data), cout << _1 << ' ');
cout << endl;
cout
<< "\n"
"Permutations:" << endl;
for (int i = 0; i < 7; ++i)
{
p_index.nextShuffle();
// *** FOR DEBUGGING ***
// p_index.print();
// cout << endl;
copy(array_begin(data), array_end(data), array_begin(permutation));
p_index.apply(array_begin(permutation), array_end(permutation));
for_each(array_begin(permutation), array_end(permutation), cout << _1 << ' ');
cout << '\n' << endl;
}
}
|
Best Regards