CS415 数据结构与算法

Chapter 3 Mathematical Background

| 关于   «  2. 集合与关系   ::   目录   ::   4. 对数  »

3. 杂项数学记号

本模块汇集了若干数学术语和概念的定义,供需要时查阅。

计量单位 : OpenDSA 模块对计量单位使用以下记号。 "B" 用作字节的缩写,"b" 表示位, "KB" 表示千字节 \((2^{10} = 1024\) 字节), "MB" 表示兆字节 \((2^{20}\) 字节), "GB" 表示吉字节 \((2^{30}\) 字节), "ms" 表示毫秒 (1 毫秒是 1/1000 秒)。 当意指 2 的幂时,数字与单位缩写之间不放置空格。 因此,一个容量为 25 吉字节的磁盘驱动器(这里吉字节意指 \(2^{30}\) 字节)将写作 "25GB"。 当意指十进制数值时则使用空格。 因此 2000 位将写作 "2 Kb",而 "2Kb" 表示 2048 位。 2000 毫秒写作 2000 ms。 注意,在本书中,大量存储几乎总是以 2 的幂来度量, 而时间则以 10 的幂来度量。

阶乘函数 : 阶乘 函数,对大于 0 的整数 \(n\) 写作 \(n!\) ,是 1 到 \(n\) (含)之间所有整数的乘积。 于是, \(5! = 1 \cdot 2 \cdot 3 \cdot 4 \cdot 5 = 120\) 。 作为特例, \(0! = 1\) 。 阶乘函数随着 \(n\) 变大而快速增长。 由于直接计算阶乘函数是一个耗时的过程, 拥有一个能给出良好近似值的公式会很有用。 斯特林近似(Stirling's approximation)指出 \(n! \approx \sqrt{2\pi n}(\frac{n}{e})^n\) , 其中 \(e \approx 2.71828\) ( \(e\) 是自然对数系统的底) [1]。 因此我们看到,虽然 \(n!\) 的增长 慢于 \(n^n\) (因为 \(\sqrt{2\pi n}/e^n < 1\) ), 但对于任意正整数常数 \(c\) ,它的增长快于 \(c^n\) 。

排列 : 序列 \(\mathbf{S}\) 的一个 排列 就是把 \(\mathbf{S}\) 的成员按某种顺序排列。 例如,整数 1 到 \(n\) 的一个排列 就是把这些值按某种顺序排列。 如果序列包含 \(n\) 个互不相同的成员, 那么该序列有 \(n!\) 个不同的排列。 这是因为排列的第一个成员有 \(n\) 种选择; 对第一个成员的每种选择,第二个成员有 \(n-1\) 种选择,依此类推。 有时人们希望获得一个序列的 随机排列 , 即从 \(n!\) 个可能的排列中选出一个, 使得每个排列被选中的概率相等。 生成随机排列的一个简单函数如下。 这里,序列的 \(n\) 个值存储在 数组 A 的位置 0 到 \(n-1\) 中, 函数 swap(A, i, j) 交换数组 A 中的元素 i 和 j , 而 Random(n) 返回 0 到 \(n-1\) 范围内的一个整数值。

// Randomly permute the values in array A
public static void permute(Object[] A) {
  for (int i = A.length; i > 0; i--) // for each i
    swap(A, i-1, random(i));         //   swap A[i-1] with a random
}                                    //   position in the range 0 to i-1.
// Randomly permute the values in array A
public static <T> void permute(T[] A) {
  for (int i = A.length; i > 0; i--) { // for each i
    swap(A, i-1, random(i));         //   swap A[i-1] with a random
  }
}                                    //   position in the range 0 to i-1.
//Randomly permute the values in array A
void permute(int A[], int n) {
  for (int i = n; i > 0; i--) // for each i
    swap(A, i-1, int(Random(i)));    //   swap A[i-1] with a random
                                     //   position in the range 0 to i-1.
}

布尔变量 : 一个 布尔变量 是一个取 True 和 False 两个值之一的变量。 这两个值通常分别与值 1 和 0 相关联, 尽管没有理由必须如此。 依赖 0 与 False 之间的对应关系是一种糟糕的编程实践, 因为它们是逻辑上不同、类型也不同的对象。

逻辑记号 : 我们偶尔会用到符号逻辑或布尔逻辑的记号。 \(A \Rightarrow B\) 表示" \(A\) 蕴含 \(B\) "或 "如果 \(A\) 则 \(B\) "。 \(A \Leftrightarrow B\) 表示" \(A\) 当且仅当 \(B\) " 或" \(A\) 等价于 \(B\) "。 \(A \vee B\) 表示" \(A\) 或 \(B\) " (在符号逻辑语境中或执行布尔运算时都很有用)。 \(A \wedge B\) 表示" \(A\) 且 \(B\) "。 \(\sim\!A\) 和 \(\overline{A}\) 都表示"非 \(A\) ", 即 \(A\) 的否定,其中 \(A\) 是一个布尔变量。

向下取整与向上取整 : \(x\) 的 向下取整 (写作 \(\lfloor x \rfloor\) ) 接受实数值 \(x\) 并返回最大整数 \(\leq x\)。 例如, \(\lfloor 3.4 \rfloor = 3\) , \(\lfloor 3.0 \rfloor\) 也是如此, 而 \(\lfloor -3.4 \rfloor = -4\) , \(\lfloor -3.0 \rfloor = -3\) 。 \(x\) 的 向上取整 (写作 \(\lceil x \rceil\) )接受实数值 \(x\) 并返回最小整数 \(\geq x\)。 例如, \(\lceil 3.4 \rceil = 4\) , \(\lceil 4.0 \rceil\) 也是如此, 而 \(\lceil -3.4 \rceil = \lceil -3.0 \rceil = -3\) 。

取模函数 : 取模 (或 模 )函数返回整数除法的余数。 在数学表达式中有时写作 \(n \bmod m\) , 而在许多程序设计语言中语法为 n % m 。 根据余数的定义, \(n \bmod m\) 是满足 \(n = qm + r\) 的整数 \(r\) ,其中 \(q\) 为整数, 且 \(|r| < |m|\) 。 因此,当 \(n\) 和 \(m\) 为正整数时, \(n \bmod m\) 的结果必须介于 0 和 \(m-1\) 之间。 例如, \(5 \bmod 3 = 2\) ; \(25 \bmod 3 = 1\) , \(5 \bmod 7 = 5\) , \(5 \bmod 5 = 0\) 。

给 \(q\) 和 \(r\) 赋值的方式不止一种, 取决于如何解释整数除法。 最常见的数学定义把取模函数计算为 \(n \bmod m = n - m\lfloor n/m\rfloor\) 。 此时, \(-3 \bmod 5 = 2\) 。 然而,Java 和 C++ 编译器通常使用底层处理器的机器指令 来计算整数运算。 在许多计算机上,这是通过截断所得分数来实现的, 即 \(n \bmod m = n - m (\mathrm{trunc}(n/m))\) 。 按此定义, \(-3 \bmod 5 = -3\) 。 另一种语言可能会做不同的事。

遗憾的是,对许多应用来说,这并不是用户想要或期望的。 例如,许多 散列系统 会对记录的 键 值进行某种计算, 然后对散列表长度取模。 这里的期望是结果成为散列表中的合法下标, 而不是一个负数。 散列函数的实现者必须要么确保计算结果始终为正, 要么在取模函数的结果为负时给该结果加上散列表长度。

   «  2. 集合与关系   ::   目录   ::   4. 对数  »

关闭窗口