我必须找到2 ^ 25个随机字符串的SHA256哈希值。然后寻找碰撞(使用生日悖论的最后一个,比如说只有50位的散列)。
我将字符串:hash对存储在dict变量中。然后用值(而不是键)对变量进行排序,然后使用O(n)循环查找碰撞。
问题在于,由于有2 ^ 25个字符串及其2 ^ 25个哈希值,因此dict变量中包含2 ^ 50个值。这是非常耗费资源的。那么,我如何使用有限的内存,比如大约1GB呢?
我已经尝试过:
1.使用6GB交换空间运行它。该程序一夜之间运行,但仍未完成。这基本上比O(n_square)搜索更慢!哈希计算的RAM使用量约为3.2 GB。但在那之后,当涉及到sort命令时,使用的RAM再次开始拍摄!我虽然python排序使用In-Place-Quicksort :(
我只存储了哈希并发现了碰撞。但找不到相应的字符串,因为没有存储它。
我不应该使用数据库等。最多是一个文本文件,但这没有帮助。另外,我对Python很陌生,但不要限制你的答案水平。
PS:这是一项任务。有人声称在一分钟内发现了300MB RAM的冲突。不知道这是不是真的。我已经解决了问题,但答案无法实现! 在工作中,所以现在无法访问代码。将很快添加。
代码:
from Crypto.Hash import SHA256
import os
import random
import string
from operator import itemgetter
def shaa():
trun=[]
clist={}
for i in range(0,33554432):
sha=SHA256.new(str(i)).hexdigest()
sha=int(bin(int(sha,16))[-50:],2)
clist[i]=sha
print 'Hashes done.'
clist=sorted(clist.items(), key=itemgetter(1))
for i in range(0,33554432):
if(clist[i]==clist[i+1]):
#print string[i],string[i+1]
print clist[i]
return 1
return 2
result=2
while(result==2):
result=shaa()
答案 0 :(得分:3)
我会选择这样的事情:
打开16个文件(以二进制模式打开应该没问题;如果所有字符串都具有相同的长度,这将是最简单的)。生成字符串和散列,并根据散列的前4位将它们写入文件。然后单独加载和处理每个文件。这将使内存使用量减少16倍。(当然,只要不用完文件句柄,就可以使用任意数量的文件。每次访问时打开和关闭每个文件都会变得相当慢。)
如果生成字符串和哈希值相对便宜,则甚至不需要这些文件。只需要进行16次传球,并且在每次传球中只保留散列上半球的传球号码。
答案 1 :(得分:2)
解决问题的一种方法是使用很长的位域,以便每个哈希都映射到2^25
位长内存块中的某个位置。
通过Bloom filter或其他概率数据结构来解决此类问题的更好但非100%保证的方式。
布隆过滤器是一种节省空间的概率数据结构,用于测试元素是否是集合的成员。假阳性是可能的,但假阴性不是;即查询返回“内部设置(可能是错误的)”或“绝对不设置”。
Bloom过滤器比其他数据结构具有强大的空间优势,可用于表示集合,例如自平衡二进制搜索树,尝试,哈希表或简单数组或条目的链接列表。
1%错误的布隆过滤器每个元素只需要大约9.6位 - 无论元素的大小如何。
因此,每2 ^ 25个元素9.6位,只需要38.4 MiB的内存。
答案 2 :(得分:1)
我认为这里的关键洞察力 - 我承认有一段时间回避我,直到几个小时后才回到这里 - 是sha256
哈希摘要是它自己的哈希< / em>的。换句话说,您不必进行任何其他散列或集创建。您所要做的就是使用sha256
摘要作为哈希创建自定义哈希表。为了节省空间,请不要存储字符串;只需创建一个位数组(对使用table = numpy.zeros(table_size / bits_per_int + 1, dtype='i')
创建的整数数组中的整数使用移位运算)来检测冲突,然后将冲突中的碰撞字符串保存到字符串中,以便在第二次传递中查找字符串。
table_size
应该是一个很大的素数 - 我选择了一个稍大于2 ** 31的数据,这就是268MB的表 - 因为这会产生很少的新碰撞/误报(由模运算引入)在哈希)。您可以将字符串本身保存到文本文件中,您可以迭代它。
因此,对于任何字符串,要设置的相应位的索引将为index = int(hashlib.sha256('foo').hexdigest(), base=16) % table_size
。然后计算major_index = index / bits_in_int
和minor_index = index % bits_in_int
,在minor_index
上使用shift和bitwise操作来检查并在table[major_index]
处的int中存储正确的位,依此类推。
现在对字符串进行传递。每当字符串生成映射到已设置的位的散列时,将hash:string
对存储在字典中。或者更好的是,存储hash:[string_list]
对,在多次碰撞的情况下将新字符串附加到列表中。现在对于任何碰撞的字符串对(a,b),字典将包含b的散列和值。然后执行第二次遍历字符串,依次散列,并检查每个散列的字典。如果散列位于字典中,并且字符串不在相应列表中,请将该字符串添加到列表中。字典中的一些字符串与真正的碰撞不对应;大多数哈希值的[string_list]
只有一个项目长,而hash:[string_list]
对可能会被丢弃。其他可能是由哈希函数本身产生的真实冲突,而不是模数运算。但是,在那些既有真实也有误报的情况下,你可能仍然会有一些误报。你必须仔细检查结果列表是否有误报。
BasicWolf建议使用布隆过滤器是一个很好的建议,可能会导致一个较小的表。但它增加了许多复杂性;我没打扰。我尝试了从'0\n'
到'33554431\n'
的以换行符结尾的字符串的上述方法,并找到了两个具有54位重叠的哈希值。花了11分钟,最大内存使用量大约为350MB(尽管可能会减少。)我做了一些分析,发现大部分时间花在计算位表的偏移量上。在c中编码可能会提供显着的加速,预先散列和存储哈希以及字符串也会有所帮助。
事实上,我尝试对字符串进行预处理,并使用same name的基于c的扩展模块中的numpy
替换了我的基于bitarray
的基于比特阵列的比特阵列。 。这将运行时间缩短到2分钟以上,同时保持内存配置文件只有350MB左右。
我认为足够接近政府工作。由于这是一项任务,我不会发布代码,但我很乐意提供额外的指示。
答案 3 :(得分:0)
为什么不使用最后50位的字典到字符串?
答案 4 :(得分:0)
例如以10个字符为一组拆分散列。并以这种方式嵌套值,您将进行递归搜索,但它应该更快