zoukankan      html  css  js  c++  java
  • 位图排序思想及代码详解

    输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7。如果在输入文件中有任何重复整数出现就是致命错误。没有其他数据与该整数相关联

    输出:按升序排列的 输入整数的列表。

    约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。

        

        从0开始解决这个问题的话,可以把它分为两个部分:首先随机生成K个小于N的数;再对这K个数排列。

        

        系统参数:GCC:#pragma pack(4)    小端模式 -std=c99

    一、位图思想(bit-map)

        引入:如何用一个Byte表示尽可能多的无重复的数?

        一个bit只能表示0或1,两个bit能表示2(01)和3(11),4则要3个bit,这样的话8个bit只能表示0-3四个数字。

        我们得换个思路,暂且先丢掉头脑中的进制,用小学生排队的思想,拍第一的报1,第二的报2…嗯哼,我们一字节8个bit,这样就表示了8个数字。

        所以,让我们走进内存中,丢掉进制的羁绊,来数数这个bit排第几。

        这张图以左边为低位的话,就和作者系统的小端模式契合

    二、生成K个小于N的不重复随机数

        基于位图的思想,我们可以用K个bit来表示这么多个数字。由于存在内存对齐等问题,我声明了一个32Bit的union结构,为了更好的对这32个bit进行操作,我把它分为了4个char,而两个short则是为了测试与debug。

    1. typedef union str{  
    2.         short element[2];  
    3.         char ele_ch[4];  
    4.     }Bits;  
    5.     
    6.     int n = 0;  
    7.     printf("Please input range:\n");  
    8.     scanf("%d",&n);   
    9.     const int N = n/32+1;  
    10.     Bits arr[N];  
    11.         
    12.     memset(arr,0,sizeof(arr));  
    13.         
    14.     int k = 0;  
    15.         
    16.     printf("Please input count of number,");  
    17.     do{  
    18.         printf("this number is less than %d\n",n);  
    19.         scanf("%d",&k);  
    20.     }while(k>n);  

    所以,当生成了一个随机数num后,就把内存中第num位置为1,以表示该数已存在,同理,检查是否有重复时,只需看该位的值。

    对检查该位的值以及修改该位的值这两个操作,用两个函数表示。

    1. /*      查看该位是否为0        * 
    2. *       输入:一个整数n        * 
    3. *       返回:bit[n]的值         */  
    4. bool bitValue(int num,Bits arr[],...);  
    5.  
    6.  
    7. /*      把某位设为n                  * 
    8. *       输入:一个整数n代表的地址       * 
    9. *       操作:将该地址上的值设为1       */  
    10. void setBitValue(Bits arr[],...); 

    所以我们可以用这样一个循环来寻找随机数。用rand得到随机数后,再判断这个数是否出现过,如果没出现过的话就把那一位设为1,并输出。

    1. while(k)  
    2. {  
    3.     int value = rand()%n;  
    4.     if(bitValue(value,arr)==0)  
    5.     {  
    6.         setBitValue(arr,n);  
    7.         printf(" %d",value);  
    8.         k--;  
    9.     }  
    10. }  

    由于申请的内存是以4B(32bit)为一个单位,所以N个数字在内存中有如下x种情况:

            1.N为32 的倍数,则申请到的数组全满。(N = 32*n)

            2.除最后一个数组外,其余数组全满。也分两种情况:

                ①.最后一个数组中,前x个char全满,后一个char被用到的bit小于8个。(N = n*32+8*x+bit)

                ②. 最后一个数组中,恰好满了x个char。(N = n*32+8*x)

        所以bool bitValue(int num ,Bits arr[])接受两个参数,一个是数组首地址,一个是待求的数字(0~n-1)

    1. bool bitValue(int num,Bits arr[])  
    2. {     
    3.     if(num!=0)  
    4.     {  
    5.         int arr_n = num/(32);   //在第几个数组   
    6.         int arr_b = num-arr_n*32;  
    7.         short arr_ch = 0;   //在第几个char   
    8.         if(arr_b)  
    9.         {  
    10.             arr_ch = arr_b/8;  
    11.         }  
    12.             
    13.         int arr_c_b = arr_b;    //第几个bit   
    14.         while(arr_c_b>=8)  
    15.         {  
    16.             arr_c_b -= 8;  
    17.         }  
    18.                 
    19.         return (arr[arr_n].ele_ch[arr_ch] & arr2n[arr_c_b]);  
    20.     }  
    21.     else    //处理  
    22.     {  
    23.         return (arr[num].ele_ch[0] & arr2n[0]);   
    24.     }  
    25.     
    26. }

      又因为是以8bit为一单位,所以创建数组全局数组 arr2n[8],用来存储2^0~2^7的值。在小端模式下,恰好是左第一位为2^0。

    27. const int arr2n[8]={1,2,4,8,16,32,64,128};  

      所以void setBitValue()也和bool bitValue()过程类似,找到n在内存中的位置,在更新它的值。不过在这个函数里重复计算位置不划算,所以我们可以在第一次计算时把位置信息保存起来,要更新数据时再传给函数。

      这里申明一个位置结构,用来存储一个数字在内存中相对于数组结构的位置。

    28. typedef struct bitLoca{  
    29.     int n_arr;  
    30.     int n_ch;  
    31.     int n_bit;  
    32. }bitLoca;  

    然后在在主函数的while循环里加上它,并修改两个函数的参数:

    1. int main(void)  
    2. {  
    3.     //...  
    4.     
    5.     while(key)  
    6.         {  
    7.             bitLoca bitL;  
    8.             int value = rand()%n;  
    9.             if(bitValue(value,arr,&bitL)==0)  
    10.             {  
    11.                 setBitValue(arr,&bitL);  
    12.                 printf("%d\n",value);  
    13.                 key--;  
    14.             }  
    15.         }  
    16.         
    17.     //...   
    18. }  

      更新后的bitValue():

    19. bool bitValue(int num,Bits arr[],bitLoca *bitL)  
    20. {     
    21.     if(num!=0)  
    22.     {  
    23.         int arr_n = num/(32);  
    24.         int arr_b = num-arr_n*32;  
    25.         short arr_ch = 0;   //在第几个char   
    26.         if(arr_b)  
    27.         {  
    28.             arr_ch = arr_b/8;  
    29.         }  
    30.             
    31.         int arr_c_b = arr_b;  
    32.         while(arr_c_b>=8)  
    33.         {  
    34.             arr_c_b -= 8;  
    35.         }  
    36.             
    37.         bitL->n_arr = arr_n;  
    38.         bitL->n_ch = arr_ch;  
    39.         bitL->n_bit = arr_c_b;  
    40.             
    41.         return (arr[arr_n].ele_ch[arr_ch] & arr2n[arr_c_b]);  
    42.     }  
    43.     else    //处理  
    44.     {  
    45.         bitL->n_arr = 0;  
    46.         bitL->n_ch = 0;  
    47.         bitL->n_bit = 0;  
    48.         return (arr[num].ele_ch[0] & arr2n[0]);   
    49.     }  
    50. }  

      这样的话setBitValue()也就简单多了:

    51. /*      把某位设为n                      * 
    52. *       输入:一个整数n代表的地址       * 
    53. *       操作:将该地址上的值设为1       */  
    54. void setBitValue(Bits arr[],bitLoca *bitL)  
    55. {  
    56.     arr[bitL->n_arr].ele_ch[bitL->n_ch] = arr[bitL->n_arr].ele_ch[bitL->n_ch] | arr2n[bitL->n_bit];  
    57. }  

      写到这里,输出不重复的随机数部分就能跑了,不过有一个小问题,有关rand()函数的。

    58. int __cdecl rand(void);  

      rand()函数产生一个0到RAND_MAX的伪随机数,而在楼主的系统里,RAND_MAX是32767,就是2^15。所以题目要求的k<1000000就死活打不成了,为了解决这个问题,我不假思索的写了这样一条语句:

    59. int value = rand()*rand()%n;  

      再跑一次,ok,解决了…(\简单粗暴)

    运行一下。

        ok,第一部分——找随机数解决。

    三、排序

        其实到了现在这一步,这已经不适合叫排序了,倒更像是遍历。从0开始,把申请到的内存全过一遍,值为1 就输出。这一步只需要注意边界的问题,用for循环就可以搞定。

        不过在这个地方我还是用的简单粗暴(不动脑子)的办法,先求出最后一个数字所在的位置(arr\ch\bit),再分情况依次遍历,所以写的又长又臭。

    1. void bitSort(Bits arr[],int n)  
    2. {  
    3.     int max_arr = n/32;  
    4.     int tbit = n-max_arr*32;  
    5.     short max_ch = 0;   //在第几个char   
    6.     if(tbit)  
    7.     {  
    8.         max_ch = tbit/8;  
    9.     }  
    10.     int max_bit = tbit%8;  
    11.     
    12.     for(int tarr = 0;tarr<=max_arr;tarr++)  
    13.         if(tarr != max_arr) //全满的数组   
    14.         {  
    15.             for(int tch = 0;tch<4;tch++)  
    16.                 {  
    17.                     for(int tbit = 0;tbit<8;tbit++)  
    18.                     {  
    19.                         if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    20.                         {  
    21.                             printf("%d ",tarr*32+tch*8+tbit);  
    22.                         }  
    23.                     }  
    24.                 }     
    25.         }  
    26.         else    //非全满的数组   
    27.         {  
    28.             for(int tch = 0;tch<=max_ch;tch++)  
    29.                 if(tch != max_ch)   //全满的ch   
    30.                 {  
    31.                     for(int tbit = 0;tbit<8;tbit++)  
    32.                     {  
    33.                         printf("tarr = %d ,tch = %d ,tbit = %d : \n",tarr,tch,tbit);  
    34.                         if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    35.                         {  
    36.                             printf("%d ",tarr*32+tch*8+tbit);  
    37.                         }  
    38.                     }  
    39.                 }  
    40.                 else    //非全满ch   
    41.                 {  
    42.                     for(int tbit = 0;tbit<max_bit;tbit++)  
    43.                     {  
    44.                         if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    45.                         {  
    46.                             printf("%d ",tarr*32+tch*8+tbit);  
    47.                         }  
    48.                     }  
    49.                 }  
    50.         }  
    51. }  

      再运行一遍:

    四、下一步

        仓促之下完成的代码,不可避免的留下了许多可以提升的空间,以下几点是重构、优化时必须要考虑的问题。

        

        1.随机数分布不均的问题:rand()*rand()简单粗暴,不过从概率的角度上讲只能称之为无脑。

        2.遍历的优化。

        3.代码的封装:寻找一个数的地址的功能可以封装成一个函数。

        4.位运算可以用位移操作代替。

    五、代码

    1. /* 
    2.     163_union_1_sort.c 
    3.     author:Magic激流 
    4.     2018-5-26  
    5. */  
    6. #include <stdio.h>  
    7. #include <stdlib.h>  
    8. #include <time.h>  
    9. #include <windows.h>  
    10. #include <stdbool.h>  
    11.     
    12. typedef union str{  
    13.     short element[2];  
    14.     char ele_ch[4];  
    15. }Bits;  
    16.     
    17. typedef struct bitLoca{  
    18.     int n_arr;  
    19.     int n_ch;  
    20.     int n_bit;  
    21. }bitLoca;  
    22.     
    23. int arr2n[8]={1,2,4,8,16,32,64,128};  
    24.     
    25. /*      查看该位是否为0        * 
    26. *       输入:一个整数n        * 
    27. *       返回:bit[n]的值 */  
    28. bool bitValue(int num,Bits arr[],bitLoca *bitL)  
    29. {     
    30.     if(num!=0)  
    31.     {  
    32.         int arr_n = num/32;  
    33.         int arr_b = num-arr_n*32;  
    34.         short arr_ch = 0;   //在第几个char   
    35.         if(arr_b)  
    36.         {  
    37.             arr_ch = arr_b/8;  
    38.         }  
    39.             
    40.         int arr_c_b = arr_b;  
    41.         while(arr_c_b>=8)  
    42.         {  
    43.             arr_c_b -= 8;  
    44.         }  
    45.             
    46.         bitL->n_arr = arr_n;  
    47.         bitL->n_ch = arr_ch;  
    48.         bitL->n_bit = arr_c_b;  
    49.             
    50.         return (arr[arr_n].ele_ch[arr_ch] & arr2n[arr_c_b]);  
    51.     }  
    52.     else    //处理  
    53.     {  
    54.         bitL->n_arr = 0;  
    55.         bitL->n_ch = 0;  
    56.         bitL->n_bit = 0;  
    57.             
    58.         return (arr[num].ele_ch[0] & arr2n[0]);   
    59.     }  
    60. }  
    61.     
    62. /*      把某位设为n                      * 
    63. *       输入:一个整数n代表的地址       * 
    64. *       操作:将该地址上的值设为1       */  
    65. void setBitValue(Bits arr[],bitLoca *bitL)  
    66. {  
    67.     arr[bitL->n_arr].ele_ch[bitL->n_ch] = arr[bitL->n_arr].ele_ch[bitL->n_ch] | arr2n[bitL->n_bit];  
    68. }  
    69.     
    70. void bitSort(Bits arr[],int n)  
    71. {  
    72.     int max_arr = n/32;  
    73.     int tbit = n-max_arr*32;  
    74.     short max_ch = 0;   //在第几个char   
    75.     if(tbit)  
    76.     {  
    77.         max_ch = tbit/8;  
    78.     }  
    79.     int max_bit = tbit%8;  
    80.     
    81.     for(int tarr = 0;tarr<=max_arr;tarr++)  
    82.         if(tarr != max_arr) //全满的数组   
    83.         {  
    84.             for(int tch = 0;tch<4;tch++)  
    85.                 for(int tbit = 0;tbit<8;tbit++)  
    86.                 {  
    87.                     if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    88.                     {  
    89.                         printf("%d ",tarr*32+tch*8+tbit);  
    90.                     }  
    91.                 }  
    92.         }  
    93.         else    //非全满的数组   
    94.         {  
    95.             for(int tch = 0;tch<=max_ch;tch++)  
    96.                 if(tch != max_ch)   //全满的ch   
    97.                 {  
    98.                     for(int tbit = 0;tbit<8;tbit++)  
    99.                     {  
    100.                         if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    101.                         {  
    102.                             printf("%d ",tarr*32+tch*8+tbit);  
    103.                         }  
    104.                     }  
    105.                 }  
    106.                 else    //非全满ch   
    107.                 {  
    108.                     for(int tbit = 0;tbit<max_bit;tbit++)  
    109.                     {  
    110.                         if(arr[tarr].ele_ch[tch] & arr2n[tbit])  
    111.                         {  
    112.                             printf("%d ",tarr*32+tch*8+tbit);  
    113.                         }  
    114.                     }  
    115.                 }  
    116.         }  
    117. }  
    118.     
    119. int main (void)  
    120. {  
    121.     int n = 0;  
    122.     printf("Please input range:\n");  
    123.     scanf("%d",&n);   
    124.     const int N = n/32+1;  
    125.     Bits arr[N];  
    126.         
    127.     memset(arr,0,sizeof(arr));  
    128.         
    129.     int k = 0;  
    130.         
    131.     printf("Please input count of number,");  
    132.     do{  
    133.         printf("this number is less than %d\n",n);  
    134.         scanf("%d",&k);  
    135.     }while(k>n);  
    136.     
    137.     srand(time(NULL));  
    138.         
    139.     while(k)  
    140.     {  
    141.         bitLoca bitL;  
    142.         int value = rand()*rand()%n;  
    143. //      int value = rand()%n;  
    144.         if(bitValue(value,arr,&bitL)==0)  
    145.         {  
    146.             setBitValue(arr,&bitL);  
    147.             printf("%d ",value);  
    148.             k--;  
    149.         }  
    150.     }  
    151.         
    152.     printf("\nSort:\n");  
    153.         
    154.     bitSort(arr,n);  
    155.         
    156.     return 0;  
    157. }  
  • 相关阅读:
    mycat1.6.6.1读写分离-分库分表-keepalived高可用-mysql主从
    水火一一
    CentOS7.3虚拟机vmware双网卡配置
    GoldenGate的安全配置
    GoldenGate实时投递数据到大数据平台(1)-MongoDB
    GoldenGate实时投递数据到大数据平台(7)– Apache Hbase
    GoldenGate实时投递数据到大数据平台(5)
    GoldenGate实时投递数据到大数据平台(4)- ElasticSearch 2.x
    明史资料010---- (明初功臣表)明朝建国前后功劳最大的一百五十位功臣名单以及他们的简历
    转载oracle ogg--ogg搭建过程中遇到的错误及处理
  • 原文地址:https://www.cnblogs.com/magicxyx/p/9092701.html
Copyright © 2011-2022 走看看