当前位置: 首页 > 编程日记 > 正文

一个从四秒到10毫秒,花了1年的算法问题?

原文:一个从四秒到10毫秒,花了1年的算法问题?

五一后的第一周,由于搬家腰扭伤了,没注意导致压迫神经,躺在床上休息了好几天。所以没事就挂 QQ,一个网友突然问了我一个算法问题。所以有了这篇文章。感触很深,所以特发此文,以纪念和写给新朋友,以及那些热爱编程的非专业人事。本人可能技术含量很低,但都很真实。虽然我只花了很少的时间,但解决了这个网友困惑了1年的问题,这个网友倒是特别感激,而我倒是感觉特别心塞。那大家喝杯茶,看看这个过程吧。

本文原文地址:http://www.cnblogs.com/asxinyu/p/4504487.html

1.人物背景

这个网友我也是后来聊天才了解到他的情况。他是1个1977年出生的湖北网民,为了分析相关数据,自学了VB.NET,这个年龄的人还学了这个,真的不容易,而且能够用VB.NET开发比较复杂的数据分析界面。其实后来了解到这些,自愧不如啊。所以对算法问题,这个朋友遇到困难,也可以理解。

其实这个朋友很早就是我的QQ好友,也知道都是做数据分析,所有我有新的算法方面的文章会发给他看,偶尔聊一下,但没有问过我问题。上个月发表了一篇文章:机器学习之PageRank算法应用与C#实现(1)算法介绍,发表之后,他看到后,才问我这个问题。

我:其实我也是个半吊子,对算法也不精通,只是业余研究感兴趣而已。。说实话你要我写个二分搜索,我一时半会还搞不定,但我看看论文和资料,可以写个马尔可夫链或者贝叶斯之类的。。。这个东西怎么说呢,在很多问题中,空间效率和时间效率,特别是在硬件条件如此富裕的情况下,可以考虑得更少一点。。当然这里绝对不是说算法是没有用的,只是对很多非常普通的人来说,研究的规模太小,而且由于经验和特殊原因,没有算法和数据结构基础,只能不考虑了,以解决实际问题为主吧。

2.原始问题

该网友的原始问题是这样的,我从QQ聊天记录里直接Copy过来:

 有两组随机生成的(0~99999)Int32数据A和B,将A按顺序判断在B中是否存在并记录在Boolean型的C中,我分别尝试了Array与List(Of T),在VS2010下以我的破电脑的速度Array大概需要4秒,而List(Of T)则要24秒,以下是我用Array和List(of T)的代码,请高手指点, 顺便问下有无秒杀的方法。(注:他的VB代码我就不贴了,思路知道就可以了)

帮我看看用什么方法解决,谢谢

有人说用哈希,可惜我不会,也没百度到

他的开发环境是VS2010 + VB.NET

我收到他的消息的时候是正在用手机QQ上的,他还贴了段VB的代码,我是比较反感直接贴代码的人。不过当时躺在床上,也没啥事,好奇嘛,就仔细看了一下这个问题,代码真的没看。

3.解决问题的过程

由于是手机上的,所以也没开电脑敲代码。就想了一下。

网友的原始代码中的比较都是使用Array.IndexOf,可以想象7万的数组,速度慢也正常 。

1.首先我是把哈希给否定了的。其实后来想起来,是我错了,我以为他说的哈希是把每个元素求哈希值后对比,这不是多此一举么。。本来计算哈希就要时间,还是要比较。。。那何必呢。。。后来我才想到,他说的可能是“哈希表”,这是后话,不提了,哈希表这个方法怎么样不知道,应该也还可以吧;但还是先看看我的方法。

2.我当时先给了他一个初步的方案,解决问题有时候不是一步到位的,先试试看咯。我的想法是使用IndexOf查找会浪费很多时间。所以,你先把B排序,或者B在实际构造过程中就可以进行排序存储,然后A依次对比的时候,采用二分法搜索,甚至有条件,A也可以先排序,然后搜索的时候记录起点,二分法搜索,这样可以节省不少时间。A和B排序的问题,其实根据他的情况,是可以在实际过程中就排序好的,而不是生成后排序,这样就更费时间了。

