3 个版本 (重大更改)

使用旧 Rust 2015

0.3.0 2016年8月22日
0.2.0 2016年8月18日
0.1.0 2016年8月12日

#557模板引擎

每月 26 次下载
用于 crate-race

MIT 许可证

26KB
520

rdxsort-rs

Crates.io License Build Status Coverage Status

Rust 中的快速基数排序

版权 (c) 2016 Marco Neumann


lib.rs:

RdxSort

这个 crate 实现了对不同数据类型的切片进行基数排序,可以直接或通过利用其他数据类型的实现。

实现另一种排序算法的主要原因是因为大多数排序算法都是比较方法。为了排序数据,它们依赖于一个比较数据元素的函数。可以证明这会导致平均时间复杂度为 O(n*log(n)),在最坏情况下为 O(n^2)。相比之下,基数排序利用了这样一个事实,即许多数据类型有一个可能值的有限范围,并且在这个范围内有一个有限的分辨率(这也适用于浮点数)。关于详细解释,请参阅维基百科文章。这种特殊处理的结果是降低了并且是恒定的复杂度 O(n*k),其中 k 是对特定数据类型进行排序所需的固定轮数。

支持的数据类型

目前支持以下数据类型

  • bool: 简单分为 2 部分
  • char: 类似于 u32
  • 无符号整数:原生实现,取决于宽度
  • 有符号整数:将其拆分为正负两部分并使用无符号实现
  • 浮点数:将数据拆分为 -(-,-0)-0+0(+0,+)+,并将这两个范围视为无符号整数值。不支持 非规格数NaN
  • 数组,元组:使用内部数据类型的实现
  • 自定义数据类型...:填写提供的模板特质

示例

use rdxsort::*;

fn main() {
    let mut data = vec![2, 10, 0, 1];
    data.rdxsort();
    assert!(data == vec![0, 1, 2, 10]);
}

性能

当然,基数排序较低的运行时间复杂度在排序特定数据类型时显示出其力量。优势取决于类型的大小和复杂性。虽然短的无符号整数受益最大,但长类型并不显示出巨大的改进。以下列表显示了不同大小数据集排序所需的运行时间(ns)。数据集使用均匀分布进行采样。以下标记了最佳的算法

请注意,结果可能因硬件、编译器版本、操作系统版本和配置以及天气而异。

小型(1,000个元素)

对于小型数据集,基数排序可以是无符号整数大小为32位的数据类型的优势。对于64位,应首选标准库排序。

类型 快速排序 rdxsort std
bool 4,070 2,246 26,068
char 31,121 20,204 34,051
f32 79,714 25,825 77,774
f64 80,954 52,262 79,431
i16 32,896 12,496 31,167
i32 32,854 22,009 30,713
i64 33,064 53,366 31,669
i8 24,819 8,190 46,281
u16 35,252 9,937 33,946
u32 33,002 19,202 33,627
u64 32,986 47,739 33,204
u8 25,425 5,170 47,369

中型(10,000个元素)

对于中型数据集,由于64位类型的劣势相当小,因此可以盲目地使用基数排序。

类型 快速排序 rdxsort std
bool 52,211 22,083 400,111
char 655,553 192,328 508,557
f32 1,086,882 230,670 1,117,565
f64 1,095,529 417,104 1,152,340
i16 665,131 108,128 455,047
i32 650,501 202,533 460,097
i64 669,643 378,480 470,572
i8 383,545 65,521 617,405
u16 675,060 78,424 508,264
u32 670,348 177,068 511,134
u64 664,549 342,935 517,176
u8 386,572 37,012 655,377

大型(100,000个元素)

对于大型数据集,基数排序对所有数据类型都是很好的。

类型 快速排序 rdxsort std
bool 815,653 216,809 4,900,426
char 8,131,402 2,538,064 6,333,865
f32 13,554,291 3,264,657 14,340,082
f64 13,746,190 7,122,334 15,563,206
i16 8,235,642 1,248,289 5,575,196
i32 8,184,902 2,494,882 5,852,913
i64 8,222,482 5,446,507 6,561,292
i8 3,664,532 703,288 7,118,816
u16 8,272,903 866,833 6,291,101
u32 8,193,408 2,051,413 6,395,163
u64 8,179,078 4,393,579 7,216,868
u8 3,681,361 367,240 7,816,775

实现新类型

此软件包通过实现 RdxSortTemplate 允许您添加对新类型的支持。它描述了数据如何排序到桶中以及安排了多少轮排序。

use rdxsort::*;

// `Clone` is required for `RdxSort`
// `PartialEq` is only required for the equality assert, not for the actual sorting
#[derive(Clone, PartialEq)]
struct Foo {
    a: u8,
    b: u8,
}

impl RdxSortTemplate for Foo {
    // using `#[inline]` is generally recommended since it helps
    // the compiler to optimize the sorting algorithm
    #[inline]
    fn cfg_nbuckets() -> usize {
        // usually too high, but works as a simple demonstration
        // `256 = 2^8`
        256
    }

    #[inline]
    fn cfg_nrounds() -> usize {
        // one per sub-type
        2
    }

    #[inline]
    fn get_bucket(&self, round: usize) -> usize {
        // return the least significant digit first
        if round == 0 {
            self.b as usize
        } else {
            self.a as usize
        }
    }

    #[inline]
    fn reverse(_round: usize, _bucket: usize) -> bool {
        // not required in our case
        false
    }
}

fn main() {
    let mut data = vec![
        Foo{a: 5, b: 2},
        Foo{a: 0, b: 1},
        Foo{a: 5, b: 1},
        Foo{a: 0, b: 2},
    ];
    data.rdxsort();

    let reference = vec![
        Foo{a: 0, b: 1},
        Foo{a: 0, b: 2},
        Foo{a: 5, b: 1},
        Foo{a: 5, b: 2},
    ];
    assert!(data == reference);
}

无运行时依赖