一种变进制数及其应用(全排列之Hash实现)

我们经常使用的数的进制为“常数进制”,即始终逢p进1。例如,p进制数K可表示为
    K = a0*p^0 + a1*p^1 + a2*p^2 + ... + an*p^n (其中0 <= ai <= p-1),
它可以表示任何一个自然数。

对于这种常数进制表示法,以及各种进制之间的转换大家应该是很熟悉的了,但大家可能很少听说变进制数。这里我要介绍一种特殊的变进制数,它能够被用来实现全排列的Hash函数,并且该Hash函数能够实现完美的防碰撞和空间利用(不会发生碰撞,且所有空间被完全使用,不多不少)。这种全排列Hash函数也被称为全排列数化技术。下面,我们就来看看这种变进制数。

我们考查这样一种变进制数:第1位逢2进1,第2位逢3进1,……,第n位逢n+1进1。它的表示形式为
    K = a1*1! + a2*2! + a3*3! + ... + an*n! (其中0 <= ai <= i),
也可以扩展为如下形式(因为按定义a0始终为0),以与p进制表示相对应:
    K = a0*0! + a1*1! + a2*2! + a3*3! + ... + an*n! (其中0 <= ai <= i)。
(后面的变进制数均指这种变进制数,且采用前一种表示法)

先让我们来考查一下该变进制数的进位是否正确。假设变进制数K的第i位ai为i+1,需要进位,而ai*i!=(i+1)*i!=1*(i+1)!,即正确的向高位进1。这说明该变进制数能够正确进位,从而是一种合法的计数方式。

接下来我们考查n位变进制数K的性质:
(1)当所有位ai均为i时,此时K有最大值
    MAX[K] = 1*1! + 2*2! + 3*3! + ... + n*n!
           = 1! + 1*1! + 2*2! + 3*3! + ... + n*n! - 1
           = (1+1)*1! + 2*2! + 3*3! + ... + n*n! - 1
           = 2! + 2*2! + 3*3! + ... + n*n! - 1
           = ...
           = (n+1)!-1
    因此,n位K进制数的最大值为(n+1)!-1。
(2)当所有位ai均为0时,此时K有最小值0。
因此,n位变进制数能够表示0到(n+1)!-1的范围内的所有自然数,共(n+1)!个。

在一些状态空间搜索算法中,我们需要快速判断某个状态是否已经出现,此时常常使用Hash函数来实现。其中,有一类特殊的状态空间,它们是由全排列产生的,比如N数码问题。对于n个元素的全排列,共产生n!个不同的排列或状态。下面将讨论如何使用这里的变进制数来实现一个针对全排列的Hash函数。

从数的角度来看,全排列和变进制数都用到了阶乘。如果我们能够用0到n!-1这n!个连续的变进制数来表示n个元素的所有排列,那么就能够把全排列完全地数化,建立起全排列和自然数之间一一对应的关系,也就实现了一个完美的Hash函数。那么,我们的想法能否实现呢?答案是肯定的,下面将进行讨论。

假设我们有b0,b1,b2,b3,...,bn共n+1个不同的元素,并假设各元素之间有一种次序关系 b0<b1<b2<...<bn。对它们进行全排列,共产生(n+1)!种不同的排列。对于产生的任一排列 c0,c1,c2,..,cn,其中第i个元素ci(1 <= i <= n)与它前面的i个元素构成的逆序对的个数为di(0 <= di <= i),那么我们得到一个逆序数序列d1,d2,...,dn(0 <= di <= i)。这不就是前面的n位变进制数的各个位么?于是,我们用n位变进制数M来表示该排列:
   M = d1*1! + d2*2! + ... + dn*n!
因此,每个排列都可以按这种方式表示成一个n位变进制数。下面,我们来考查n位变进制数能否与n+1个元素的全排列建立起一一对应的关系。

由于n位变进制数能表示(n+1)!个不同的数,而n+1个元素的全排列刚好有(n+1)!个不同的排列,且每一个排列都已经能表示成一个n位变进制数。如果我们能够证明任意两个不同的排列产生两个不同的变进制数,那么我们就可以得出结论:
★ 定理1 n+1个元素的全排列的每一个排列对应着一个不同的n位变进制数。

