【算法】基数排序应用于浮点数 (Radix sort deal with float number)

这次的se106的homework有一题对浮点数使用基数排序,做完之后感觉比较有趣。

 

很多人觉得对浮点数使用基数排序,就是从小数点后面多少位开始使用10进制的方式一位一位排。

但这是计算机保存浮点数,并不是数学上的小数,个人认为这样考虑不妥。应该遵循计算机保存浮点数的格式来排序。

使用 IEEE 的 float 为例: 

1.算法思路

  对于 IEEE 的 float 类型:

  

  最高位31位是符号位,30~23位是阶码位,22~0位是尾数位。

  IEEE的 float 类型有一个特性,就是从0到30位(除了符号位)对数字的大小的重要性依次增大,这样就可以和整数各位的排序一样了。

  有一个例外问题就是符号位导致正负数的排序出现的问题,例如对于一个4位的 IEEE 方式编码的浮点数,最高位是符号位,后面3位为尾数位。

  用传统的基数排序将会得到如下结果:

 0  :  0000
 1  :  0001
 2  :  0010
 4  :  0100
 -1  :  1001
 -2  :  1010
 -7  :  1111  

  产生的问题是 先是正数从小到大排,再是负数从大到小排。

  为了解决这个问题,可以做两种处理:

 1. 将排列之后的序列的正数倒置,就形成了从大到小的序列。

 2. 先将正数做一个处理,符号位从0置成1,再将负数做一个处理,全部取反,排列完后再恢复。

  我们使用第2种方法(因为感觉更有趣)。

 

2.代码实现

 

void radix_sort(float input[], int length)
{
    // positive: sign bit set 0 to 1
    // negative: all bit trans
    // 32bits split into 4 bytes, sort 1 byte 1 time
    for (int i=0; i<length; i++)
        reinterpret_cast<int&>(input[i]) = (reinterpret_cast<int&>(input[i])>>31 & 0x1)? ~reinterpret_cast<int&>(input[i]) : reinterpret_cast<int&>(input[i]) | 0x80000000;
    vector<float> bucket[256];
    for (int i=0; i<4; i++) {
        for (int j=0; j<length; j++)
            bucket[reinterpret_cast<int&>(input[j])>>(i*8) & 0xff].push_back(input[j]);
        int count = 0;
        for (int j=0; j<256; j++) {
            for (int k=0; k<bucket[j].size(); k++)
                input[count++] = bucket[j][k];
            bucket[j].clear();
        }
    }
    // after sort, recover
    for (int i=0; i<length; i++)
        reinterpret_cast<int&>(input[i]) = (reinterpret_cast<int&>(input[i])>>31 & 0x1)? reinterpret_cast<int&>(input[i]) & 0x7fffffff : ~reinterpret_cast<int&>(input[i]);
}

 

 

 

  第3行将输入的浮点数做如上处理。之后做传统的基数排序。

  以一个byte为单元,产生256个不同的key,对于4个byte的 float,一共循环4次。

  第22行做变换会原本数位的处理。

 

  这样,就使用位来对浮点数进行了基数排序。

posted @ 2013-10-30 15:40  Azard  阅读(2887)  评论(2编辑  收藏  举报