我想创建一个接受字符串并返回介于0和1之间的数字的函数。当给定相同的字符串时,该函数应始终返回相同的数字,但除此之外,结果应该没有可辨别的模式。任何大量输入字符串的输出数字都应遵循统一分布。
此外,我需要生成多个这样的函数,即当给定字符串“abc”时,函数A可能始终返回0.593927,而函数B始终返回0.0162524。我需要它快速(这是一个数值模拟),并有相当不错的统计数据。
我正在使用Python,并且愿意接受以下形式的答案:“这是使用Python库的简单方法”或“这是一个可以实现的算法”。如果在Python中没有快速的方法,我只需改用C语言。
我意识到以下两种方法中的任何一种都可以使用,但每种方法都有缺点,这使我想要寻找更优雅的解决方案。
存储字典
我每次给一个新字符串时都可以计算一个新的随机数,如果我再次收到相同的字符串,则将其存储在字典中以便检索。 然而,我的应用程序可能会生成很多只出现一次的字符串,这最终会导致必须在内存中存储一个非常大的字典。它也使重复性变得更加困难,因为即使我使用相同的种子,如果我以不同的顺序接收相同的字符串,我将生成不同的函数。出于这些原因,一致地“随时”计算随机数会好得多。
使用哈希函数
我可以在字符串上调用哈希函数,然后将结果转换为数字。生成多个函数的问题可以通过例如向每个输入字符串附加“种子”字符串来解决。 然而,然后我一直试图找到具有适当速度和统计数据的哈希函数。 Python的内置哈希是快速但依赖于实现的,我不知道统计数据有多好,因为它不是为此类目的而设计的。另一方面,我可以使用安全散列算法,如md5,它将具有良好的统计数据,但这对我的应用程序来说太慢了。针对数据存储应用程序的哈希函数通常比像md5这样的加密安全函数快得多,但它们的设计目的是避免冲突,而不是产生均匀分布的输出,并且这些在所有情况下都不一定相同。 / p>
关于哈希函数的进一步说明
为了说明避免冲突和产生统一结果是不同的事情,请考虑使用Python的内置哈希函数的以下示例:
>>> hash("aaa") % 1000
340
>>> hash("aab") % 1000
343
>>> hash("aac") % 1000
342
>>> hash("aad") % 1000
337
>>> hash("aae") % 1000
336
>>> hash("aaf") % 1000
339
>>> hash("aag") % 1000
338
>>> hash("aah") % 1000
349
>>> hash("aai") % 1000
348
>>> hash("aaj") % 1000
351
>>> hash("aak") % 1000
350
上述输出中没有碰撞,但它们也明显不均匀分布,因为它们都在336和351之间,并且在第三个数字中也有一个明确的模式。我意识到我可以通过(hash("aaa")/HASH_MAX)*1000
得到更好的统计数据(假设我可以解决HASH_MAX
应该是什么),但这应该有助于说明良好的哈希函数的要求与我正在寻找的功能要求。
有关此问题的一些相关信息
我不确切知道该算法需要处理的字符串是什么,因为字符串将由模拟生成,但以下情况可能就是这样:
他们的字符集非常有限(可能只有4或5个不同的符号)。
会有很多独特或罕见的字符串和一些非常常见的字符串,长度各不相同。
字符串的长度没有上限,但短字符串可能比长字符串更常见。如果我从未看到超过100个字符的人,我不会感到惊讶,但我不确定。其中许多只有一到三个字符,因此对于短字符串来说算法很快很重要。 (但我想我可以使用查找表来查找小于一定长度的字符串。)
通常,字符串将具有相同的大子字符串 - 通常两个字符串的区别仅在于添加到开头或结尾的单个字符。当字符串相似时,算法不倾向于给出类似的输出值。
答案 0 :(得分:3)
使用一个好的随机数生成器并用字符串播种它。
答案 1 :(得分:1)
在universal hashing上的维基百科文章的“哈希字符串”一节中有一个算法。
或者,你可以使用一些内置的哈希函数;每个随机函数都会在散列之前为字符串添加一个随机(但固定)的前缀。
答案 2 :(得分:1)
Lookup3被认为具有非常好的碰撞特性,这应该意味着结果的均匀分布,并且它也很快。将它放在Python扩展中应该很简单。
更一般地说,如果你找到一个能很好地减少哈希表冲突的函数并且具有你需要的速度属性,那么从32位或64位整数到浮点数的最终转换就是所需要的。网络和其他地方有很多来源的字符串散列函数。首先检查Knuth。
<强>加成强>
可能值得尝试的另一件事是首先使用像RC4这样的快速1-1算法加密字符串(不安全,但仍然足够接近伪随机),然后运行一个简单的哈希(h = h + a * c [i] + b)通过密文。 RC4键是uniquifier。
答案 3 :(得分:1)
尝试使用指纹,如拉宾指纹识别 http://en.wikipedia.org/wiki/Fingerprint_(computing)。
如果选择N位指纹,则只需将结果除以2 ^ N.
指纹是一种散列函数,通常对计算机来说非常快(与MD5相比Cryptographic hash functions)但对加密应用程序不利(键值可以使用指纹以某种方式恢复)