7.4 计算一个数字的哈希值

    我们需要恰当地定义hash()方法。关于数值类型哈希值计算,也可参见Python标准库(Python Standard Library)中的4.4.4节部分。那部分定义了一个hash_fraction()函数,是我们所推荐的一种做法。下面是我们的一种做法。

      def hash( self ):
        P = sys.hashinfo.modulus
        m, n = self.value, self.scale
        # Remove common factors of P. (Unnecessary if m and n already
    coprime.)
        while m % P == n % P == 0:
          m, n = m // P, n // P
        if n % P == 0:
          hash
    = sys.hashinfo.inf
        else:
          # Fermat's Little Theorem: pow(n, P-1, P) is 1, so
          # pow(n, P-2, P) gives the inverse of n modulo P.
          
    hash = (abs(m) % P) * pow(n, P - 2, P) % P
        if m < 0:
          hash = -hash
        if hash == -1:
          hash
    = -2
        return hash_

    它将有理分式的两部分化简为一部分,就是标准的哈希值。与参考文档的实现相比,这段代码做了一些修改。要强调的是计算过程的核心部分,分子乘以分母的倒数。实际上,也就是分子除以分母,即mod P。我们可以针对具体问题对此进行优化。

    首先,我们可以(而且应该)修改new()方法来确保比值为非0,消除对sys.hasinfo.inf的依赖。其次,应显式限制比例因数的取值范围要比sys.hash_info.modulus(对于64位的计算机来说,此值为261-1)小。可以消除对需要删除的常用因数P的依赖。这样一来,散列表达式将为hash = (abs(m) % P) * pow(n , P - 2, P) % P,信号处理和特殊情况中的−1被映射为了−2。

    最终,或许希望对计算出的哈希值提供记忆化功能。这需要一个额外的地方,用来存放仅当第1次哈希值被访问时所求得的计算结果。pow(n , P - 2, P)表达式在使用时显得不够轻量,因此对于不必要的场景通常不用。

    设计更有用的四舍五入方法

    在显示四舍五入的值时,通常会做截断。我们定义了所需的round()trunc()函数,不需要更多的解释说明。这些定义对于抽象基类来说是必需的。然而,这些定义并不足以满足我们的需求。

    在货币的计算过程中,我们通常会编写如下代码。

    >>> price= FixedPoint( 1299, 100 )
    >>> tax_rate= FixedPoint( 725, 1000 )
    >>> price * tax_rate
    FixedPoint(941775,scale=100000)

    然后,我们会以精确到百分位为基准做四舍五入,得到942这个值。将一个数值基于一个新的范围进行四舍五入(和截断)的操作,需要一些方法来做这件事。如下是一个用来完成对特定的范围做四舍五入的方法。

    def round_to( self, new_scale ):
      f = new_scale/self.scale
      return FixedPoint( int(self.value*f+.5), scale=new_scale )

    如下代码可用来重设四舍五入范围。

    >>> price= FixedPoint( 1299, 100 )
    >>> tax_rate= FixedPoint( 725, 1000 )
    >>> tax= price * tax_rate
    >>> tax.round_to(100)
    FixedPoint(942,scale=100)

    以上演示了计算货币所必需的函数的定义。