好了,最后来介绍一下Jetbrains第三组谜题的解决办法吧。
线索一:推特代码
先看看这次的推特代码,和前两组不太一样,这次是真的无意义随机字符串了。不过之前一段时间我正好研究了某软件的配置,它的配置分享方式就是用base64方式加密,这次我看到这个貌似也有点像,于是就把代码复制到Python中试了一下,还真让我蒙对了。
代码语言:javascript复制fun twitterCode3() {
val sss = "SGF2ZSB5b3Ugc2VlbiB0aGUgcG9zdCBvbiBvdXIgSW5zdGFncmFtIGFjY291bnQ/"
println(Base64.getDecoder().decode(sss).map { it.toChar() }.joinToString(separator = ""))
}
解出来的密文如下。
Have you seen the post on our Instagram account?
线索二:Instagram图片
那我们就看看Jetbrains的Instagram有啥吧。果然还真有这么一张图片,图片说明中让我们看看另一个网页,它是一个Kotlin小代码。
打开之后就是这么一个东西,显然,还是移位加密方式。不过如果你还记得第一个谜题最后的密文,应该就会轻松记得移位距离应该是3,因为它们中都出现了井号(也就是移位以后的空格)。所以把n改成3就完事了。
当然假如你不确定的话,也不用一个一个试,写个Kotlin代码,循环一遍,看看结果就明白了。循环第三遍的时候,就会显示出正确的密文。
代码语言:javascript复制fun tryDecrypt() {
val tryFunc = fun(n: Int) {
val s =
"Zh#kdyh#ehhq#zrunlqj#552:#rq#wkh#ylghr#iru#wkh#iluvw#hslvrgh#ri#wkh#SksVwrup#HDS1#Li#zh#jdyh#|rx#d#foxh/#lw#zrxog#eh#hdv|#dv#sl1"
for (c in s) {
print(c - n)
}
println()
}
(1..3).forEach { tryFunc(it) }
}
密文如下。
We have been working 22/7 on the video for the first episode of the PhpStorm EAP. If we gave you a clue, it would be easy as pi.
线索三:YouTube视频
好吧,我虽然英语过了4级,但是对于上面简单一句话还是没明白到底啥意思。不过最后还是干脆求助谷歌,谷歌倒是远远比我了解Jetbrains,第一个结果就是YouTube上PhpStorm EAP版的介绍视频。
在视频的3分14秒,视频画面发生了一点抖动,有大概一秒钟的时间,显示了另外一个地址。值得一提的是,这个视频早在2月份就发布了,也就是说Jetbrains早早的就在产品中埋下了彩蛋。
访问这个地址,发现这是一个限时一分钟的问答题。感觉有点像B站注册会员时的答题一样。问题的答案其实就藏在Jetbrains的20周年年度报告里,注意以下一些重要信息即可:
- 创始人图片
- Jetbrains雇员数目
- Jetbrains主要工作国家
- 接受额外基金支持
- 最新推出的Jetbrains产品
- 增长最快的国家和地区
其实就算回答错了也没关系,反正不限制次数,可以随时答题,而且总共五道题,记住正确答案多试几次也可以尝试出来。
全部选对之后,就会显示出最后一个提示,告诉我们谜题隐藏在某个版本的Jetbrains IDEA社区版的每日提示中,但是需要先知道版本号才能下载安装。而构建号就藏在第二段话当中。这第二段话,我对着有道词典查了大半天,最后扔到谷歌翻译里面也没看懂到底讲了啥意思。
但是注意到每个单词的首字母都大写了,于是尝试把首字母连起来。就可以得到下面一段话。
.Net day call for speakers blog post image hides the next clue.
线索四:.NET博客
谷歌一下,就可以发现这指的是一篇Jetbrains的 .NET博客,标题是call for speakers,在博客中果然找到了一个图片。图片上没有发现任何问题,所以再次查看一下网页源代码。
这下终于看到了,在图片的alt属性上,写了我们要寻找的版本号是201_6303。
线索五:Jetbrains社区版每日提示
好了,现在有了版本号,返回到上面一条线索中,线索提示了我们到Confluence网站上寻找Jetbrains社区版。不过其实也不用这么麻烦的去寻找,因为由于Jetbrains Quest热度的关系,这个版本号已经被顶到了热度第一的位置。
点进去下载安装软件即可,这里我懒得安装了,就直接介绍最后一条提示吧。
JETBRAINS QUEST: LAST PUZZLE You have discovered our JetBrains Quest! If you don’t know what this is, you should start from the beginning. This is it. The last puzzle. You are just one step away from glory! Now you just need the Key to unlock the Quest page. The Key is the first and last 4 digits of the 50 * 10^6 position of the Fibonacci sequence (F(50 Million)). As you may know by now, not all that glitters is gold, and to solve this puzzle you should not go straight for the obvious answer. May you make a glorious choice. Remember that you have until the 15th of March 12:00 CEST.
好吧,最后这个问题我是真的不会解了。他问的是斐波那契数列的第五千万项的前四位数字和最后4位数字。这个数字超乎想象大的大,远远大于宇宙中所有原子的数目,数量级大约是10的一千万次方。普通的暴力方法完全不管用。所以想偷懒的同学可以直接在Wolfram Alpha里面计算fib(5e7)
以及fib(5e7)mod10000
,即可得到前后4位数字。
不过这个数字虽大,难道就真的无法计算了吗?其实也不是不可以,因为这个数字保存下来也就10兆左右的大小,还是在可计算范围之内的,不过要使用非常高级的数学技巧。我搜了一下找到了这篇博客,里面提到了一个论文里面的分治算法。
使用它可以非常轻松的计算得到斐波那契的第五千万项的内容,在我电脑上仅需35秒即可得到结果,简直是太牛逼了。
代码语言:javascript复制// https://www.maplesoft.com/applications/download.aspx?SF=154362/fibonaccinumbers.pdf
// duplication formula
// f(2n) = f(n)^2 2f(n-1)f(n)
// f(2n-1)= f(n)^2 f(n-1)^2
fun fibonacci2(n: Int, cache: HashMap<Int, BigInteger>): BigInteger {
if (n <= 1) {
return BigInteger.valueOf(n.toLong())
}
return if (n % 2 == 0) {
val b = cache.getOrPut(n / 2, { fibonacci2(n / 2, cache) })
val a = cache.getOrPut(n / 2 - 1, { fibonacci2(n / 2 - 1, cache) })
b * b BigInteger.TWO * b * a
} else {
val b = cache.getOrPut((n 1) / 2, { fibonacci2((n 1) / 2, cache) })
val a = cache.getOrPut((n - 1) / 2, { fibonacci2((n - 1) / 2, cache) })
a * a b * b
}
}
fun main() {
val profile = fun(task: () -> Unit) {
val t1 = LocalDateTime.now()
task()
val t2 = LocalDateTime.now()
println(Duration.between(t1, t2))
}
val c = HashMap<Int, BigInteger>()
val n = 5e7.toInt()
//profile { println(fibonacci1(n)) }
profile { println(fibonacci2(n, c)) }
}
不管哪种方法,都可以得到最终结果,答案就是46023125,这就是最终的代码。使用它即可获取Jetbrains全家桶的八折优惠券。