免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: yulihua49
打印 上一主题 下一主题

[C] 通用可重复值的排序数组的二分法检索 [复制链接]

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
21 [报告]
发表于 2016-11-02 20:55 |只看该作者
本帖最后由 yulihua49 于 2016-11-02 21:00 编辑
mr_sev 发表于 2016-11-02 17:38
不是有个bsearch?    你咋不把glibc重写一遍?

你根本没有仔细看。bsearch,支持可重复值吗?支持不等式查找吗?
类似这样的:
1,1,1,2,2,4,4,4,5,6,6,7,8,9,9,9,,,,,,
为key定上下界,避免线性查找。
脑子里能不能多考虑下实际需求。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
22 [报告]
发表于 2016-11-02 21:05 |只看该作者
windoze 发表于 2016-11-01 22:04
std::binary_search不就行了,干吗要重新造轮子?
mr_sev 发表于 2016-11-02 17:38
不是有个bsearch?    你咋不把glibc重写一遍?

你根本没有仔细看。bsearch,支持可重复值吗?支持不等式查找吗?
类似这样的:
1,1,1,2,2,4,4,4,5,6,6,7,8,9,9,9,,,,,,
为key定上下界,避免线性查找。
你不应该问这问题啊,老鸟了,这需求应该见过啊。能满足需求的就是multimap,太慢。

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
23 [报告]
发表于 2016-11-02 22:28 |只看该作者
本帖最后由 windoze 于 2016-11-02 22:30 编辑

回复 22# yulihua49

STL里就有upper_bound和lower_bound,和你写的这两个函数的功能一模一样,速度还要快一点……
比如你要在一个排好序的数组里找小于等于n的最大的数字:

  1. std::vector<int> seq{1,1,1,2,2,4,4,4,5,6,6,7,8,9,9,9};
  2. int n=6;
  3. auto i=std::upper_bound(seq.begin(), seq.end(), n);
  4. // 现在i指向7
  5. if(i==seq.begin()) {
  6.     // 没找到,整个seq都比n大
  7. } else {
  8.     i--;
  9.     // 现在i就指向第二个6
  10. }
复制代码

时间复杂度一样是O(log(N)),lower_bound类似,不过是反过来的。

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
24 [报告]
发表于 2016-11-02 23:10 |只看该作者
本帖最后由 windoze 于 2016-11-02 23:20 编辑

刚才做了一个micro benchmark,代码如下:

  1. #include <chrono>
  2. #include <iostream>
  3. #include <algorithm>

  4. constexpr size_t len=100000;
  5. int test_data[len];
  6. size_t garbage;

  7. void generate_test_data() {
  8.         for(size_t i=0; i<len; i++) {
  9.                 test_data[i]=i;
  10.         }
  11. }

  12. int upperBound(void *key,void *data,int data_count,int (*compare)(void *key,void *data,int n))
  13. {
  14. int middle,start=0,end=data_count-1,val;
  15. int result=-1;
  16.         if(!key||!data) return -1;
  17.         while(start <= end) {
  18.                 middle = start + ((end-start) >> 1);
  19.                 val=compare(key,data,middle);
  20.                 if(val>0)  {
  21.                         if(!middle||compare(key,data,middle - 1) <= 0)
  22.                                 result=middle;
  23.                         end=middle-1;
  24.                 } else start=middle+1;
  25.         };
  26.         return result;
  27. }

  28. int cmp(void *key, void *data, int n) {
  29.         return ((int *)data)[n]-*((int *)key);
  30. }

  31. void test1() {
  32.         // Find the last element smaller than 80000 in test_data
  33.         int key=80000;
  34.         // Make sure this is not optimized;
  35.         garbage+=upperBound(&key,test_data,len,cmp)-1;
  36. }

  37. struct cmp_stl {
  38.         bool operator()(int a, int b) const { return a<b; }
  39. };

  40. void test2() {
  41.         // Find the last element smaller than 80000 in test_data
  42.         // Make sure this is not optimized;
  43.         garbage+=std::upper_bound(test_data, test_data+len, 80000, cmp_stl())-test_data-1;
  44. }

  45. void time_it(size_t repeat, void(*f)()) {
  46.         auto start=std::chrono::system_clock::now();
  47.         for(size_t i=0; i<repeat; i++) {
  48.                 f();
  49.         }
  50.         auto end=std::chrono::system_clock::now();
  51.         std::cout << "Duration: " << (end-start).count() << std::endl;
  52. }

  53. int main() {
  54.         generate_test_data();
  55.         // Warm up
  56.         for(size_t i=0; i<100; i++) {
  57.                 test1();
  58.                 test2();
  59.         }
  60.         std::cout << "Test1 ";
  61.         time_it(100000, test1);
  62.         std::cout << "Test2 ";
  63.         time_it(100000, test2);
  64. }
