【算法】基数排序应用于浮点数 (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行做变换会原本数位的处理。
这样,就使用位来对浮点数进行了基数排序。