简介:本源代码库为C++编写的实现大整数乘法的程序,涵盖多种算法如Karatsuba、Toom-Cook和基于FFT的算法。程序利用字符串或自定义结构体来模拟大整数运算,适用于加密算法、数论计算、分布式计算、金融计算和科学计算等场景。在使用前,需要进行编译和测试以确保其正确性和性能。
1. 大整数乘法算法概述
随着数字加密、密码学和图形学的发展,大整数乘法在IT行业中变得越来越重要。这类运算通常超出传统编程语言内置整数类型的处理能力,因此需要特殊的大数算法来完成。本章将介绍大整数乘法算法的基本概念及其重要性,并概述后续章节内容,为深入研究算法提供基础。
大整数乘法算法通常指的是对两个大于普通整数类型可以表示的数值进行乘法运算的方法。这些算法通过分割和重组数字,利用分治策略,以减少所需的计算量和存储空间。它们的核心在于找到算法效率和实现复杂度之间的最佳平衡点。
本章将为读者提供一个对大整数乘法算法的宏观认识,为接下来章节对算法细节的深入探索打下基础。后续章节将逐一探讨C++中的大整数表示方法、著名的Karatsuba和Toom-Cook算法原理及其实现,以及快速傅里叶变换(FFT)在大整数乘法中的应用,最后总结C++实现大整数乘法的要点和应用场景。
2. C++中大整数表示方法
2.1 大整数在C++中的基本表示
2.1.1 基于字符串的表示法
在C++中,处理大整数最直接的方法之一是使用字符串表示。这种方法利用了C++标准库中的字符串类型(如 std::string
或 std::wstring
),将大整数的每一位数字存储在字符串的每一个字符中。字符串表示法的优点在于它的直观性和易用性,它允许开发者通过字符串操作函数来进行大整数的基本运算,例如位加法和位移操作。然而,这种方法的缺点是效率较低,因为它需要进行频繁的字符转换和字符串操作,特别是在进行乘法运算时,需要将每一位字符转换成整数,执行乘法后又转换回字符串表示。
#include <iostream>
#include <string>
// 字符串表示法实现大整数加法
std::string bigIntegerAdd(const std::string &num1, const std::string &num2) {
// 从两个数的最低位开始逐位相加
std::string result = "";
int carry = 0; // 进位
int i = num1.size() - 1, j = num2.size() - 1;
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) sum += num1[i--] - '0'; // 将字符数字转换为整数进行计算
if (j >= 0) sum += num2[j--] - '0';
carry = sum / 10; // 计算新的进位
result.push_back(sum % 10 + '0'); // 转换回字符并存储在结果字符串中
}
// 因为是从最低位开始的,所以需要反转结果字符串
std::reverse(result.begin(), result.end());
return result;
}
在上述代码中, bigIntegerAdd
函数展示了如何使用字符串来实现大整数的加法。函数逐位将两个数字相加,包括进位的处理,最后将得到的字符串反转得到正确的结果。由于使用了 std::string
,所以不需要手动管理内存分配和释放。
2.1.2 基于数组的表示法
基于数组的表示法是另一种常见的大整数表示方式。在这种方法中,一个整数被表示为一个整数数组,数组中的每个元素存储原整数的一部分。例如,对于一个n位的整数,可以将其表示为一个长度为n的数组,每个数组元素存储一个位上的数字。这种方法的优点是相比于基于字符串的表示,数组表示在进行算术运算时,特别是乘法,更加高效,因为它可以利用CPU的原生字节操作。但其缺点是需要额外的逻辑来处理进位。
#include <iostream>
#include <vector>
// 数组表示法实现大整数乘法
std::vector<int> bigIntegerMultiply(std::vector<int> num1, int num2) {
std::vector<int> result(num1.size() + 1, 0);
for (int i = num1.size() - 1; i >= 0; i--) {
int carry = 0;
int product = 0;
for (int j = 0; j <= i; j++) {
product = num1[i - j] * num2 + result[j] + carry;
result[j] = product % 10;
carry = product / 10;
}
result[i + 1] = carry;
}
// 移除结果前导零
while (result.size() > 1 && result.back() == 0) {
result.pop_back();
}
return result;
}
在上述代码中, bigIntegerMultiply
函数展示了如何使用数组(这里以 std::vector<int>
为示例)来实现大整数的乘法。函数从最低位开始进行逐位乘法,同时处理进位,直到完成所有位的乘法运算。结果以数组的形式返回,最高位数字存储在数组的第一个位置。
接下来,让我们探讨一下在表示大整数时所采用的一些优化方法。
3. Karatsuba算法介绍
Karatsuba算法是一种快速的乘法算法,由Anatolii Alexeevitch Karatsuba于1960年发明。它基于分治策略,能够在较低的时间复杂度内完成大整数的乘法运算。在本章节中,我们将详细介绍Karatsuba算法的原理、实现步骤以及性能分析。
3.1 Karatsuba算法原理
3.1.1 算法的数学基础
Karatsuba算法的数学基础来源于大整数乘法的分治策略。传统的乘法算法,如小学所学的竖式乘法,其时间复杂度为O(n^2),其中n是整数的位数。Karatsuba算法通过将大整数分成较小的部分,然后应用分治策略,将时间复杂度降低到了O(n^log2(3)),即O(n^1.585)。
假设我们有两个n位的大整数X和Y,可以将它们分别表示为:
X = A * 10^(n/2) + B
Y = C * 10^(n/2) + D
其中A和C是前半部分,B和D是后半部分。传统的乘法算法需要计算以下四个乘积:
AC, AD, BC, BD
然后将它们组合起来得到最终结果:
XY = AC * 10^n + (AD + BC) * 10^(n/2) + BD
3.1.2 分治策略的应用
Karatsuba算法的核心思想是减少乘法的次数。通过巧妙的数学变换,它只需要计算三个乘积:
AC, (A+B)(C+D), BD
然后通过这三个乘积来得到最终的结果。具体来说,我们可以得到:
XY = (AC) * 10^n + [(A+B)(C+D) - AC - BD] * 10^(n/2) + BD
这样,我们就将原本需要四次乘法的问题简化为三次乘法和几次加法与减法。随着n的增大,这种方法相比传统的乘法算法能够显著减少计算量。
3.2 Karatsuba算法的实现步骤
3.2.1 分割与递归乘法
在实现Karatsuba算法时,我们首先需要将大整数分割为更小的部分。例如,对于32位的整数,我们可以将其分为两个16位的部分。然后,我们将递归地应用Karatsuba算法来计算这些部分的乘积。
以下是Karatsuba算法的伪代码:
function karatsuba(x, y):
// x 和 y 是需要相乘的两个大整数
if x < 10 or y < 10:
return x * y
else:
// 分割大整数
m = min(size_base(x), size_base(y))
m2 = m / 2
high1, low1 = split_at(x, m2)
high2, low2 = split_at(y, m2)
// 递归计算乘积
z0 = karatsuba(low1, low2)
z1 = karatsuba((low1 + high1), (low2 + high2))
z2 = karatsuba(high1, high2)
return (z2 * 10^(2*m2)) + ((z1 - z2 - z0) * 10^(m2)) + z0
3.2.2 合并中间结果
在递归地计算出z0, z1, z2之后,我们需要将它们合并成最终的结果。这个步骤主要涉及到加法和乘法操作。
以下是合并中间结果的伪代码:
function merge(z0, z1, z2, m2):
return (z2 * 10^(2*m2)) + ((z1 - z2 - z0) * 10^(m2)) + z0
3.3 Karatsuba算法的性能分析
3.3.1 时间复杂度评估
Karatsuba算法的时间复杂度为O(n^1.585)。这是因为每一层递归中,我们只需要进行三次乘法和两次加法操作。虽然每次递归都会减少问题的规模,但递归的层数却增加了。通过数学分析,可以得到时间复杂度为O(n^log2(3))。
3.3.2 空间复杂度评估
Karatsuba算法的空间复杂度为O(n),这是因为我们需要存储中间结果和递归调用栈的空间。每次递归调用,我们都需要存储四个临时变量z0, z1, z2, m2,它们的总大小与输入大小成正比。
通过本章节的介绍,我们了解了Karatsuba算法的原理和实现步骤,并对其性能进行了分析。Karatsuba算法是一种非常实用的算法,尤其适用于大整数乘法的场景。在接下来的章节中,我们将介绍另一种高效的算法——Toom-Cook算法。
4. Toom-Cook算法介绍
在处理大整数运算时,Toom-Cook算法是一个高效的乘法算法,它将大整数分成多个较小的部分,通过多项式乘法转换为多项式卷积问题,利用快速傅里叶变换(FFT)进行加速,从而提高运算效率。接下来我们将详细介绍Toom-Cook算法的原理、实现步骤以及性能分析。
4.1 Toom-Cook算法原理
4.1.1 算法的基本思想
Toom-Cook算法是一种分治算法,其核心思想是将大整数分割为几个较小的部分,然后使用多项式乘法代替整数乘法。算法通过多项式的系数相乘来表示大整数的乘法,转换为多项式乘法问题。这种转换是通过选择适当的多项式和分割策略来实现的,从而在计算上更加高效。
4.1.2 多项式乘法的转换
将大整数分割后,Toom-Cook算法将整数乘法转换为多项式乘法。假设我们有两个多项式A(x)和B(x),它们的乘积C(x)可以通过计算系数相乘得到。具体来说,如果A(x)和B(x)都是k-1次多项式,那么C(x)将是一个2k-1次多项式。这样,原本需要进行k^2次的乘法运算,现在只需要进行(k+1)(k/2)次,有效减少了乘法的次数。
4.2 Toom-Cook算法的实现步骤
4.2.1 多项式的选取和分割
Toom-Cook算法的第一步是确定多项式的选取和分割策略。在实际应用中,多项式的选取一般根据大整数的长度来决定。分割策略包括了分割的位置以及分割的段数,这需要算法根据输入的大整数动态计算和选择。
// 以下为Toom-Cook算法的分割策略示例代码
void splitPolynomial(int *poly, int size) {
// 分割大整数为多项式系数
// poly是系数数组,size是大整数长度
// 代码实现需要根据具体多项式选择策略进行分割
}
4.2.2 递归乘法与结果恢复
分割后,我们需要对每一对多项式的系数进行乘法运算。Toom-Cook算法是一个递归算法,它在递归过程中逐步降低多项式的次数。最终,在递归的最底层,我们执行小整数的乘法运算。递归结束后,通过一系列数学操作将中间结果合并起来,恢复出最终的乘积。
// 以下为Toom-Cook算法递归乘法与结果恢复示例代码
int toomCookMultiply(int *a, int *b, int size) {
// 递归进行多项式乘法
// a 和 b 是分割后的系数数组
// size 是系数的数量
// 代码实现需要包含递归运算以及合并中间结果
}
4.3 Toom-Cook算法的性能分析
4.3.1 与Karatsuba算法的比较
Toom-Cook算法与Karatsuba算法在某些方面有相似之处,都是通过减少乘法的次数来提高效率。然而,Toom-Cook算法在多项式的选取和分割策略上更加灵活。通常情况下,Toom-Cook算法适用于更大的整数乘法,其性能也更优。
4.3.2 实际应用中的性能表现
在实际应用中,Toom-Cook算法的性能表现受到多种因素的影响,包括大整数的长度、计算机的硬件资源以及算法实现的优化程度等。在处理非常大的整数乘法时,Toom-Cook算法能够提供更快的运算速度,尤其是在经过适当的优化后。
graph TD;
A[开始大整数乘法] --> B[选择算法Toom-Cook];
B --> C[多项式分割与系数选取];
C --> D[递归执行多项式乘法];
D --> E[合并中间结果恢复乘积];
E --> F[输出最终乘积];
F --> G[结束大整数乘法];
在下一章节中,我们将继续探讨快速傅里叶变换(FFT)在大整数乘法中的应用,以及如何将Toom-Cook算法与其他技术相结合以提高效率。
5. FFT基础算法介绍
5.1 快速傅里叶变换(FFT)基础
5.1.1 离散傅里叶变换(DFT)
离散傅里叶变换(Discrete Fourier Transform,DFT)是一种在数字信号处理中广泛使用的变换方法,它能将时域上的离散信号转换为频域上的表示。对于一个长度为N的复数序列{x(n)},其DFT定义为:
[ X(k) = \sum_{n=0}^{N-1} x(n) \cdot e^{-\frac{j2\pi}{N}nk}, \quad k = 0, 1, \dots, N-1 ]
DFT的计算复杂度为O(N^2),这在N较大时会非常耗时,因此在实际应用中,往往采用快速傅里叶变换(FFT)来降低计算复杂度。
5.1.2 FFT的算法原理
FFT的基本思想是利用DFT的周期性和对称性,将N点DFT分解为更小的DFTs。常见的FFT算法有Cooley-Tukey算法,适用于长度为2的幂次的序列。FFT的计算复杂度降低到了O(NlogN),极大地提高了效率。
Cooley-Tukey FFT算法的核心步骤包括:比特反转(bit reversal)和蝶形运算(butterfly operation)。通过这两步,原本需要的复杂数学运算被大幅减少。
5.2 FFT在大整数乘法中的应用
5.2.1 多项式乘法与FFT
大整数乘法可以转化为多项式乘法问题。假设两个大整数a和b分别表示为两个多项式A(x)和B(x)的系数,那么它们的乘积就对应于多项式乘法C(x)=A(x)B(x)的结果。
利用FFT可以快速计算出C(x)的系数,这是因为FFT可以在O(NlogN)的时间内将多项式从时域转换到频域,并在频域中进行高效的乘法操作,最后通过逆FFT(IFFT)得到结果多项式的系数,即为所求的大整数乘积。
5.2.2 算法优化与并行计算
在使用FFT进行大整数乘法时,算法的优化主要体现在减少不必要的计算和提高内存访问效率上。例如,利用缓存预取、循环展开等手段减少运算时间。
此外,FFT算法天然适合并行计算。现代多核处理器可以并行执行蝶形运算,显著缩短计算时间。一些特定的FFT算法如并行Cooley-Tukey FFT,就是为并行化设计的,可以在多处理器或多线程环境中运行,进一步提高算法的执行效率。
接下来,让我们看一个如何使用FFT进行多项式乘法的简单代码示例,以及该代码的详细解释。
#include <iostream>
#include <vector>
#include <complex>
#include <cmath>
const double PI = 3.***;
typedef std::complex<double> Complex;
typedef std::vector<Complex> CVector;
// FFT计算函数
void fft(CVector& a, bool invert) {
int n = a.size();
// 假设n总是2的幂次
int p = std::log2(n);
for (int i = 1; i < n; i++) {
int j = 0;
for (int k = 0; k < p; k++) {
j |= ((i >> k) & 1) << (p - k - 1);
}
if (j > i) {
std::swap(a[i], a[j]);
}
}
for (int s = 1; s <= p; s++) {
int m = 1 << s;
Complex wm = std::exp(Complex(0, -2 * PI / m) * (invert ? 1 : -1));
for (int k = 0; k < n; k += m) {
Complex w(1);
for (int j = 0; j < m / 2; j++) {
Complex t = w * a[k + j + m / 2];
Complex u = a[k + j];
a[k + j] = u + t;
a[k + j + m / 2] = u - t;
w *= wm;
}
}
}
if (invert) {
for (Complex& x : a) {
x /= n;
}
}
}
// 用于多项式乘法的FFT优化乘法
CVector multiply(const CVector& a, const CVector& b) {
CVector result(a.size() + b.size() - 1);
std::copy(a.begin(), a.end(), result.begin());
fft(result, false);
for (size_t i = 0; i < b.size(); ++i) {
Complex t(b[i], 0);
for (size_t j = 0; j < result.size(); ++j) {
result[j] *= t;
}
fft(result, false);
}
return result;
}
int main() {
// 用多项式系数示例
CVector a = {1, 2, 3};
CVector b = {0, 1, 2, 3};
// 计算多项式乘积
CVector product = multiply(a, b);
// 输出结果
for (const Complex& c : product) {
std::cout << c << std::endl;
}
return 0;
}
以上代码展示了如何使用FFT进行两个多项式的乘法操作。这里,我们使用了 <complex>
库来处理复数,并定义了相关的数据结构和FFT函数。在 multiply
函数中,我们首先复制了一个多项式的系数到结果向量中,然后使用FFT将其转换到频域。接下来,我们对每个项进行了乘法运算,并使用逆FFT得到最终的多项式系数,这些系数就是原多项式乘积的系数。
该段代码也展示了FFT算法优化的一个方面:通过减少循环内部的重复计算,并利用FFT的性质来提高效率。
在进一步优化大整数乘法算法时,我们需要考虑更多的实际应用场景和硬件特性,以便在保持算法正确性的同时,进一步提升性能。在后续的章节中,我们将深入探讨这些优化手段及其在实际中的应用。
6. C++实现大整数运算的要点
在处理大整数乘法时,C++编程语言的灵活性和性能优势使其成为一个理想的选择。本章将深入探讨在C++中实现大整数运算时需要注意的要点,包括编程技巧、内存管理、边界处理、错误处理和溢出检测等。通过这些要点的阐述,我们将揭示如何在保持代码性能的同时,确保运算的准确性和程序的稳定性。
6.1 C++编程技巧
C++不仅是一种高效的编程语言,它还提供了丰富的编程技巧和工具,用以解决大整数运算中可能遇到的问题。理解并应用这些技巧将对实现高效、稳定的大整数运算至关重要。
6.1.1 内存管理与优化
在C++中,内存管理是性能优化的一个关键方面。尤其是在处理大整数时,由于其数据量大,内存的分配和释放将直接影响到程序的性能。
// 示例代码:手动管理内存的简单示例
int* allocate_memory() {
int* ptr = new int[1024]; // 动态分配内存
return ptr;
}
void deallocate_memory(int* ptr) {
delete[] ptr; // 释放内存
}
int main() {
int* myArray = allocate_memory();
// 使用myArray进行运算
deallocate_memory(myArray);
return 0;
}
在上述代码示例中,我们使用 new
操作符动态分配了1024个整数的数组,并在不再需要时用 delete[]
操作符释放了内存。在大整数运算中,常常需要创建和销毁大量数据结构,因此手动管理内存可以有效地减少内存碎片的产生,并且能精确地控制内存使用。
6.1.2 高效算法的实现与优化
高效算法的实现与优化对于大整数运算是至关重要的。一个精心设计的算法可以减少不必要的计算,从而显著提升性能。
// 示例代码:一个简单的乘法函数
unsigned long long simple_multiply(unsigned long long a, unsigned long long b) {
return a * b;
}
// 优化后的乘法函数,模拟分治思想
unsigned long long optimized_multiply(unsigned long long a, unsigned long long b) {
// 这里可以嵌入Karatsuba或Toom-Cook算法的实现
return a * b;
}
在这个例子中, simple_multiply
函数直接使用了内置的乘法运算符,而 optimized_multiply
函数则暗示了进一步优化算法的潜力。在大整数运算中,可以实现如Karatsuba或Toom-Cook算法这样的高级算法,它们通过减少基本乘法操作的次数来提高效率。
6.2 大整数运算的边界处理
在大整数运算中,边界处理是确保运算正确性的关键。它包括错误处理、异常捕获、溢出检测等。
6.2.1 错误处理与异常捕获
在实际应用中,大整数运算可能会遇到各种异常情况,如非法输入、内存不足等。因此,合理地处理错误和捕获异常是确保程序稳定运行的重要环节。
// 示例代码:使用try-catch进行异常处理
try {
// 假设这里有一个执行大整数运算的过程
if (some_condition) {
throw std::runtime_error("Error: 非法操作");
}
} catch (const std::exception& e) {
std::cerr << "捕获异常: " << e.what() << std::endl;
}
在上述代码中,我们使用了 try-catch
块来捕获和处理可能发生的异常。这是一种常见的异常处理机制,可以有效地帮助我们发现并响应错误情况。
6.2.2 溢出检测与处理
大整数运算的一个常见问题是溢出,即运算结果超出了存储该数值类型的最大范围。为了避免溢出,需要实现检测机制并适当处理。
// 示例代码:使用std::numeric_limits检测溢出
#include <limits>
#include <iostream>
int main() {
unsigned long long a = std::numeric_limits<unsigned long long>::max();
unsigned long long b = 1;
if (b > 0 && a > std::numeric_limits<unsigned long long>::max() - b) {
std::cerr << "溢出检测:计算结果会导致溢出。" << std::endl;
} else {
unsigned long long result = a + b;
std::cout << "计算结果为: " << result << std::endl;
}
return 0;
}
在示例中,我们使用了 std::numeric_limits
来获取 unsigned long long
类型的最大值,并通过比较来检测加法运算是否会超出这个范围。这是处理溢出的一种方法,但需要注意,对于更大范围的大整数运算,可能需要自定义的溢出检测逻辑。
在本章中,我们探讨了在C++中实现大整数运算的几个关键要点,包括内存管理、高效算法的实现、边界处理、错误处理以及溢出检测。理解这些要点将帮助我们更好地编写出既高效又稳定的大整数运算代码,从而在实际应用中获得更好的性能。
7. 大整数乘法应用场景
大整数乘法在现代计算领域中扮演着重要的角色,特别是在那些需要处理高精度数值计算的场景中。在密码学、计算机图形学等领域,大整数乘法都发挥着不可替代的作用。
7.1 密码学中的应用
7.1.1 公钥加密与大整数
公钥加密技术是现代网络安全的基石之一,而其中大部分依赖于大整数的因数分解难题。RSA算法就是一个经典的例子,它的安全性基于这样一个事实:将两个大质数相乘是容易的,而将乘积分解回原来的质数却是极其困难的。
// 示例:使用大整数库来模拟RSA加密过程中的密钥生成
#include <iostream>
#include <openssl/bn.h> // 引入大整数库
void GenerateRSAKeys(BIGNUM *n, BIGNUM **e, BIGNUM **d) {
// 这里省略了生成大质数p和q,以及计算n = p*q的代码
// ...
// 计算欧拉函数phi(n) = (p-1)*(q-1)
BIGNUM *phi = BN_new();
BN_sub_word(phi, 1);
BN_mul(phi, phi, BN_value_one());
BN_sub_word(phi, 1);
BN_mul(phi, phi, n);
// 随机生成公钥指数e
// ...
// 计算私钥指数d
// ...
// 设置输出参数
*n = n;
*e = e;
*d = d;
}
int main() {
BIGNUM *n = nullptr;
BIGNUM *e = nullptr;
BIGNUM *d = nullptr;
GenerateRSAKeys(n, &e, &d);
// 输出生成的密钥
// ...
// 清理资源
BN_free(n);
BN_free(e);
BN_free(d);
return 0;
}
7.1.2 数字签名与验证
数字签名是加密技术的另一个重要应用。它保证了数据传输的真实性、完整性和不可否认性。在生成数字签名时,通常会涉及大整数的哈希值运算和模幂运算。这些运算要求能够处理超出标准数据类型的数字。
7.2 计算机图形学中的应用
7.2.1 大规模数值计算
在计算机图形学中,大整数乘法可用于大规模数值计算,如光线追踪算法中使用了大量大整数表示光线与物体的交点坐标。由于这些坐标值非常大,普通的整数类型无法满足需求,因此需要使用大整数运算。
7.2.2 精确的几何计算
为了实现精确的几何图形表示和变换,特别是在3D建模和动画制作中,需要对微小的几何数据进行高精度的数学运算。这包括但不限于顶点位置、法线、矩阵变换等,这些都可能涉及到大整数的乘法运算。
// 示例:计算两个3D向量的点积(使用大整数)
#include <iostream>
#include <vector>
// 假设已经有一个大整数库,能够处理大整数的乘法
#include <biginteger.h>
// 向量点积函数
BigInteger DotProduct(const std::vector<BigInteger>& v1, const std::vector<BigInteger>& v2) {
if (v1.size() != v2.size()) {
throw std::invalid_argument("Vectors must be of same size.");
}
BigInteger result(0);
for (size_t i = 0; i < v1.size(); ++i) {
result += v1[i] * v2[i];
}
return result;
}
int main() {
std::vector<BigInteger> vectorA = { ... }; // 3D向量A的分量
std::vector<BigInteger> vectorB = { ... }; // 3D向量B的分量
BigInteger dotProd = DotProduct(vectorA, vectorB);
std::cout << "Dot product: " << dotProd << std::endl;
return 0;
}
在这些应用场景中,大整数乘法不仅仅是一个简单的数学操作,而是实现复杂系统功能的关键组件。随着计算技术的发展,对大整数乘法算法的效率和准确性要求会越来越高。
简介:本源代码库为C++编写的实现大整数乘法的程序,涵盖多种算法如Karatsuba、Toom-Cook和基于FFT的算法。程序利用字符串或自定义结构体来模拟大整数运算,适用于加密算法、数论计算、分布式计算、金融计算和科学计算等场景。在使用前,需要进行编译和测试以确保其正确性和性能。