复制代码


用O3优化:
Test1 Duration: 3038
Test2 Duration: 2581
STL比你的代码快15%

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
25 [报告]
发表于 2016-11-03 11:03 |只看该作者
本帖最后由 yulihua49 于 2016-11-03 11:22 编辑
windoze 发表于 2016-11-02 23:10
刚才做了一个micro benchmark,代码如下:
  1. ./stl_test
  2. Binary Duration: 40821
  3. MAP Duration: 60072
  4. [sdbc@erg0devprc01 test]$ ./stl_test
  5. Binary Duration: 26527
  6. MAP Duration: 67755
  7. [sdbc@erg0devprc01 test]$ ./stl_test
  8. Binary Duration: 34094
  9. MAP Duration: 68970
  10. [sdbc@erg0devprc01 test]$ ./stl_test
  11. Binary Duration: 45728
  12. MAP Duration: 45947
  13. [sdbc@erg0devprc01 test]$ ./stl_test
  14. Binary Duration: 38153
  15. MAP Duration: 65282
  16. [sdbc@erg0devprc01 test]$ ./stl_test
  17. Binary Duration: 37609
  18. MAP Duration: 63948
  19. [sdbc@erg0devprc01 test]$ ./stl_test
  20. Binary Duration: 36992
  21. MAP Duration: 66479

复制代码
在我这里编译一下。很奇怪,运行时间不稳定,但总的来说,Binary比较快。时间单位是微秒。
  1. ./stl_test
  2. Binary Duration: 34061
  3. MAP Duration: 73334
  4. [sdbc@erg0devprc01 test]$ ./stl_test
  5. Binary Duration: 33392
  6. MAP Duration: 73889
  7. [sdbc@erg0devprc01 test]$ ./stl_test
  8. Binary Duration: 40846
  9. MAP Duration: 68421
复制代码
知道原因了,系统有别的任务在运行。
  1. #include <datejul.h>
  2. #include <iostream>
  3. #include <algorithm>
  4. #define len 100000
  5. int test_data[len];
  6. size_t garbage;
  7. void generate_test_data() {
  8.         for(size_t i=0; i<len; i++) {
  9.                 test_data[i]=i;
  10.         }
  11. }
  12. int upperBound(void *key,void *data,int data_count,int (*compare)(void *key,void *data,int n))
  13. {
  14. int middle,start=0,end=data_count-1,val;
  15. int result=-1;
  16.         if(!key||!data) return -1;
  17.         while(start <= end) {
  18.                 middle = start + ((end-start) >> 1);
  19.                 val=compare(key,data,middle);
  20.                 if(val>0)  {
  21.                         if(!middle||compare(key,data,middle - 1) <= 0)
  22.                                 result=middle;
  23.                         end=middle-1;
  24.                 } else start=middle+1;
  25.         };
  26.         return result;
  27. }
  28. int cmp(void *key, void *data, int n) {
  29.         return ((int *)data)[n]-*((int *)key);
  30. }
  31. void test1() {
  32.         // Find the last element smaller than 80000 in test_data
  33.         int key=80000;
  34.         // Make sure this is not optimized;
  35.         garbage+=upperBound(&key,test_data,len,cmp)-1;
  36. }
  37. struct cmp_stl {
  38.         bool operator()(int a, int b) const { return a<b; }
  39. };
  40. void test2() {
  41.         // Find the last element smaller than 80000 in test_data
  42.         // Make sure this is not optimized;
  43.         garbage+=std::upper_bound(test_data, test_data+len, 80000, cmp_stl())-test_data-1;
  44. }
  45. void time_it(size_t repeat, void(*f)()) {
  46. //        auto start=std::chrono::system_clock::now();
  47. INT64 now=now_usec();
  48.         for(size_t i=0; i<repeat; i++) {
  49.                 f();
  50.         }
  51. //        auto end=std::chrono::system_clock::now();
  52.         std::cout << "Duration: " << INTERVAL(now) << std::endl;
  53. }
  54. int main() {
  55.         generate_test_data();
  56.         // Warm up
  57.         for(size_t i=0; i<100; i++) {
  58.                 test1();
  59.                 test2();
  60.         }
  61.         std::cout << "Binary ";
  62.         time_it(100000, test1);
  63.         std::cout << "MAP ";
  64.         time_it(100000, test2);
  65. }
