📜  整数N循环移位另一个整数m(1)

📅  最后修改于: 2023-12-03 15:10:23.014000             🧑  作者: Mango

整数N循环移位另一个整数M

在计算机程序中,我们经常需要对数值进行位移操作。其中,循环移位是一种比较常见的操作方式。循环移位是指将一个数值(通常为整数)的二进制表示向左或向右移动若干位,将溢出的部分重新移回到该数值的另一端。这个过程就像是将一个珠子围绕着一条环形的链子上滑动。本文将介绍如何对一个整数进行循环移位。

循环移位的实现

下面给出一个将一个整数 $m$ 循环右移 $n$ 位的实现。这个函数先将 $m$ 向右移动 $n$ 位,然后再将溢出的 $n$ 位移动到 $m$ 的最高 $n$ 位的位置。具体来说,如果 $m$ 用二进制表示为:

$m = b_{n-1} b_{n-2} \dots b_1 b_0$

则循环右移 $n$ 位后的结果为:

$m' = b_{n-1} b_{n-2} \dots b_{n-k} b_{n-k-1} \dots b_1 b_0$

其中,$k$ 是 $n$ 对 $m$ 二进制表示的长度取模的结果,也就是 $n$ 除以 $m$ 的二进制表示的位数的余数。

def ror(m: int, n: int) -> int:
    """将整数 m 循环右移 n 位"""
    bits = m.bit_length()  # 求出 m 的二进制表示的位数
    mask = (1 << n) - 1  # 求出一个低 n 位均为 1 的掩码
    return (m >> n) | ((m & mask) << (bits - n))

这个函数的实现思路比较简单。首先,我们通过 m.bit_length() 计算出 $m$ 的二进制表示的位数 bits。然后,我们利用一个掩码 (1 << n) - 1 来得到低 $n$ 位均为 $1$,高位均为 $0$ 的二进制数。我们可以将这个掩码与 $m$ 处理一下,得到 $m$ 的低 $n$ 位。最后,我们将 $m$ 先向右移动 $n$ 位,在将低 $n$ 位移动到 $m$ 的最高 $n$ 位的位置即可。

循环移位的应用

循环移位是一种非常灵活的操作方式,并且在计算机程序中有着广泛的应用。下面列举几个例子:

字符串哈希

字符串哈希是指将一个字符串映射成一个整数。字符串哈希的设计思路有很多种,其中一种比较常见的方法是将字符串中的每个字符都映射成一个整数,并将这些整数相加。如果两个字符串的哈希值相同,则这两个字符串相同的概率非常低。

例如,给定一个字符串 $s$,我们可以计算出它的哈希值:

def hash_string(s: str) -> int:
    """将字符串 s 的哈希值计算出来"""
    h = 0
    for c in s:
        h = (h << 5) + ord(c)  # 每次将 h 向左移动 5 位,并加上新的字符的 ASCII 码值
    return h

这个哈希函数的实现很简单,它假设字符串只包含 ASCII 码中的可打印字符,并将每个字符的 ASCII 码值左移 5 位,然后相加。有趣的是,这个哈希函数使用了循环移位操作。具体来说,每次我们都将当前的 $h$ 向左移动 $5$ 位,并使用循环移位的方式将加上新的字符的 ASCII 码值。这个哈希函数的性质很好,哈希碰撞比较少,而且哈希值很难预测。

旋转数组

旋转数组是指将一个数组的元素顺序向右循环移动 $k$ 个位置。例如,如果数组原来的值是 $a_0, a_1, \dots, a_{n-1}$,则旋转后的数组的值为:

$a_{n-k}, a_{n-k+1}, \dots, a_{n-1}, a_0, a_1, \dots, a_{n-k-1}$

旋转数组的实现非常简单。我们可以使用循环右移的函数对数组进行操作,也可以使用 Python 的 “切片” 运算符来实现。

def rotate_array(a: List[int], k: int) -> None:
    """将数组 a 向右循环移动 k 个位置"""
    n = len(a)
    if n <= 1 or k % n == 0:
        return
    k %= n
    a[:k], a[k:] = a[n-k:], a[:n-k]

a = [1, 2, 3, 4, 5, 6, 7]
rotate_array(a, 3)
print(a)  # 输出 [5, 6, 7, 1, 2, 3, 4]

这个函数的实现比较简单。首先,我们将 $k$ 对 $n$ 取模,这样可以得到一个小于 $n$ 的非负整数。然后,我们可以使用 Python 的 “切片” 运算符将数组拆分成两部分,并将它们交换位置即可。如果数组的长度小于等于 $1$,或者 $k$ 等于 $0$ 或者 $n$,则不需要做任何操作。