这个网友也很迅速,过了大概1个小时,测试出来说:“我用的随机数测试了下,速度提升相当明显,比Array.indexof要快多了”

 3.上面手机沟通不方便,也就随便说了一下,没想到他很快做出来了。虽然快了很多,但具体时间我也没问。然后我洗澡的时候,感觉这个问题不是那么回事,我以前貌似也做过类似的,应该还有更快的方法。然后洗澡过程中,思考了若干秒。。。一个思路也有了,虽然这个想法我感觉很土,但我想实际效果应该很好,所以洗完澡,马上开电脑,跟网友说了一下思路,考虑到他有可能无法理解算法的伪代码或者比较严格的表述(实际上我也不知道该怎么严格表述),所以就直接打了一个比方,在这里为了方便大家理解,我先大概写了个思路,应该会看得懂吧。至于问题中的记录在C中,我具体没问他怎么记录,其实这和问题关系不大,核心在前面如何确定是否包括:

我给那位网友是这么打比方的(原始有点乱,我写博客的时候稍微整理了下),不知道大家有没有歧义,感觉还是上面的伪代码容易理解,但是开心的是,这个网友还是理解了 :

A数组:不管,随意,也不用排序,
B数组:[5,2,4,1],假设最大为5,注意没有3

初始化一个长度为5(最大数)的布尔数组:a[1],[2],[3],[4],[5]
循环B,将B中值作为a的下标,对应位置标记为true,例如
a[5]= true;
a[2]= true;
a[4]= true;
a[1]= true;
注意a[3]没有,为false

最后循环A,直接对比下标,如果A={2,3},那么:
a[2]=true,说明存在,则C[2]=true,到C中标记true
a[3]=false,则没有。C中标记为false
如果你最大为99999,那么这个数组要这么长你可以直接设置为99999,浪费一点空间;
如果你业务中可以直接求出最大值,那是最好的了。自己试一试。

这个思路理解了非常简单。这个网友也很快理解了,过了一会就把他的结果告诉我了。

下降到10毫秒左右,他把数据扩大到10万,速度也挺快的。

4.后记与C#实现

解决他的问题后,第二天我们又聊了一会,他表示很感谢,说这个方法速度非常快。说这1年来,问过很多人,也找过很多计算机方面的人,但效果都不好。。。

据说还问过一个拿过什么微软认证的人。。。说他的电脑不行,要去换一下。。。这个有点过份操蛋了。。才几万的数组,能耗多少内存,都是简单的比较计算,需要很好的CPU么。。。

后来我也给他分析过说,其他人可能没有完全理解你的问题,都一根筋考虑效率和速度的问题了,所以考虑的东西多了,给你的建议也不一定合适。对这些小问题,牺牲一点空间,何况又不多,而且内存也便宜,现在动不动2G,4G。。换时间也是够划算的。我这里说的空间,是直接初始化数组C的长度包括所有的数字个数,因为我也不了解他实际的数据怎么来的,当然如果能计算最大值,肯定最好了。这样稍微计算一下时间复杂度,循环2遍就能解决问题。至于我第一次提到的排序和二分法的问题,也只是刚开始想到的,没有更深入的思考,因为也是考虑到他的数据是可以在生成的时候就进行排序的,这样也可以省时间,而不是所有的都IndexOf,不慢才怪。

4.1 C#代码实现原始方法

