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 162 163 164 165
|
// inverse permutation mapping problem
#include <iostream>
#include <string>
#include <array>
#include<algorithm>
#include <random>
#include <chrono>
using std::cout;
using std::string;
// the core functions
string create_permutation( string alpha );// generate permutation of given string
bool is_permutation( const string& a, const string& b );// courtesy JLBorges. Works for general alphabet
string findInversePerm( const string& permStr, const string& alpha );// Generic. Uses findEle(), which deals with the alpha classification
// utility and helper functions
int classifyALPHABET( const string& alpha );// returns: 1 = O(1), 2 = O(logn), 3 = O(n) lookups
int findEle( char c, const string& alpha_ord, int alphaType );// employs method appropriate to the alphaType. returns -1 by default
int bin_search( char c, const string& alpha_ord );// returns index where c appears in alpha_ord for alpha_ord only. -1 if not found
int bs_rec( const char val, const char* pa, int lo, int hi );// recursive helper callled by bin_search()
int main()//**************** MAIN ************************
{
// The 3 alphabet types
const string ALPHA_SeqOrd = "ABCDEFGHIJKLMN";// ordered sequential values = O(1) value lookup
const string ALPHA_Ord = "AEIJMNOQRTUVXZ";// ordered but non-sequential values = (logn) lookup
const string ALPHA_Rand = "XETIAVQJMORNUZ";// random order. Worst case = O(n) lookup
int choice = 0;
cout << "Choose an alphabet type\n";
cout << "1: Element values ordered and sequential. O(1) lookup.\n";
cout << "2: Element values ordered, but not sequential. O(logn) lookup.\n";
cout << "3: Values in random order = permutation of type 2 above. O(n) lookup.\n";
cout << "type: ";
std::cin >> choice;
const string* pAlpha = nullptr;
switch( choice )
{
case 1: pAlpha = &ALPHA_SeqOrd; break;
case 2: pAlpha = &ALPHA_Ord; break;
case 3: pAlpha = &ALPHA_Rand; break;
default: return 1;
}
if( !pAlpha ) return 2;
const string& rAlpha = *pAlpha;// for notational convenience in following code
std::string perm = create_permutation(rAlpha);// generate permutation of selected alphabet type
bool isPerm = is_permutation(perm, rAlpha);// verify that perm is a permutation of rAlpha
if( isPerm )
{
std::string invPerm = findInversePerm( perm, rAlpha );// generate the inverse permutation
cout << "\nalpha = " << rAlpha << " type: " << classifyALPHABET( rAlpha ) << '\n';// show alpha and type
cout << "perm = " << perm << '\n';
cout << "invPerm = " << invPerm << '\n';
cout << "perm back? " << findInversePerm( invPerm, rAlpha ) << '\n';// inv inv permute
}
else
cout << "perm isn\'t a permutation of " << rAlpha << '\n';
return 0;
}// end of main()
std::string create_permutation( std::string alpha )
{
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::shuffle( alpha.begin(), alpha.end(), std::default_random_engine(seed) );
return alpha;
}
// courtesy JLBorges
bool is_permutation( const std::string& a, const std::string& b )
{
if( a.size() != b.size() ) return false ;
static constexpr std::size_t N = std::numeric_limits<unsigned char>::max() + 1 ;
std::array<std::size_t,N> aa {} ;
for( unsigned char c : a ) ++aa[c] ;
std::array<std::size_t,N> bb {} ;
for( unsigned char c : b ) ++bb[c] ;
return aa == bb ;
}
int classifyALPHABET( const string& alpha )// returns: 1 = O(1), 2 = O(logn), 3 = O(n) lookups
{
if( alpha.length() < 2 ) return 1;
int classification = 1;// best case assumed
int currVal = 0, lastVal = (int)alpha[0];
for( size_t i = 1; i < alpha.length(); ++i )
{
currVal = (int)alpha[i];
if( currVal < lastVal ) return 3;// random order. Must be checked for to end of alpha
if( currVal != lastVal + 1 )
classification = 2;// can't break. Above condition may still be found.
lastVal = currVal;
}
return classification;
}
// employs method appropriate to the alphabet type. alphaType must be correct.
// This method would be private in a class. Returns index to element where c found.
int findEle( char c, const string& alpha_ord, int alphaType )
{
switch( alphaType )
{
case 1:// O(1) lookup = calculate result
return (int)( c - alpha_ord[0] );
case 2:// O(logn) lookup = at least they're in order
return bin_search( c, alpha_ord );
case 3:// O(n) lookup = could be anywhere. Sequential search good as any.
for( size_t i=0; i < alpha_ord.length(); ++i )
if( c == alpha_ord[i] ) return i;
}
// this handles default case
return -1;// dangerous return value?
}
// precond: alpha_ord is correct type = ordered perm
int bin_search( char c, const string& alpha_ord )// returns index where c appears in alpha_ord
{
int idx = bs_rec( c, alpha_ord.c_str(), 0, (int)alpha_ord.length() - 1 );
if( alpha_ord[idx] == c ) return idx;
return -1;// fail
}
int bs_rec( const char val, const char* pa, int lo, int hi )
{
if( lo < hi )
{
int mid = (lo+hi)/2;
if( val == pa[mid] ) return mid;// new
if( val < pa[mid] ) return bs_rec( val, pa, lo, mid );// new
// if( val <= pa[mid] ) return bs_rec( val, pa, lo, mid );// old
return bs_rec( val, pa, mid+1, hi );
}
return lo;
}
// generic. findEle() fields cases.
std::string findInversePerm( const std::string& permStr, const string& alpha )
{
if( !is_permutation( permStr, alpha ) ) return std::string();// empty string
int alphaClass = classifyALPHABET( alpha );
if( alphaClass == -1 ) return std::string();// empty string
std::string invPermStr( alpha );// actually, just need the sizes =
for( size_t i = 0; i < alpha.length(); ++i )
{
int j = findEle( permStr[i], alpha, alphaClass );
if( j >= 0 ) invPermStr[j] = alpha[i];
}
return invPermStr;
}
|