【问题标题】:Interview : Hash function: sine function采访:哈希函数:正弦函数
【发布时间】:2012-10-10 21:36:44
【问题描述】:

有人问我这个面试问题。我不确定它的正确答案是什么(以及答案背后的原因):

sin(x) 是一个好的散列函数吗?

【问题讨论】:

  • 不,不是。它实际上是一个非常糟糕的哈希函数,就像所有周期性函数一样......
  • @ArmenTsirunyan 这不取决于你在散列什么吗?当然,在整数上使用 sin(至少在无符号的情况下)会产生独特的结果。
  • 什么是sine()? C 标准只定义了sin(),但没有定义sine()
  • @AlexeyFrunze:好的..更正了。
  • @AlexeyFrunze 无需居高临下,如果您发表评论,您显然知道这是什么意思,如果您愿意,您有足够多的代表进入并更改它。否则,从技术上讲,SINE 这个词是 C 中 sin() 函数的正确数学术语。这些术语是类似的......

标签: c trigonometry


【解决方案1】:

如果你的意思是sin(),它不是一个好的散列函数,因为:

  • 这是完全可以预测的,对于某些x 来说,它并不比x 本身更好。密钥和密钥的哈希之间不应存在看似明显的关系。
  • 它不产生整数值。您不能使用浮点索引对数组进行索引/下标,并且哈希表中必须存在某种数组。
  • 浮点数是非常特定于实现的,即使您使用 sin() 制作散列函数,它也可能不适用于不同的编译器或不同类型的 CPU/计算机。
  • sin() 可能比一些更简单的整数算术函数慢得多。

【讨论】:

  • 为什么可预测性是一件坏事?
  • @stefan 你想避免冲突,并且哈希值的分布几乎均匀。键。使用非常简单(可预测)的哈希函数,您很可能会产生明显偏斜的分布并遇到冲突,这两者都会降低哈希表的性能。
  • @stefan:Hash table implementations vulnerable to algorithmic complexity attacks 提供了一个实例,其中可预测性可能是哈希函数的一个缺点。
  • 我不同意。带有碰撞的散列确实很糟糕,但导致这个问题的不是可预测性,而是 sin/cos 的周期性行为。定义可预测性 ;-)
  • @stefan 也许这不是最好的措辞。散列函数的质量不仅仅由散列函数决定,它在上下文中由输入决定。尽管sin(x) 具有周期性,但如果x 本身相当随机,则适用于散列的sin(x) 可能会正常工作。
【解决方案2】:

不是真的。

  1. 速度太慢了。
  2. 无论如何,您都需要将结果转换为某种整数类型,以避免浮点相等比较的疯狂。 (实际上并不是 FP 等式比较特有的通常精度问题,它是由计算两种稍微不同的方式引起的;我的意思是特别是由诸如 387 派生的 FPU 存储额外它们的寄存器中的精度位,因此如果在寄存器中的两个新计算的值之间进行比较,则与将其中一个操作数从内存加载到寄存器中时相比,您可能会得到不同的答案。)
  3. 在波峰和波谷附近几乎是平坦的,因此量化步骤(乘以某个大数并舍入为整数)将在最小值和最大值附近产生许多哈希值,而不是均匀分布。