闲的没事,我用C#实现了一下网友原始的方法,代码如下:

 1 static void ValidateArrayElement2()
 2 {
 3     Stopwatch sp = new Stopwatch();
 4     sp.Start();//开始计时
 5     Random rand = new Random();
 6     Int32 maxValue = 120000;//元素最大值,是一个假定值
 7     Int32 length = 70000;// A,B的长度
 8     Int32[] A = new Int32[length];
 9     Int32[] B = new Int32[length];
10     Boolean[] C = new Boolean[length];
11     //随机初始化A,B数组
12     for (int i = 0; i < length; i++)
13     {
14         A[i] = rand.Next(maxValue);
15         B[i] = rand.Next(maxValue);
16     }
17     //循环A,验证是否存在,将C对应位置标记为true
18     for (int i = 0; i < A.Length; i++) if (B.Contains(A[i])) C[i] = true;
19     sp.Stop();
20     Console.WriteLine(sp.ElapsedMilliseconds);
21 }

测试了下,我机器是X200+T9400,3G内存。加上数据初始化总共时间是4.3秒,所以实际的时间是4秒左右,和网友的结论是差不多的。看看我下面的方法:

4.2 C#代码实现上述算法

使用第3节提出的方法,我测试一下时间:

 1 static void ValidateArrayElement()
 2 {
 3     Stopwatch sp = new Stopwatch();
 4     sp.Start();
 5     Random rand = new Random();
 6     Int32 maxValue = 120000;//元素最大值,是一个假定值
 7     Int32 length = 70000;// A,B的长度
 8     Int32[] A = new Int32[length];
 9     Int32[] B = new Int32[length];
10     Boolean[] C = new Boolean[length];
11     Boolean[] Atemp = new Boolean[maxValue];//临时的辅助变量
12     //随机初始化A,B数组
13     for (int i = 0; i < length; i++)
14     {
15         A[i] = rand.Next(maxValue);
16         B[i] = rand.Next(maxValue);
17     }          
18     //循环B,验证元素是否存在
19     foreach (var item in B) Atemp[item] = true;
20     //循环A,验证是否存在,将C对应位置标记为true
21     for (int i = 0; i < A.Length; i++) if (Atemp[A[i]]) C[i] = true;
22     sp.Stop();//停止计时
23     Console.WriteLine(sp.ElapsedMilliseconds);
24 }

实际时间只有5ms左右,如果不算数据初始化的时间,基本只有1ms,和网友的10ms有点差别,可能和机器有关吧。总的来说,速度的确是提高了不少。

至于所谓的哈希表方法,这里就不实现了,已经够快了。

最后感谢那些和我一样,热爱编程的业余人事。。。虽然我们不是正规军,虽然我们没有学过数据结构,也没有系统学习过专业的算法课程,没有接受过专业的编程培训,但只要细心和动脑筋,解决一些小规模的问题,还是可以的。至于那些大量数据的效率问题,算法问题就交给大牛吧。

剩下的时间交给网友,这个问题简单吗?你会怎么解决?期待评论有更好更佳的答案。。。如果是喷,说问题简单那就算了吧,没必要,何苦为难我呢。。。

4.3 HashSet测试

感谢passer.net网友,说用HashSet,这个类以前知道,但很少用,既然提出来了,就测试一下,代码如下:

 1 Stopwatch sp = new Stopwatch();
 2 sp.Start();
 3 Random rand = new Random();
 4 Int32 length = 70000;// A,B的长度
 5 Int32[] A = new Int32[length];
 6 Int32[] B = new Int32[length];
 7 Boolean[] C = new Boolean[length];
 8 var tmp = new HashSet<int>();
 9 //随机初始化A,B数组
10 for (int i = 0; i < length; i++)
11 {
12     A[i] = rand.Next();
13     B[i] = rand.Next();
14     if (!tmp.Contains(B[i]))
15         tmp.Add(B[i]);
16 }
17 
18 //循环A,验证是否存在,将C对应位置标记为true
19 for (int i = 0; i < A.Length; i++) C[i] = tmp.Contains(A[i]);
20 sp.Stop();//停止计时
21 Console.WriteLine(sp.ElapsedMilliseconds);

