浮点数-基数排序

浮点数-基数排序        

        这段时间在研究海量浮点数外部排序时,研究了内部排序的一些算法,一直也没往基数排序上想。这两天恰好看到了RadixSortRevisited这篇文章,感触很深,便想尝试复现下浮点数基数排序算法。

        传统的基数排序的原理就是将整数按位进行切分,然后按每个位数分别进行比较。该方法网上资料很多,这里就不做一一赘述。

        根据IEEE754标准,double类型的数据在存储中会被划分为1、11、52三个段。1代表数符,11代表阶码,52代表尾数。具体的标准可查阅相关资料。

        算法的第一步就是要理解将double类型的数据强制转化成无符号长整型进行基数排序。由于double类型的数据占64位,所以我们可以考虑将这64位进行划分,比如划分成4个部分,每个部分16位,即桶的大小为65536。可类比十进制基数排序,十进制基数排序每一位桶的大小为10,而将浮点数如此划分之后,每个部分桶的大小为65536。

        算法的第二步就是理解将这些浮点数强制转化后进行基数排序,最后得到的结果如何。可以发现,正数的数符位总为0,负数的数符位总为1,所以最后排序后的结果:正数小-正数大-负数大-负数小

        算法的第三步就是要调整排序后的顺序,即将排序后的结果更正为负数小-负数大-正数小-正数大。这一步就需要我们在之前就统计好负数或者正数的个数,在此统计负数个数会更加方便。

        粗略描述了下算法的步骤后,代码如下:

// 浮点数在计算机中的存储方式:https://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
// https://www.boatsky.com/blog/26
// http://www.codercorner.com/RadixSortRevisited.htm
// 2.5亿个数尝试划分成8份、4份,划分成4份的效果最好,但两种都比C++内置sort好
void in_sort(double *buffer, long long n, int sort_seq_num) {
  // 一共64位,若按每两个字节(16位)对其进行排序,类比十进制的基数排序,所以分成了4份,此时
  // sort_seq_num = 4
  // 每16位有一个bucket数组,数组大小为65536,bucket数组进行计数
  int bit_num = 64 / sort_seq_num;
  int *bucket = new int[1 << (bit_num)];
  // 临时存储
  auto *temp = (double *)malloc((n + 100) * sizeof(double));
  long long negative_num = 0; //记录负数的个数
  // 将数转化成无符号长整型对其进行基数排序
  for (int i = 0; i < sort_seq_num; i++) {
    for (int j = 0; j < (1 << bit_num); ++j) {
      bucket[j] = 0;
    }
    for (long long j = 0; j < n; j++) {
      if (i == 0 && buffer[j] < 0) {
        negative_num++;
      }
      int idx;
      unsigned long long val;
      val = reinterpret_cast<unsigned long long &>(buffer[j]);
      idx = val >> i * bit_num & ((1 << bit_num) - 1); //每16位提取一次
      bucket[idx]++;
    }
    //求出基数桶的边界索引
    for (int j = 1; j < (1 << bit_num); j++) {
      bucket[j] = bucket[j - 1] + bucket[j];
    }
    //从右向左扫描,保证排序稳定性,记录进temp中
    for (long long j = n - 1; j >= 0; j--) {
      int idx;
      unsigned long long val;
      val = reinterpret_cast<unsigned long long &>(buffer[j]);
      idx = val >> i * bit_num & ((1 << bit_num) - 1);
      temp[--bucket[idx]] = buffer[j];
    }
    for (long long j = 0; j < n; j++) {
      buffer[j] = temp[j];
    }
  }
  // 处理负数
  for (long long i = 0; i < n; i++) {
    if (temp[i] < 0.0) {
      buffer[n - 1 - i] = temp[i];
    } else {
      buffer[negative_num + i] = temp[i];
    }
  }
  free(temp);
  delete[] bucket;
}

        其中sort_seq_num代表的就是想把64位长整型划分为多少个部分,即算法的第二步需要比较多少次。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值