品鉴一个类Radix排序算法的内存占用

本来我是想重新编辑《一个借鉴现代OS的MMU的排序算法》这篇文章的,但想来新的主题还是另起一篇为好。

  首先要说明的是,上文中的那个算法的代码是有缺陷的,比如如果有重复的数字,那么该实现将会冲掉重复的数据,正确的做法应该是在256叉树的叶子节点维护一个链表,重复的数字全部链接到该链表中。但由于我不是很会编程,不晓得一些现成的数据结构所在的库是怎么一种用法,实现这个可能需要重新做起,就算了,大致知道个意思就好了。

  另外,更加重要的是,这个算法其实是在执行一种插入排序,和标准的插入排序不同的是,该算法会一次性把一个数字插入到它应该在的位置,时间复杂度为 O(1) ,如果仅就这个来讲,这应该是一个最好的排序算法,然而它却名不见经传。
因为,这个算法可能会耗费大量的内存!


为了验证这个,我把源代码修改一下,加入一个计数器:

unsigned int count = 0;

然后在每一次申请内存的时候该计数器递增:

...(struct entry*)calloc(1, 256*sizeof(struct entry));
count ++;

我们看下如果是随机分布的待排序数组,count值如何变化。为此,再修改代码:

int n = atoi(argv[1]);
...
srandom(time(NULL));
for (i = 0; i < n; i++) {
    int v = random()&0xffffffff;
    insert(v);
}
...
printf("%d  %d\n", n, count);

虽然,我也知道srandom设置的种子是基于时间的,以至于后面的random函数依然不是真正的随机数,但是这足以说明问题了。我们并不是真正期待一个完全的随机分布的数组序列,而是足够随机就好了。在这个情况下,我写下如下的脚本:

for ((i=0;i<500001;i=i+100));do ./a.out $i >>./result;done

然后用gnuplot画出图像(到500001太慢,我只取到300000):

这里写图片描述

可以看到有一个上凸趋势,但并不明显,总体看来内存占用还是 O(n) 的,你可以看到这确实俨然一条直线。

   O(n) 的空间复杂度,并不是很糟糕,但却也并不是很美好。我在上一篇正文里说过,在待排序数组是密集分布而不是稀疏分布的时候,算法表现会更好一些。理论上确实如此,但如何来验证呢?

  为此,我们使用一种极端的方案,让待排序数组集中分布,修改代码如下:

int n = atoi(argv[1]);
...
for (i = 0; i < n; i++) {
    insert(i);
}
...
printf("%d  %d\n", n, count);

直接把连续递增的 i 插入到设施中,此时依然使用下面的脚本:

for ((i=0;i<500001;i=i+100));do ./a.out $i >>./result;done

然后同样画出曲线(我依然只取到300000:):

这里写图片描述

可以看到依然是一条直线,然而却缓了很多:

这里写图片描述


也许你仅仅从O(n)看不出二者的区别,但是从细节上,你会发现这条直线还有个参数,这正是很多人所忽略的。参数由什么决定?参数由约束决定!

  在本文叙述的场景中,如果待排序的数据分布很密集,那么显然这个算法是很好的选择,如果不是,那就只能自行权衡了。

  同样的道理,快速排序非常快并不仅仅因为它叫做快速排序,和它的时间复杂度一样的算法多得是,但在统计意义上就是没它优秀,所以大O并不能代表算法的优劣!


昨天发烧,但今天就奇迹般好了,没有吃任何药物,一度怀疑被感染了流感,或者是因为这么冷的天穿短袖冻感冒了,但马上就发现不是那回事,差点就被他们忽悠了,生活在众人之间,就要被众人的言论指使,然而我偏不,事后证明我是对的…不管怎样吧,不要听别人胡说,有可能95%的人说的都是错了,他们一个又一个都是听了别人错误的结论而已。要自己亲自试一下才知道。我自己可以在冬天穿短袖,所以我就知道即便我发烧,也一定不是因为被冻到了,随便别人怎么说,在我看来都是胡说。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值