测试了一下,大约17ms,比文章的方法稍微慢了一点,但也非常快了,在一个数量级水平吧。可能哈希表对其他复杂的类似数据或者大数据量更管用。不过无所谓了,都是方法,都能解决问题,不必纠结这些细节。

相关文章:

xinetd 说明

xinetd 是什么在linux中一些不长期使用的服务&#xff08;不重要的服务&#xff1f;&#xff09;没有被作为单独的守护进程在开机时启用&#xff0c;linux把这些服务监听端口全部由一个独立的进程xinetd集中监听&#xff0c;当收到相应的客户端请求之后&#xff0c;xinetd进程就…

英特尔携手中科院计算所建立中国首个 oneAPI 卓越中心

11月12日&#xff0c;在第三届中国超级算力大会&#xff08;ChinaSC 2021&#xff09;上&#xff0c;英特尔与中国科学院计算技术研究所共同建立中国首个 oneAPI 卓越中心&#xff0c;来扩大 oneAPI 对中国本土国产硬件的支持及使用oneAPI来开发全栈式开源软件。 在上个月刚结…

前端学习资源分享

2019独角兽企业重金招聘Python工程师标准>>> 推荐大神文章(文字教程) 1 综合类 前端知识体系前端知识结构Web前端开发大系概览Web前端开发大系概览-中文版智能社 - 精通JavaScript开发JavaScript中的this陷阱的最全收集--没有之一JS函数式编程指南腾讯移动Web前端知…

Nginx源码分析链接

nginx-0.8.38源码探秘&#xff1a;http://blog.csdn.net/ccdd14/article/details/5872312nginx源码分析&#xff1a; http://blog.sina.com.cn/s/blog_677be95b0100iiv7.html

基于聚类的图像分割(Python)

作者 | 小白来源 | 小白学视觉了解图像分割当我们在做一个图像分类任务时&#xff0c;首先我们会想从图像中捕获感兴趣的区域&#xff0c;然后再将其输入到模型中。让我们尝试一种称为基于聚类的图像分割技术&#xff0c;它会帮助我们在一定程度上提高模型性能&#xff0c;让我…

4月第4周全球域名商TOP15:万网第四 增势减弱

IDC评述网&#xff08;idcps.com&#xff09;05月21日报道&#xff1a;据WebHosting.info公布的最新数据显示&#xff0c;在4月第4周&#xff0c;全球十五强域名商中&#xff0c;域名总量成功实现净增长的有7家。其中&#xff0c;中法各1家&#xff0c;即中国万网与OVH.NET&…

PXE全自动安装操作系统--centos7.3学习笔记

PXE服务器&#xff1a;192.168.110.110 环境准备 安装软件 # yum -y install dhcp tftp-server tftp vsftpd lftp DHCP配置 # cd /var/dhcp # cp /usr/share/doc/dhcp-4.2.5/dhcpd.conf.example /etc/dhcp/dhcpd.conf # vim /etc/dhcp/dhcpd.conf subnet 192.168.110.0 netmask…

无事“自动驾驶”,有事“辅助驾驶”?

近日来&#xff0c;智能汽车事故频发&#xff0c;且事故原因多与所谓的“自动驾驶”功能有关&#xff0c;这不由得引起了人们对“自动驾驶”发展前景的担忧。实际上&#xff0c;大众理解的“自动驾驶”与官方的定义可能有所出入。全球公认的标准一般是由SAE International&…

九、数据库群集部署、配置 (二)

九、 数据库群集部署、配置&#xff08;二&#xff09;配置DTC 角色高可用在群集管理器对话框&#xff0c;选择"配置角色"&#xff0c;如图2. 选择"下一步"&#xff0c;如图3. 在选择角色对话框&#xff0c;选择"分布式事务协调器&#xff08;DTC&a…

Linux下怎么诊断网站性能异常

网站如果突然慢了&#xff0c;怎么样诊断&#xff1f; 先用Top命令查看进程 #top选择Haporxy代理的进程 #strace -p 25054进程在干什么看的一清二楚。

[Java面试五]Spring总结以及在面试中的一些问题.

2019独角兽企业重金招聘Python工程师标准>>> 1.谈谈你对spring IOC和DI的理解&#xff0c;它们有什么区别&#xff1f; IoC Inverse of Control 反转控制的概念&#xff0c;就是将原本在程序中手动创建UserService对象的控制权&#xff0c;交由Spring框架管理&#…

一次完整的抓包分析 Reserved TCP/IP Port List

抓包如图所示&#xff1a; 本机IP被粉色遮住。。。http://www.skynet.ie/~colinmac/Programming/port_listing.htmlReserved TCP/IP Port List This is an complete list of the TCP/IP ports that are IANA registered and so are not for general use in network programming…

关于Centos下Clamv反病毒软件包更新问题

最近一直在研究学习Centos下搭建Postfix实现邮件网关的内容&#xff0c;以便后期邮件平台网关的灾备做一些准备&#xff0c;今天安装Postfix到了对Clamv反病毒软件包更新的安装配置部分&#xff0c;遇到了个小的插曲。 具体遇到问题看着不是什么大问题&#xff0c;就是Clamv之前…

Meta 研发触觉手套助力元宇宙,虚拟世界也可以有触觉

编译 | 禾木木 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 你不能戴着 Meta 的新型高科技虚拟现实手套抚摸狗。 但研究人员可以让它越来越接近。 Meta&#xff08;前身为 Facebook&#xff09;伴随着对于虚拟世界和元宇宙的领域而闻名。然而&#xff0c;七年…

如何判断哪个商城系统好?

现在市面上很多商城系统&#xff0c;如果开发者有商城系统的需求&#xff0c;那么可以用&#xff0c;可以缩短开发周期&#xff0c;网站更快速上线&#xff1b;可降低开发成本。但是正因为系统很多&#xff0c;怎么选择就是个问题了。因为一个商城所使用的商城系统也会产生对一…

TCP/IP中 3688端口是什么?

原文英文&#xff1a;http://www.corrupteddatarecovery.com/Port/3688udp-Port-Type-simple-push-s-simple-push-s.asp 翻译的不好将就看吧。 一个软件端口&#xff08;通常只是被称为一个“口”&#xff09;是一个虚拟的数据连接&#xff0c;可以通过程序用于直接交换数据&a…

文件处理命令:sed

使用&#xff1a;sed [-nefr] actionaction:-i直接修改读取的档案内容&#xff0c;而不是由屏幕输出&#xff0c;-r表示支持延伸型正则表达式的语法。动作说明&#xff1a;[n1[,n2]] function n1,n2表示要选择的行数&#xff0c;function包括&#xff1a;a-新增&#xff0c;c-取…

新技能 Get,使用直方图处理进行颜色校正

作者 | 小白来源 | 小白学视觉在这篇文章中&#xff0c;我们将探讨如何使用直方图处理技术来校正图像中的颜色。像往常一样&#xff0c;我们导入库&#xff0c;如numpy和matplotlib。此外&#xff0c;我们还从skimage 和scipy.stats库中导入特定函数。import numpy as np impor…

Oracle数据库 之 删除RMAN备份

#su – oracle 切换至存放备份的目录&#xff0c;删除不需要的备份文件。 $export ORACLE_SIDorcl $rman RMAN>connect target / RMAN>crosscheck backup; RMAN>delete expired backup; RMAN>exit 转载于:https://www.cnblogs.com/hdtiny/p/8420770.html

Linux环境编程--fflush(stdout)有什么作用

代码&#xff1a; printf("hello\n");//fflush(stdout);fork(); 输出&#xff1a; hello代码&#xff1a; printf("hello\n");fflush(stdout);fork(); 输出&#xff1a; hellohello说明&#xff1a;系统函数fork()创建新的进程。 printh后打印内容在缓冲区…

sysdba不能远程登录,我们该怎么做 (转载)

