星期二,我们在JDriven举行了第二次“ Vrolijke Framboos”(快乐树莓的荷兰语)Java代码挑战赛 ,这是爆炸性的! 今年的挑战是创建一个REST服务客户端,该客户端将与服务器一起玩猜数字游戏。 设置会话后,您会猜到一个数字,服务器将以“较低”,“较高”或“宾果”响应。 目标是在两分钟内猜出尽可能多的数字。 给定挑战的名称,您可能会猜到目标平台将是Raspberry Pi !
这是一次令人难以置信的有趣体验,在这篇文章中,我想解释一下我是如何应对挑战的,而我的解决方案最终是如何获得第二名的,这让我只是错失了将奖杯和新树莓带回家的机会。
制备
从上一期挑战中我所知道的(不幸的是,这并不是我的一部分),我们必须提供一个可运行的jar,该jar将在树莓上执行。 我通过设置一个空的Maven项目来进行准备,该项目将其自身及其依赖项组装到一个胖的jar中,该胖的jar可通过java -jar <jarfile> .jar从命令行运行。 我还包括了一堆我甚至没有使用过的标准依赖项(Log4j,Junit,Guava)。
执行
当我们被解释为将通过发布请求与REST服务进行通信时,我首先添加了Apache HTTP客户端( 流畅的API很好!),并为该客户端添加了Jackson。
我还使用了一个现有的Spring Boot REST测试项目来添加我自己的服务器模拟版本,因为直到截止日期前30分钟我们才可以使用该服务器。 我花了相当多的时间来创建服务器端点,以便我的客户可以和别人交谈。
在服务器就位的情况下,我实现了客户端接口。 使用Jackson + Apache HTTP客户端,这非常简单。 一个例子:
public GuessResponse guess(GuessRequest req) throws IOException {String result = Request.Post(baseUrl + "/api/numbergame").bodyString(toJson(req), ContentType.APPLICATION_JSON).execute().returnContent().asString();return mapper.readValue(result, GuessResponse.class);
}
这是进行猜测的REST调用; 非常简单!
算法
该操作的“大脑”是一个Worker线程,该线程不断启动新游戏,并且在游戏内使用简单的二进制搜索来尽快向下钻取正确的猜测。 通过伪查询中的二进制搜索来猜测数字是:
low_bounds = 0
high_bounds = 2^63
loop:pivot = low_bounts + (high_bounds - low_bounds / 2)guess = guess(pivot)if guess == "lower":high_bounds = pivot - 1else if guess == "higher":low_bounds = pivot + 1else if guess == "bingo"return pivotelsethrow Error
典型的复杂度是O(log n),比O(n)蛮力好得多。
我的第一个实现使用2 ^ 31的上限,但是我很快发现服务器正在分发更高的数字。
最佳化
通过基本的实现工作,我开始尝试优化解决方案,因为我想我不是唯一一个使用binsearch实现的人。 我的第一个猜测是通过让多个工人同时玩游戏来并行化工作。 效果很好; 似乎最大的瓶颈是HTTP往返,转移到8个线程使我大大提高了速度。
不幸的是,当截止日期临近时,我听说实际的比赛只能允许一个会话进行,所以我的方法行不通。 我花了很多时间试图找到一种方法来让多个线程处理该问题,以尝试规避HTTP开销,但是不幸的是,我没有时间提出解决方案。
比赛时间
我们提交了解决方案(在20位左右的参赛者中,我们大约有13种实现方案),而我的同事Pepijn开始运行它们。 该服务器具有非常整洁的报告功能,可向我们实时显示得分!
某些解决方案根本不起作用,但一堆却不能,而且很快! 我进入前三名的机会确实开始变得渺茫了。 我的提交实际上是最后一次执行,因此必须等待,这真让人不知所措。 当他们最终运行我的解决方案时,它比我期望的运行速度要快得多,这是基于我看到的运行自己的机器的速度。 这显然是由于树莓派和服务器之间的有线连接。
所有解决方案都运行了2分钟,我以556个正确的猜测最终排名第二。 数字1(由里卡多提交)是714,数字3是289,所以我对结果非常满意!
验尸
这是一个了不起的夜晚,当我们接到任务后,看到每个人都进入极端聚焦模式真是太有趣了。 我们大多数人浪费很少的时间(如果有的话)吃披萨,而是非常努力地工作以找到可行的解决方案。
对我来说很好的是
- 准备 :必须拥有一个IDE,该IDE的一个空项目已经准备好构建到jar中。 进行这样的设置不需要花费很多时间,但是当您的总时间约为2-3小时时,那15分钟非常有价值!
- 算法 :我的二进制搜索方法效果很好,特别是与某些人采用的蛮力方法相比。 最初,我以为他们会使用整个“ int”搜索空间,但是我很快就知道这实际上是一个“ long”搜索空间。 蛮力根本不会削减它。
- 关注速度 :我没有为单元测试或使用getter / setter创建适当的Java POJO而烦恼。 CheckStyle用我的代码会心脏病发作。 重要的是使其正常运行。
- 删除调试语句 :System.out非常昂贵! 有些人忘了删除紧密循环中的打印内容,这会大大降低您的应用程序的速度。 我的只报告了猜测的数字。
本来可以更好的
- 准备 :尽管我设置了IDE,但我不知道我会实现模拟REST服务。 如果我可以使用Node.js之类的东西和基本的REST服务,那么我将在客户端集成方面取得很大进步。
- 专注于多线程 :最终没有成功的赌博。 会话系统将不允许执行并行游戏,并且二进制搜索实际上并不能很好地并行化。
- 缺乏对搜索空间的关注 :我认为0到2 ^ 63之间的完整空间是可以猜测的,但是很明显,当我们开始比赛时,它总是在猜测非常高的数字。 如果我创建了第一个测试结果的直方图,我可能会发现分布根本不均匀。 我本可以使下限和上限适应所找到的数字。
- 缺乏对HTTP开销的关注 :我没有时间去寻找如何通过例如保持连接打开来减少HTTP开销的方法。 回想起来,这可能会有很大的不同。
结论
这是我的第一个代码的质询/ hackaton我参加了,这是这么多的乐趣,我可以把它推荐给任何人。 在竞争激烈的环境中编码与您的日常工作有很大不同。 这要更加激烈,因此每个人都直接进入“区域”,而实际上您的生产力非常高。 这个“区域”对我来说是个快乐的地方,相当让人上瘾。 缺点是我被大肆宣传,甚至无法入睡。 我写这篇文章的主要原因是 把它从我的脑子里弄出来;)
翻译自: https://www.javacodegeeks.com/2015/07/code-challenge-vrolijke-framboos-postmortem.html