对于全排列的任意两个不同的排列p0,p1,p2,...,pn(排列P)和q0,q1,q2,...,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 < i <= n)。
(1)如果qi > pi,那么,
如果在排列Q中qi之前的元素x与qi构成逆序对,即有x > qi,则在排列P中pi之前也有相同元素x > pi(因为x > qi且qi > pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的逆序数大于qi的逆序数。
(2)同理,如果pi > qi,那么qi的逆序数大于pi的逆序数。
因此,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的任意两个不同的排列具有不同的变进制数。至此,定理1得证。

计算n个元素的一个排列的变进制数的算法大致如下(时间复杂度为O(n^2)):
template <typename T>
size_t PermutationToNumber(const T permutation[], int n)
{
    // n不能太大,否则会溢出(如果size_t为32位,则n <= 12)
    size_t result = 0;
    for (int j = 1; j < n; ++j) {
        int count = 0;
        for (int k = 0; k < j; ++k) {
            if (permutation[k] > permutation[j])
                ++count;
        }
        // factorials[j]保存着j!
        result += count * factorials[j];
    }

    return result;
}

说明:
(1)由于n!是一个很大的数,因此一般只能用于较小的n。
(2)有了计算排列的变进制数的算法,我们就可以使用一个大小为n!的数组来保存每一个排列的状态,使用排列的变进制数作为数组下标,从而实现状态的快速检索。如果只是标记状态是否出现,则可以用一位来标记状态。

最后,附上一段完整的代码来演示使用变进制数实现全排列的数化(或者Hash,随便怎么称乎了)。


ImmutableArray.h

[cpp]  view plain copy
  1. /** 
  2. * ImmutableArray.h 
  3. * @Author   Tu Yongce <yongce (at) 126 (dot) com> 
  4. * @Created  2008-10-7 
  5. * @Modified 2008-10-7 
  6. * @Version  0.1 
  7. */  
  8.   
  9. #ifndef IMMUTABLE_ARRAY_H_INCLUDED  
  10. #define IMMUTABLE_ARRAY_H_INCLUDED  
  11.   
  12. #include <vector>  
  13. #include <cassert>  
  14.   
  15. /* 
  16. * 不可修改数组,数组元素值一旦设定即不可修改 
  17. */  
  18. template <typename T>  
  19. class ImmutableArray  
  20. {  
  21. public:  
  22.     typedef T ValueType;  
  23.   
  24. private:  
  25.     ValueType m_placeHolder;  
  26.     std::vector<ValueType> m_data;  
  27.   
  28. public:  
  29.     /* 
  30.     * 构造一个ImmutableArray对象 
  31.     * @param n: 数组元素个数 
  32.     * @param placeHolder: 数组元素在被设定值之前的占位符 
  33.     */  
  34.     ImmutableArray(size_t n, ValueType placeHolder): m_placeHolder(placeHolder),   
  35.         m_data(n, placeHolder)   
  36.     { }  
  37.   
  38.     /* 
  39.     * 在数组的指定位置处存储值 
  40.     * @param index: 指示存储位置的数组下标,必须在范围[0, n)内 
  41.     * @param value: 待存储的值,不能与构造函数使用的参数值placeHolder相同 
  42.     * @return: 如果指定位置已经有值,则返回false,并且放弃存储操作;否则返回true 
  43.     */  
  44.     bool Put(size_t index, ValueType value)   
  45.     {  
  46.         assert(index < m_data.size());  
  47.         assert(value != m_placeHolder);  
  48.   
  49.         if (m_data[index] != m_placeHolder)   
  50.             return false;  
  51.   
  52.         m_data[index] = value;  
  53.   
  54.         return true;  
  55.     }  
  56.   
  57.     /* 
  58.     * 读取数组指定存储位置处的值 
  59.     * @param index: 指示存储位置的数组下标,必须在范围[0, n)内 
  60.     * @return: 如果指定位置已经有值,则返回该值; 
  61.     *          否则,返回构造函数使用的参数值placeHolder 
  62.     */  
  63.     ValueType Get(size_t index) const  
  64.     {  
  65.         return m_data[index];  
  66.     }  
  67.   
  68.     /* 
  69.     * 查询数组指定位置处是否为空(还未存储值) 
  70.     * @param index: 指示存储位置的数组下标,必须在范围[0, n)内 
  71.     * @return: 如果指定位置有值,则返回false;否则返回true 
  72.     */  
  73.     bool Empty(size_t index) const  
  74.     {  
  75.         return m_data[index] == m_placeHolder;  
  76.     }  
  77.   
  78.     /* 
  79.     * 返回数组的元素个数(即返回构造的参数n的值) 
  80.     */  
  81.     size_t Size() const   
  82.     {  
  83.         return m_data.size();  
  84.     }  
  85. };  
  86.   
  87. /* 
  88. * 模板类ImmutableArray针对bool类型的特化类 
  89. * @note: 能够压缩存储空间,有效节约使用的内存资源 
  90. */  
  91. template<>  
  92. class ImmutableArray<bool>  
  93. {  
  94. public:  
  95.     typedef bool ValueType;  
  96.   
  97. private:  
  98.     typedef unsigned char uint8_t;  
  99.     size_t m_size;  
  100.     bool m_placeHolder;  
  101.     std::vector<uint8_t> m_data;  
  102.   
  103. public:  
  104.     ImmutableArray(size_t n, bool placeHolder) : m_size(n),  
  105.         m_placeHolder(placeHolder),   
  106.         m_data((n + 7) / 8, (placeHolder ? 0xFF : 0x00))   
  107.     { }  
  108.   
  109.     bool Put(size_t index, bool value)  
  110.     {  
  111.         assert(index < m_size);  
  112.         assert(value != m_placeHolder);  
  113.   
  114.         bool tag = (m_data[index / 8] & (uint8_t(0x01) << (index % 8))) != 0;  
  115.         if (tag != m_placeHolder)  
  116.             return false;  
  117.   
  118.         // 指定位的0,1互换  
  119.         m_data[index / 8] ^= (uint8_t(0x01) << (index % 8));  
  120.   
  121.         return true;  
  122.     }  
  123.   
  124.     bool Get(size_t index) const  
  125.     {  
  126.         return (m_data[index / 8] & (uint8_t(0x01) << (index % 8))) != 0;  
  127.     }  
  128.   
  129.     bool Empty(size_t index) const  
  130.     {  
  131.         bool tag = (m_data[index / 8] & (uint8_t(0x01) << (index % 8))) != 0;  
  132.         return tag == m_placeHolder;  
  133.     }  
  134.   
  135.     size_t Size() const   
  136.     {  
  137.         return m_size;  
  138.     }  
  139. };  
  140.   
  141. #endif // IMMUTABLE_ARRAY_H_INCLUDED  

 

ImmutableArray_example.cpp

[cpp]  view plain copy
  1. /** 
  2. * ImmutableArray_example.cpp 
  3. * @Author   Tu Yongce <yongce (at) 126 (dot) com> 
  4. * @Created  2008-10-7 
  5. * @Modified 2008-10-7 
  6. * @Version  0.1 
  7. */  
  8.   
  9. #include <iostream>  
  10. #include "ImmutableArray.h"  
  11. #include "Assure.h"  
  12.   
  13. using namespace std;  
  14.   
  15. ANONYMOUS_NAMESPACE_START  
  16.   
  17. class UnitTest  
  18. {  
  19. private:  
  20.     ostream &m_log;  
  21. public:  
  22.     UnitTest(ostream &log): m_log(log)  
  23.     {  
  24.         m_log << "TestImmutableArray Start.../n";  
  25.   
  26.         DoTest1();  
  27.         DoTest2();  
  28.         DoTest3();  
  29.   
  30.         m_log << "TestImmutableArray End/n/n";  
  31.     }  
  32.   
  33. private:  
  34.     void DoTest1()  
  35.     {  
  36.         m_log << "DoTest1 Start.../n";  
  37.   
  38.         try {  
  39.             const size_t NUM = 0x100000;  // 2^20  
  40.             ImmutableArray<bool> arr(NUM, false);  
  41.   
  42.             Assure(m_log, arr.Size() == NUM);  
  43.   
  44.             for (size_t i = 0; i < NUM; ++i) {  
  45.                 Assure(m_log, arr.Empty(i));  
  46.                 Assure(m_log, arr.Get(i) == false);  
  47.                 Assure(m_log, arr.Put(i, true));  
  48.                 Assure(m_log, !arr.Empty(i));  
  49.                 Assure(m_log, arr.Get(i) == true);  
  50.                 Assure(m_log, !arr.Put(i, true));  
  51.             }  
  52.         }  
  53.         catch (AssureException) {  
  54.         }  
  55.   
  56.         m_log << "DoTest1 End/n";  
  57.     }  
  58.   
  59.     void DoTest2()  
  60.     {  
  61.         m_log << "DoTest2 Start.../n";  
  62.   
  63.         try {  
  64.             const size_t NUM = 0x100000;  // 2^20  
  65.             ImmutableArray<bool> arr(NUM, true);  
  66.   
  67.             Assure(m_log, arr.Size() == NUM);  
  68.   
  69.             for (size_t i = 0; i < NUM; ++i) {  
  70.                 Assure(m_log, arr.Empty(i));  
  71.                 Assure(m_log, arr.Get(i) == true);  
  72.                 Assure(m_log, arr.Put(i, false));  
  73.                 Assure(m_log, !arr.Empty(i));  
  74.                 Assure(m_log, arr.Get(i) == false);  
  75.                 Assure(m_log, !arr.Put(i, false));  
  76.             }  
  77.         }  
  78.         catch (AssureException) {  
  79.         }  
  80.   
  81.         m_log << "DoTest2 End/n";  
  82.     }  
  83.   
  84.     void DoTest3()  
  85.     {  
  86.         m_log << "DoTest3 Start.../n";  
  87.   
  88.         try {  
  89.             const size_t NUM = 0x100000;  // 2^20  
  90.             ImmutableArray<int> arr(NUM, -1);  
  91.   
  92.             Assure(m_log, arr.Size() == NUM);  
  93.   
  94.             for (size_t i = 0; i < NUM; ++i) {  
  95.                 Assure(m_log, arr.Empty(i));  
  96.                 Assure(m_log, arr.Get(i) == -1);  
  97.                 Assure(m_log, arr.Put(i, i));  
  98.                 Assure(m_log, !arr.Empty(i));  
  99.                 Assure(m_log, arr.Get(i) == i);  
  100.                 Assure(m_log, !arr.Put(i, i));  
  101.             }  
  102.         }  
  103.         catch (AssureException) {  
  104.         }  
  105.   
  106.         m_log << "DoTest3 End/n";  
  107.     }  
  108. };  
  109.   
  110. #ifdef SYMBOL_DO_TEST  
  111. UnitTest obj(std::clog);  // do test  
  112. #endif // SYMBOL_DO_TEST  
  113.   
  114. ANONYMOUS_NAMESPACE_END  

 

Assure.h

[cpp]  view plain copy
  1. /** 
  2. * Assure.h 
  3. * @Author   Tu Yongce <yongce (at) 126 (dot) com> 
  4. * @Created  2008-1-1  
  5. * @Modified 2008-1-1  
  6. * @Version  0.1 
  7. */  
  8.   
  9. #ifndef ASSURE_H_INCLUDED  
  10. #define ASSURE_H_INCLUDED  
  11.   
  12. #include <ostream>  
  13. #include <exception>  
  14.   
  15. class AssureException: public std::exception  
  16. {  
  17. };  
  18.   
  19. #define Assure(os, x) (void)((!!(x)) || (ShowFailedMessage(os, #x, __FILE__, __LINE__), 0))  
  20.   
  21. inline void ShowFailedMessage(std::ostream &os, const char* expr, const char *file, size_t line)  
  22. {  
  23.     os << "Failed: " << expr << ", file /"" << file << "/", line " << line << '/n';  
  24.     throw AssureException();  
  25. }  
  26.   
  27. #define ANONYMOUS_NAMESPACE_START namespace {  
  28. #define ANONYMOUS_NAMESPACE_END   }  
  29.   
  30. #endif // ASSURE_H_INCLUDED  

 

 

PermutationMap.h

[cpp]  view plain copy
  1. /** 
  2. * PermutationMap.h 
  3. * @Author   Tu Yongce <yongce (at) 126 (dot) com> 
  4. * @Created  2008-10-7 
  5. * @Modified 2008-10-7 
  6. * @Version  0.1 
  7. */  
  8.   
  9. #ifndef PERMUTATION_MAP_H_INCLUDED  
  10. #define PERMUTATION_MAP_H_INCLUDED  
  11.   
  12. #include <vector>  
  13. #include <stdexcept>  
  14. #include <cassert>  
  15. #include "ImmutableArray.h"  
  16.   
  17. template <typename T>  
  18. class PermutationMap   
  19. {  
  20. public:  
  21.     typedef T ValueType;  
  22.   
  23. private:  
  24.     int m_num;  
  25.     std::vector<size_t> m_factorials;  
  26.     ImmutableArray<ValueType> m_data;  
  27.   
  28. public:  
  29.     // n <= 12, 12! = 479001600  
  30.     PermutationMap(int n, ValueType initValue): m_num(n), m_factorials(n, 0),   
  31.         m_data(CaclFactorial(), initValue) { }  
  32.   
  33.     template <typename ElemType>  
  34.     bool Put(const ElemType permutation[], ValueType value)   
  35.     {  
  36.         return m_data.Put(PermutationToNumber(permutation), value);  
  37.     }  
  38.   
  39.     template <typename ElemType>  
  40.     bool Put(const std::vector<ElemType> &permutation, ValueType value)  
  41.     {  
  42.         assert(permutation.size() == m_num);  
  43.         return Put(&permutation[0], value);  
  44.     }  
  45.   
  46.     template <typename ElemType>  
  47.     ValueType Get(const ElemType permutation[]) const  
  48.     {  
  49.         return m_data.Get(PermutationToNumber(permutation));  
  50.     }  
  51.   
  52.     template <typename ElemType>  
  53.     ValueType Get(const std::vector<ElemType> &permutation) const  
  54.     {  
  55.         assert(permutation.size() == m_num);  
  56.         return Get(&permutation[0]);  
  57.     }  
  58.   
  59.     size_t Size() const  
  60.     {  
  61.         return m_factorials[m_num - 1];  
  62.     }  
  63.   
  64. private:  
  65.     size_t CaclFactorial()  
  66.     {  
  67.         m_factorials[0] = 1;  
  68.         for (int i = 2; i <= m_num; ++i) {  
  69.             if (m_factorials[i - 2] * i / i != m_factorials[i - 2])  
  70.                 throw std::overflow_error("overflow in PermutationMap::CaclFactorial");  
  71.             m_factorials[i - 1] = m_factorials[i - 2] * i;  
  72.         }  
  73.   
  74.         return m_factorials[m_num - 1];  
  75.     }  
  76.   
  77.     template <typename ElemType>  
  78.     size_t PermutationToNumber(const ElemType permutation[]) const  
  79.     {  
  80.         size_t result = 0;  
  81.         for (int i = 1; i < m_num; ++i) {  
  82.             int count = 0;  
  83.             for (int k = 0; k < i; ++k) {  
  84.                 if (permutation[k] > permutation[i])  
  85.                     ++count;  
  86.             }  
  87.             result += count * m_factorials[i - 1];  
  88.         }  
  89.   
  90.         return result;  
  91.     }  
  92. };  
  93.   
  94.   
  95. #endif // PERMUTATION_MAP_H_INCLUDED  

 

PermutationMap_example.cpp

[cpp]  view plain copy
  1. /** 
  2. * PermutationMap_example.cpp 
  3. * @Author   Tu Yongce <yongce (at) 126 (dot) com> 
  4. * @Created  2008-10-7 
  5. * @Modified 2008-10-7 
  6. * @Version  0.1 
  7. */  
  8.   
  9. #include <iostream>  
  10. #include <algorithm>  
  11. #include "PermutationMap.h"  
  12. #include "Assure.h"  
  13.   
  14. using namespace std;  
  15.   
  16. ANONYMOUS_NAMESPACE_START  
  17.   
  18. class UnitTest  
  19. {  
  20. private:  
  21.     ostream &m_log;  
  22. public:  
  23.     UnitTest(ostream &log): m_log(log)  
  24.     {  
  25.         m_log << "TestPermutationMap Start.../n";  
  26.   
  27.         DoTest1();  
  28.         DoTest2();  
  29.         DoTest3();  
  30.   
  31.         m_log << "TestPermutationMap End/n/n";  
  32.     }  
  33.   
  34. private:  
  35.     void DoTest1()  
  36.     {  
  37.         m_log << "DoTest1 Start.../n";  
  38.   
  39.         try {  
  40.             PermutationMap<bool> permMap(9, false);  
  41.   
  42.             char perm[9] = {'1''2''3''4''5''6''7''8''9'};  
  43.   
  44.             Assure(m_log, permMap.Get(perm) == false);  
  45.             Assure(m_log, permMap.Put(perm, true));  
  46.             Assure(m_log, permMap.Get(perm) == true);  
  47.             Assure(m_log, !permMap.Put(perm, true));  
  48.             int count = 1;  
  49.   
  50.             while (next_permutation(perm, perm + 9)) {  
  51.                 Assure(m_log, permMap.Get(perm) == false);  
  52.                 Assure(m_log, permMap.Put(perm, true));  
  53.                 Assure(m_log, permMap.Get(perm) == true);  
  54.                 Assure(m_log, !permMap.Put(perm, true));  
  55.                 ++count;  
  56.             }  
  57.   
  58.             Assure(m_log, count == permMap.Size());  
  59.         }  
  60.         catch (AssureException) {  
  61.         }  
  62.   
  63.         m_log << "DoTest1 End/n";  
  64.     }  
  65.   
  66.     void DoTest2()  
  67.     {  
  68.         m_log << "DoTest2 Start.../n";  
  69.   
  70.         try {  
  71.             PermutationMap<bool> permMap(9, true);  
  72.   
  73.             char perm[9] = {'1''2''3''4''5''6''7''8''9'};  
  74.   
  75.             Assure(m_log, permMap.Get(perm) == true);  
  76.             Assure(m_log, permMap.Put(perm, false));  
  77.             Assure(m_log, permMap.Get(perm) == false);  
  78.             Assure(m_log, !permMap.Put(perm, false));  
  79.             int count = 1;  
  80.   
  81.             while (next_permutation(perm, perm + 9)) {  
  82.                 Assure(m_log, permMap.Get(perm) == true);  
  83.                 Assure(m_log, permMap.Put(perm, false));  
  84.                 Assure(m_log, permMap.Get(perm) == false);  
  85.                 Assure(m_log, !permMap.Put(perm, false));  
  86.                 ++count;  
  87.             }  
  88.   
  89.             Assure(m_log, count == permMap.Size());  
  90.         }  
  91.         catch (AssureException) {  
  92.         }  
  93.   
  94.         m_log << "DoTest2 End/n";  
  95.     }  
  96.   
  97.     void DoTest3()  
  98.     {  
  99.         m_log << "DoTest3 Start.../n";  
  100.   
  101.         try {  
  102.             PermutationMap<int> permMap(9, 0);  
  103.   
  104.             char perm[9] = {'1''2''3''4''5''6''7''8''9'};  
  105.   
  106.             int count = 1;  
  107.             Assure(m_log, permMap.Get(perm) == 0);  
  108.             Assure(m_log, permMap.Put(perm, count));  
  109.             Assure(m_log, permMap.Get(perm) == count);  
  110.             Assure(m_log, !permMap.Put(perm, count));  
  111.   
  112.             while (next_permutation(perm, perm + 9)) {  
  113.                 ++count;  
  114.                 Assure(m_log, permMap.Get(perm) == 0);  
  115.                 Assure(m_log, permMap.Put(perm, count));  
  116.                 Assure(m_log, permMap.Get(perm) == count);  
  117.                 Assure(m_log, !permMap.Put(perm, count));  
  118.             }  
  119.   
  120.             Assure(m_log, count == permMap.Size());  
  121.         }  
  122.         catch (AssureException) {  
  123.         }  
  124.   
  125.         m_log << "DoTest3 End/n";  
  126.     }  
  127.   
  128.     void DoTest4()  
  129.     {  
  130.         m_log << "DoTest4 Start.../n";  
  131.   
  132.         try {  
  133.             PermutationMap<int> permMap(9, 0);  
  134.   
  135.             char data[9] = {'1''2''3''4''5''6''7''8''9'};  
  136.             vector<char> perm(data, data + 9);  
  137.   
  138.             int count = 1;  
  139.             Assure(m_log, permMap.Get(perm) == 0);  
  140.             Assure(m_log, permMap.Put(perm, count));  
  141.             Assure(m_log, permMap.Get(perm) == count);  
  142.             Assure(m_log, !permMap.Put(perm, count));  
  143.   
  144.             while (next_permutation(perm.begin(), perm.end())) {  
  145.                 ++count;  
  146.                 Assure(m_log, permMap.Get(perm) == 0);  
  147.                 Assure(m_log, permMap.Put(perm, count));  
  148.                 Assure(m_log, permMap.Get(perm) == count);  
  149.                 Assure(m_log, !permMap.Put(perm, count));  
  150.             }  
  151.   
  152.             Assure(m_log, count == permMap.Size());  
  153.         }  
  154.         catch (AssureException) {  
  155.         }  
  156.   
  157.         m_log << "DoTest4 End/n";  
  158.     }  
  159. };  
  160.   
  161. #ifdef SYMBOL_DO_TEST  
  162. UnitTest obj(std::clog);  // do test  
  163. #endif // SYMBOL_DO_TEST  
  164.   
  165. ANONYMOUS_NAMESPACE_END  

 

 

十进制数 <--> 变进制数 <--> 排列”之间的转换算法

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <iterator>  
  3. #include <vector>  
  4. #include <algorithm>  
  5. #include <cassert>  
  6.   
  7. using namespace std;  
  8.   
  9. // 把十进制数转换为变进制数,并返回变进制数的位数  
  10. // 变进制数varNumber[0]对应着变进制数的最低位  
  11. int DecimalToVariableRadix(size_t decimalNumber, vector<int> &varNumber)  
  12. {  
  13.     varNumber.clear();  
  14.   
  15.     int carry = 2;  
  16.     while (decimalNumber > 0) {  
  17.         varNumber.push_back(decimalNumber % carry);  
  18.         decimalNumber /= carry;  
  19.         ++carry;  
  20.     }  
  21.   
  22.     if (varNumber.empty())  
  23.         varNumber.push_back(0);  
  24.   
  25.     return varNumber.size();   
  26. }  
  27.   
  28. // 把十进制数转换为指定位数的变进制数(高位填充0),并返回变进制数的实际有效位数  
  29. // 如果产生的变进制数的位数比指定的位数要多,则指定位数不起作用  
  30. // 变进制数varNumber[0]对应着变进制数的最低位  
  31. int DecimalToVariableRadix(size_t decimalNumber, vector<int> &varNumber, int num)  
  32. {  
  33.     varNumber.clear();  
  34.   
  35.     int carry = 2;  
  36.     while (decimalNumber > 0) {  
  37.         varNumber.push_back(decimalNumber % carry);  
  38.         decimalNumber /= carry;  
  39.         ++carry;  
  40.     }  
  41.   
  42.     int size = varNumber.size();  
  43.     if (size < num)   
  44.         varNumber.insert(varNumber.end(), num - size, 0);  
  45.   
  46.     return size;   
  47. }  
  48.   
  49. // 把变进制数转换为十进制数  
  50. // 变进制数varNumber[0]对应着变进制数的最低位  
  51. size_t VariableRadixToDecimal(const int varNumber[], int num)  
  52. {  
  53.     size_t factor = 1;  
  54.     size_t result = 0;  
  55.       
  56.     for (int i = 0; i < num; ++i) {  
  57.         result += varNumber[i] * factor;  
  58.         factor *= i + 2;  
  59.     }  
  60.   
  61.     return result;  
  62. }  
  63.   
  64. // 把排列转换为变进制数,变进制数的高位可能会出现多个0  
  65. // 变进制数varNumber[0]对应着变进制数的最低位  
  66. template <typename ElemType>  
  67. void PermutationToVariableRadix(const ElemType permutation[], int num, vector<int> &varNumber)  
  68. {  
  69.     for (int i = 1; i < num; ++i) {  
  70.         int count = 0;  
  71.         for (int k = 0; k < i; ++k) {  
  72.             if (permutation[k] > permutation[i])  
  73.                 ++count;  
  74.         }  
  75.         varNumber.push_back(count);  
  76.     }  
  77. }  
  78.   
  79. // 把变进制数转换为排列,要求传入的排列元素集合是有序的(升序)  
  80. // 并且要求变进制数的位数(包括高位的0)刚好比排列元素少一  
  81. // 变进制数varNumber[0]对应着变进制数的最低位  
  82. template <typename ElemType>  
  83. void VariableRadixToPermutation(const int varNumber[], int num, ElemType perm[])  
  84. {  
  85.     for (int k = num - 1; k >= 0; --k) {  
  86.         // 交换当前待排子集中第(varNumber[k] + 1)大元素和它后面的子序列  
  87.         int m = k + 1;             // 当前待排子集中最后一个元素下标  
  88.         int j = m - varNumber[k];  // 当前待排子集中第(varNumber[k] + 1)大元素  
  89. #if 0  
  90.         // 实现std::rotate的功能  
  91.         ElemType tmp = perm[j];  
  92.         for (; j < m; ++j)  
  93.             perm[j] = perm[j + 1];  
  94.         perm[m] = tmp;  
  95. #else  
  96.         rotate(perm + j, perm + j + 1, perm + m + 1);  
  97. #endif   
  98.     }  
  99. }  
  100.   
  101. //  
  102. class AssureException: public std::exception  
  103. {  
  104. };  
  105.   
  106. #define Assure(os, x) (void)((!!(x)) || (ShowFailedMessage(os, #x, __FILE__, __LINE__), 0))  
  107.   
  108. inline void ShowFailedMessage(std::ostream &os, const char* expr, const char *file, size_t line)  
  109. {  
  110.     os << "Failed: " << expr << ", file /"" << file << "/", line " << line << '/n';  
  111.     throw AssureException();  
  112. }  
  113.   
  114.   
  115. void ShowUsage1()  
  116. {  
  117.     try {  
  118.         size_t num = 235;  
  119.         vector<int> varNumber;  
  120.   
  121.         DecimalToVariableRadix(num, varNumber);  
  122.         cout << "Decimal number: " << num;  
  123.         cout << "/nConverted to variable radix number (low -> high): ";  
  124.         copy(varNumber.begin(), varNumber.end(), ostream_iterator<int>(cout, " "));  
  125.   
  126.         size_t newNum = VariableRadixToDecimal(&varNumber[0], varNumber.size());  
  127.         cout << "/nConverted back to decimal number: " << newNum << '/n';  
  128.   
  129.         Assure(cout, num == newNum);  
  130.         cout << endl;  
  131.     }  
  132.     catch (AssureException) {  
  133.     }  
  134. }  
  135.   
  136. void ShowUsage2()  
  137. {  
  138.     try {  
  139.         char perm[] = {'d''e''a''b''f''c''g'};  
  140.         const int NUM = sizeof(perm) / sizeof(perm[0]);  
  141.         vector<int> varNumber;  
  142.   
  143.         PermutationToVariableRadix(perm, NUM, varNumber);  
  144.         cout << "Permutation: ";  
  145.         copy(perm, perm + NUM, ostream_iterator<char>(cout));  
  146.         cout << "/nConverted to variable radix number (low -> high): ";  
  147.         copy(varNumber.begin(), varNumber.end(), ostream_iterator<int>(cout, " "));  
  148.   
  149.         char newPerm[NUM] = {'a''b''c''d''e''f''g'};  
  150.         VariableRadixToPermutation(&varNumber[0], varNumber.size(), newPerm);  
  151.         cout << "/nConverted back to permutation: ";  
  152.         copy(newPerm, newPerm + NUM, ostream_iterator<char>(cout));  
  153.         cout << '/n';  
  154.   
  155.         Assure(cout, equal(perm, perm + NUM, newPerm));  
  156.         cout << endl;  
  157.     }  
  158.     catch (AssureException) {  
  159.     }  
  160. }  
  161.   
  162. void Test()  
  163. {  
  164.     try {  
  165.         cout << "testing /"permutation -> variable radix -> decimal -> "  
  166.             "variable radix -> permutation/"...";  
  167.   
  168.         const int NUM = 9;  
  169.         char perm[NUM] = {'1''2''3''4''5''6''7''8''9'};  
  170.   
  171.         do {  
  172.             // permutation will be  converted to variable radix number  
  173.             vector<int> varNumber;  
  174.             PermutationToVariableRadix(perm, NUM, varNumber);  
  175.   
  176.             // variable radix number will be converted to decimal number  
  177.             size_t decimalNumber = VariableRadixToDecimal(&varNumber[0], varNumber.size());  
  178.   
  179.             // decimal number will be converted back to variable radix number  
  180.             vector<int> newVarNumber;  
  181.             DecimalToVariableRadix(decimalNumber, newVarNumber, NUM - 1);  
  182.   
  183.             // variable radix number will be converted back to permutation  
  184.             char newPerm[NUM] = {'1''2''3''4''5''6''7''8''9'};  
  185.             VariableRadixToPermutation(&newVarNumber[0], newVarNumber.size(), newPerm);  
  186.   
  187.             Assure(cout, equal(varNumber.begin(), varNumber.end(), newVarNumber.begin()));  
  188.             Assure(cout, equal(perm, perm + NUM, newPerm));  
  189.         } while (next_permutation(perm, perm + NUM));  
  190.   
  191.         cout << "done. Ok!" << endl;  
  192.     }  
  193.     catch (AssureException) {  
  194.     }  
  195. }  
  196.   
  197. int main()  
  198. {  
  199.     ShowUsage1();  
  200.     ShowUsage2();  
  201.     Test();  
  202. }  

 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值