sysdba不能远程登录这个也是一个很常见的问题了。 碰到这样的问题我们该如何解决呢&#xff1f; 我们用sysdba登录的时候&#xff0c;用来管理我们的数据库实例&#xff0c;特别是有时候&#xff0c;服务器不再本台机器&#xff0c;这个就更是有必要了。 当我们用sqlplus &qu…

TeaTalk 线上直播倒计时 | 云数据库技术创新研究与实践

随着云计算的发展&#xff0c;数据库上云已经成为趋势&#xff0c;云数据库服务相对于传统数据库在架构、性能与安全等方面都存在着新的挑战。11月23日&#xff0c;移动云TeaTalk线上沙龙带着满满的干货来了&#xff01;本次技术沙龙邀请了移动云创新中心的技术专家及华中科技大…

再测Golang的JSON库

2019独角兽企业重金招聘Python工程师标准>>> 写项目一直需要进行序列化&#xff0c;听到了&#xff0c;也看到了很多同学老师对各个golang的json库进行测评。那本人为什么还要继续进行这一次测评呢&#xff1f; 因为实践过的知识最有说服力&#xff0c;也是属于自己…

一、JAVA通过JDBC连接mysql数据库(连接)

JDBC ----JDBC(Java DataBase Connectivity)是Java与数据库的接口规范&#xff0c;JDBC定义了一个支持标准SQL功能的通用低层的应用程序编程接口(API)&#xff0c;它由Java 语言编写的类和接口组成&#xff0c;旨在让各数据库开发商为Java程序员提供标准的数据库API。 JDBC API…

给你一个热爱阅读的机会,走到哪儿,看到哪儿的读书体验

整理 | 禾木木出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;不知道在自我介绍的时候是不是都有一个共同的爱好&#xff1a;阅读。但是喜欢阅读就代表会经常去图书馆或者是阅读室吗&#xff1f;不&#xff01;这是一个肯定的答案。通常会因为太忙或是懒惰而选择放弃…

Linux环境编程--进程

查看正在运行的进程 #ps -ef #ps ax 可以看到状态查看nice值 #ps -l #ps -fsystem函数 传递命令&#xff0c;如同在shell中执行 char * p"ps ax"; system(p);或者 "ps ax &";//ps一启动shell就返回execl,execlp,execle函数 exec启动一个新程序&#xf…

芝麻HTTP:Scrapy-Splash的安装

2019独角兽企业重金招聘Python工程师标准>>> Scrapy-Splash是一个Scrapy中支持JavaScript渲染的工具&#xff0c;本节来介绍它的安装方式。 Scrapy-Splash的安装分为两部分。一个是Splash服务的安装&#xff0c;具体是通过Docker&#xff0c;安装之后&#xff0c;会…

用bind架设自己的智能DNS

中国的南北网络问题&#xff0c;是许多做网站的人的心病除了使用双通或者多通机房以外&#xff0c;还可以通过多台镜像服务器的方法来提高用户的访问速度 但是&#xff0c;如果使用的双通机房并不是单IP的&#xff0c;或者使用多台镜像的做法&#xff0c;就会面临多个不同的服务…

漫漫运维路——集群基础知识

集群的基本概念随着计算机科学的发展&#xff0c;对计算机的性能要求越来越高&#xff0c;比如在很多流量比较大的门户网站以及科学实验环境中需要海量计算的环境&#xff0c;这时候就迫切需要后端的服务器性能有提升。而对于提升后端服务器性能所采用的方式有两种&#xff0c;…

数据爆发时代,英特尔携手腾讯构筑全面的数据长城

作者 | 贾凯强 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 5G到来&#xff0c;边缘需求不断&#xff0c;IoT持续爆棚&#xff0c;数据在爆炸式增长。 在数据增长的过程中&#xff0c; 相应的其处理能力也需要增长&#xff0c;CPU等算力核心也在提升&#xff0c;…