复制代码

程序改了一点点,有的地方编译不过去。

那个好像不是stl里的map,就是数组的,所以没有爬楼开销。

man upper_bound
没有 upper_bound 的手册页条目

居然有这个函数,学习了。在哪里能找到这组函数的说明?








论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
26 [报告]
发表于 2016-11-03 11:33 |只看该作者
本帖最后由 yulihua49 于 2016-11-03 11:37 编辑
yulihua49 发表于 2016-11-03 11:03
在我这里编译一下。很奇怪,运行时间不稳定,但总的来说,Binary比较快。时间单位是微秒。
知道原因了, ...

又改了下,带重复值的:
./stl_test
Binary Duration: 44759,N=80009
MAP Duration: 54602,N=80009
[sdbc@erg0devprc01 test]$ ./stl_test
Binary Duration: 30905,N=80009
MAP Duration: 59491,N=80009
[sdbc@erg0devprc01 test]$ ./stl_test
Binary Duration: 34123,N=80009
MAP Duration: 58152,N=80009
[sdbc@erg0devprc01 test]$ ./stl_test
Binary Duration: 38281,N=80009
MAP Duration: 50799,N=80009
[sdbc@erg0devprc01 test]$ ./stl_test
Binary Duration: 37987,N=80009
MAP Duration: 60669,N=80009

  1. <p>void generate_test_data() {
  2.         for(size_t i=0; i<len; i++) {
  3.                 test_data[i]=i/10;
  4.         }
  5. }
复制代码


key改为8000.


尽管双方都是数组,还是我的快。

原来我不知有数组算法,只知有multimap的算法。


论坛徽章:
6
2015年辞旧岁徽章
日期:2015-03-05 16:13:092015年迎新春徽章
日期:2015-03-05 16:13:092015小元宵徽章
日期:2015-03-06 15:58:1815-16赛季CBA联赛之浙江
日期:2016-11-05 14:38:4115-16赛季CBA联赛之新疆
日期:2016-11-11 18:38:06
27 [报告]
发表于 2016-11-03 11:50 |只看该作者
创新轮子也是从重复造轮子起步的,坚持博采众长地自主开发,就能不断进步,比肩大师甚至超越,一切皆有可能

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
28 [报告]
发表于 2016-11-03 12:55 |只看该作者
本帖最后由 windoze 于 2016-11-03 12:57 编辑

回复 26# yulihua49

你肯定没开优化对吧,开O2的时候STL就已经比你的代码快了。另外你说编译不过的那两行代码是C++11的,用-std=c++11就能编译过了。
std::upper_bound的代码和你写的差不多,不是map,都是数组,但是不用函数指针可以让代码inline的更激进一点。
文档你可以在 http://en.cppreference.com/w/ 上看,C和C++的都有。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
29 [报告]
发表于 2016-11-03 13:15 |只看该作者

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
30 [报告]
发表于 2016-11-03 13:17 |只看该作者
这几个函数没有针对数组和vector的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP