在本文中,将探讨如何使用C++语言解决一个有趣的编程问题,这个问题来自于2015年12月4日的“Advent of Code”挑战。这个问题要求帮助圣诞老人挖掘一种名为AdventCoins的加密货币,为此,需要找到MD5哈希值,这些哈希值在十六进制表示中以至少五个零开始。输入到MD5哈希的是一个秘密密钥,后面跟着一个十进制数。任务是找到产生这样一个哈希值的最低正整数(没有前导零:1, 2, 3, …)。
例如,如果秘密密钥是"abcdef",答案是609043,因为"abcdef609043"的MD5哈希以五个零开始(000001dbbfa...),并且是产生这种情况的最低数。
当最初阅读这个问题时,感到非常困惑。然后,开始搜索一些资料,以更好地理解这个问题。首先,查看了MD5是什么,维基百科对此有一个很好的解释,它是一种广泛使用的消息摘要算法,产生128位的哈希值。在查看了维基百科上算法的伪代码后,意识到“广泛使用”的定义意味着其他人可能已经实现了这个算法。经过一些研究,发现OpenSSL库有一个MD5的实现。所以,使用Cmake和包管理器Conan包含了OpenSSL库(将在另一篇文章中解释是如何做到的)。现在,可以使用以下代码:
#include <openssl/md5.h>
现在有了md5函数,有两件事要做。首先,必须使它易于使用。由于这个算法是C代码,必须将其适配以便更容易地与std::string一起使用,并使其更易于阅读。因此,写了一个函数std::string getMD5(std::string key),它正是这样做的。可以在这里查看实现,但这不是认为有趣的部分。如果有人想了解更多关于它的信息,会描述它。
最后,必须使用这个函数来解决问题。
size_t result = 0;
while (true) {
const auto str = secretKey + std::to_string(result);
const auto md5Result = getMD5(str);
if (md5Result.substr(0, 5) == "00000") {
break;
}
++result;
}
std::cout << result;
正如在解决方案中看到的,查看md5哈希的前5个字符,并检查它们是否只有0。如果不是这样,那么尝试下一个数字,但如果是这样的话,就可以为圣诞老人提供正确的答案。
惊喜,这是第一部分的相同问题,除了一个“小”细节,必须找到允许找到以6个零开始的第一个哈希的数字。
为了找到解决方案,所要做的就是替换代码中的一行:
// 旧行
if (md5Result.substr(0, 5) == "00000")
// 新行
if (md5Result.substr(0, 6) == "000000")
现在可以编译、运行程序,并等待答案。等待,再等待。因为,是的,加密货币不容易挖掘。例如,在机器上,调试模式下找到第一个问题的结果需要4.12秒,但找到第二部分的解决方案需要146.18秒,超过2分钟!在发布模式下,第一个问题需要3.47秒,第二个问题需要34.24秒。
当然,有办法改进刚才描述的解决方案,运行一些基准测试可能会帮助更快地挖掘,但这不是这篇文章的目的。即使它花了超过2分钟,帮助圣诞老人也是非常令人满意的。
可能会注意到,这篇文章中写的解决方案并没有包括所有的源代码来运行程序,而只是解决这个问题的有趣部分的源代码。如果想看到从头到尾的程序,可以访问GitHub账户,探索完整的解决方案,添加评论或提问。
这里是使用的std方法和容器的列表,强烈建议查看它们的定义:
std::string::substr
std::to_string
openssl/md.h