【讨论】:

    【解决方案3】:

    基于数学知识:

    Sine(x) 是周期性的,所以它会从不同的 x 值达到相同的数字,所以 Sine(x) 作为散列函数会很糟糕,因为你会得到多个散列到完全相同的点的值。返回值在 0 和 pi 之间有 ** 无穷多个值,但超过该值将重复。所以 0 & pi & 2*pi 都会散列到同一个点。

    如果您可以使增量足够小,并将 Sine(x) 乘以 x^2 或类似性质的东西,那么它充其量只是平庸的,但话又说回来,如果您要这样做,为什么不直接使用无论如何,x^2 并把周期函数一起扔掉。

    **infinitely:一个足够大的数字,我不愿意数。

    注意:Sine(x) 的值很小,可能会受到舍入误差的影响。

    注意:任何取自正弦函数的值都应乘以一个整数,然后进行模数或取地板或天花板,以便该值可用作数组偏移量等。

    【讨论】:

    • 好吧,周期性问题在应用于单个字节时不会出现。
    • "0 & pi & 2*pi" 所有哈希值都相同吗?代数上是的,但是到目前为止我使用的每种计算机语言都只有近似 pi 导致 sin(0) != sin(about_pi) != sin(2*about_pi)。
    【解决方案4】:

    sin(x) 是三角函数,它每 360 度重复一次,所以它会是一个糟糕的哈希函数,因为哈希会重复太频繁。

    一个简单的反驳:

    sin(0) == sin(360) == sin(720) == sin(..)
    

    这不是 goodhash 函数的属性。

    即使你决定使用它,也很难表示 sin 返回的值。 正弦函数:

    sin x = x - x^3/3! + x^5/5! - ...
    

    由于浮点精度问题,这无法准确表示,这意味着对于相同的值,它可能会产生两个不同的哈希值!

    【讨论】:

    • 好吧,当它应用于单个字节时不会出现这个问题。
    【解决方案5】:

    还有一点需要注意:

    对于作为散列函数的 sine(x) - 给定近距离范围内的键也将具有近距离散列值,这是不可取的。一个好的散列函数可以均匀地分布散列值,而不管键的性质如何。

    【讨论】:

      【解决方案6】:

      哈希值通常必须是整数才能有用。由于sin 不生成整数,因此不合适。

      【讨论】:

        【解决方案7】:

        假设我们有一个字符串 s。它可以表示为十六进制的数字并提供给函数。如果您添加 2 pi,它将不再是有效输入,因为它不再是整数(该函数只接受非负整数)。您必须找到一个会产生冲突的字符串,而不仅仅是将字符串的十六进制表达式乘以 2 pi。并且将(连接?) 2 pi 直接添加到字符串不会有助于发现冲突。虽然可能还有另一种方法,但不是那么简单。

        【讨论】:

          【解决方案8】:

          我认为 sin(x) 可以做出出色的加密哈希函数, 如果使用得当。输入应该是弧度的自然数 并且从不包含 pi。我们必须使用任意精度的算术。 对于每个自然数 x(弧度),sin(x) 总是一个超越的无理数,没有其他的 具有相同正弦的自然数。但有一个问题:攻击者可以获得 关于输入的信息,通过计算哈希的反正弦值。 为了防止这种情况,我们忽略了小数部分和一些 小数部分的第一个数字,只保留接下来的 n(比如 100)个数字, 使这种攻击在计算上不可行。 似乎输入的微小变化会产生完全不同的结果, 这是一个理想的属性。 该函数的结果在统计上似乎是随机的,这也是一个很好的属性。 我不确定如何证明它是抗碰撞的,但我不明白为什么 不可能。另外,我想不出一种方法来找到产生的特定输入 在特定的哈希中。我并不是说我们应该盲目地相信它是 当然是一个很好的地穴。哈希函数。我只是觉得它看起来像一个 成为其中一员的好人选。我们应该给它一个机会 并专注于证明它是。这对我来说可能是一个非常好的。 对于那些可能会说它很慢的人:是的,确实如此。这在散列密码时很好。 在这里,我为这个想法附上了一些 perl 代码。它使用 bash 和 bc 在 linux 上运行。 (bc 是一个命令行任意精度计算器,包含在大多数发行版中) 我将检查此页面以获取任何答案,因为这让我很感兴趣。 不过不要苛刻,我只是一名CS本科生,愿意学习更多。

          use warnings;
          use strict;
          my $input='5AFF36B7';#Input for bc (as a hex number)
          $input='1'.$input;#put '1' in front of input, so that 0x0 , 0x00 , 0x1 , 0x01 , etc ... ,
                            #all give different nonzero results
          
          my $a=`bc -l -q <<< "scale=256;obase=16;ibase=16;s($input)"`;#call bc, keep result in $a
          
          #keep only fractional part
          $a=~tr/a-zA-Z0-9//cd;#Clean up string, keep only alphanumerics
          my @m = $a =~ /./g;#Convert string to array of chars
          
          #PRINT OUTPUT
          #We ignore some digits, for security reasons:
          #If we don't ignore any of the first digits, an attacker could gain information
          #about the input by computing the inverse of sin (the arcsin of the hash)
          #By ignoring enough of the first digits, it becomes computationally
          #infeasible to compute arcsin
          #Also, to avoid problems with roundoff error, we ignore some of the last digits
          for (my $c=100;$c<200;$c++){
              print $m[$c];
          }
          

          【讨论】:

          • 它很容易受到冲突的影响:在明文中添加 2 pi 并繁荣,散列破碎
          猜你喜欢
          • 2016-03-25
          • 2011-02-27
          • 2011-08-04
          • 2011-07-10
          • 2022-01-03
          • 2011-02-04
          • 2019-07-24
          • 2010-10-13
          • 2012-11-06
          相关资源
          最近更新 更多