使用分治法解决矩阵乘法的Strassen算法及C代码示例

使用分治法解决矩阵乘法的Strassen算法及C代码示例

  • 一、背景与意义
  • 二、分治法与矩阵乘法
  • 二、Strassen算法的基本思想
  • 三、Strassen算法的具体步骤
  • 四、Strassen算法的C代码实现
  • 五、Strassen算法的时间复杂度分析
  • 六、Strassen算法的优缺点及改进
  • 七、结论

一、背景与意义

在计算机科学中,矩阵乘法是一种基本的运算,广泛应用于图像处理、机器学习、线性代数、物理模拟等领域。然而,传统的矩阵乘法算法的时间复杂度较高,为O(n^3),在处理大规模矩阵时效率低下。为了降低矩阵乘法的计算成本,研究者们提出了多种优化方法,其中Strassen算法便是一种基于分治法的有效算法。

Strassen算法于1969年由德国数学家Volker Strassen提出,它将矩阵乘法的时间复杂度降低到了O(nlog2(7))≈O(n2.807),相较于传统算法有了显著的提升。尽管后来有更高效的算法被提出,如Coppersmith-Winograd算法和近年来引起轰动的算法改进,但Strassen算法依然具有重要意义,因为它在理论和实践之间达到了较好的平衡,且算法相对简单,易于理解和实现。
在这里插入图片描述

二、分治法与矩阵乘法

分治法是一种解决问题的策略,它将一个大问题分解为若干个小问题,递归地解决这些小问题,然后将它们的解组合起来,形成原问题的解。在矩阵乘法中,分治法的基本思想是将大矩阵分解为小矩阵,然后分别计算这些小矩阵的乘积,最后通过组合这些小矩阵的乘积得到最终的结果。

具体来说,当我们将一个n×n的矩阵A和一个n×n的矩阵B相乘时,可以将A和B都划分为4个n/2×n/2的子矩阵。这样,我们就可以将原问题转化为计算7个n/2×n/2的子矩阵乘积的问题,然后通过特定的组合方式将这些子矩阵乘积组合起来,得到最终的乘积矩阵C。

虽然这种分治策略看起来似乎并没有减少计算量,但实际上,Strassen算法通过一些巧妙的数学变换,使得在递归过程中,每次递归调用的计算量都有所减少,从而实现了比传统算法更低的时间复杂度。

二、Strassen算法的基本思想

Strassen算法的基本思想是将大矩阵的乘法问题分解为若干个小矩阵的乘法问题,并递归地解决这些小问题。与传统的分治法不同,Strassen算法并不仅仅是将矩阵简单地划分为四个子矩阵进行递归乘法,而是通过一系列线性组合和递归调用来减少必要的乘法次数。

具体来说,对于两个n×n的矩阵A和B,Strassen算法首先将它们各划分为四个(n/2)×(n/2)的子矩阵:

A = [A11 A12; A21 A22],B = [B11 B12; B21 B22]

然后,算法并不直接计算这些子矩阵的乘积,而是构造了7个中间矩阵M1, M2, …, M7,每个中间矩阵都是A和B的子矩阵的线性组合。这些线性组合是通过一系列精心设计的常系数加减运算得到的,它们的目的是在后续的计算中消除不必要的乘法运算。

接下来,算法递归地计算这7个中间矩阵的乘积。由于这些中间矩阵的维度是原矩阵的一半,因此递归调用的成本较低。一旦这些乘积被计算出来,就可以通过另一组线性组合将它们组合起来,得到原矩阵A和B的乘积。

三、Strassen算法的具体步骤

划分矩阵:将输入的两个n×n矩阵A和B各划分为四个(n/2)×(n/2)的子矩阵。
A = [A11 A12; A21 A22],B = [B11 B12; B21 B22]

计算中间矩阵:构造7个中间矩阵M1, M2, …, M7,如下所示:
M1 = (A11 + A22) * (B11 + B22)
M2 = (A21 + A22) * B11
M3 = A11 * (B12 - B22)
M4 = A22 * (B21 - B11)
M5 = (A11 + A12) * B22
M6 = (A21 - A11) * (B11 + B12)
M7 = (A12 - A22) * (B21 + B22)

注意,这里的"+“和”-“分别表示矩阵的对应元素相加和相减,”*"表示矩阵乘法。这些中间矩阵的计算都涉及到了子矩阵的加减和乘法运算,但由于它们都是(n/2)×(n/2)的矩阵,因此这些运算的成本相对较低。

递归调用:对每个中间矩阵Mi,递归地调用Strassen算法计算其乘积。由于Mi的维度是原矩阵的一半,因此递归调用的深度为log2(n)。当矩阵的维度足够小时(例如,达到预设的阈值),可以直接使用传统的矩阵乘法算法进行计算。

组合结果:通过另一组线性组合将递归调用的结果组合起来,得到原矩阵A和B的乘积C的子矩阵:

C11 = M1 + M4 - M5 + M7
C12 = M3 + M5
C21 = M2 + M4
C22 = M1 - M2 + M3 + M6

注意,这里的"+“和”-"分别表示矩阵的对应元素相加和相减。这些组合运算的成本相对较低,因为它们只涉及到矩阵的加减运算。

返回结果:将C11, C12, C21, C22组合起来,得到原矩阵A和B的乘积C:
C = [C11 C12; C21 C22]

四、Strassen算法的C代码实现

下面是一个简单的Strassen算法的C代码实现。为了简化代码,我们假设输入的矩阵A和B都是方阵,且它们的规模n是2的幂。在实际应用中,可能需要添加一些额外的代码来处理非方阵或非2的幂规模的情况。

#include <stdio.h>  
#include <stdlib.h>  #define MAX_SIZE 1024  typedef struct {  int data[MAX_SIZE][MAX_SIZE];  int rows;  
} Matrix;  // 函数声明  
void addMatrix(Matrix A, Matrix B, Matrix *C);  
void subtractMatrix(Matrix A, Matrix B, Matrix *C);  
void multiplyMatrix(Matrix A, Matrix B, Matrix *C);  
void strassen(Matrix A, Matrix B, Matrix *C);  // 矩阵加法  
void addMatrix(Matrix A, Matrix B, Matrix *C) {  for (int i = 0; i < A.rows; i++) {  for (int j = 0; j < A.rows; j++) {  C->data[i][j] = A.data[i][j] + B.data[i][j];
}
}
C->rows = A.rows;
}// 矩阵减法
void subtractMatrix(Matrix A, Matrix B, Matrix *C) {
for (int i = 0; i < A.rows; i++) {
for (int j = 0; j < A.rows; j++) {
C->data[i][j] = A.data[i][j] - B.data[i][j];
}
}
C->rows = A.rows;
}// 传统的矩阵乘法(用于基准测试)
void multiplyMatrix(Matrix A, Matrix B, Matrix *C) {
for (int i = 0; i < A.rows; i++) {
for (int j = 0; j < A.rows; j++) {
C->data[i][j] = 0;
for (int k = 0; k < A.rows; k++) {
C->data[i][j] += A.data[i][k] * B.data[k][j];
}
}
}
C->rows = A.rows;
}// Strassen算法的核心实现
void strassen(Matrix A, Matrix B, Matrix *C) {
int n = A.rows;
// 基准情况:如果矩阵规模足够小,则使用传统乘法  
if (n == 1) {  multiplyMatrix(A, B, C);  return;  
}  // 创建子矩阵  
Matrix A11, A12, A21, A22;  
Matrix B11, B12, B21, B22;  
Matrix M1, M2, M3, M4, M5, M6, M7;  
Matrix C11, C12, C21, C22;  // 划分矩阵A和B  
int half = n / 2;  
for (int i = 0; i < half; i++) {  for (int j = 0; j < half; j++) {  A11.data[i][j] = A.data[i][j];  A12.data[i][j] = A.data[i][j + half];  A21.data[i][j] = A.data[i + half][j];  A22.data[i][j] = A.data[i + half][j + half];  B11.data[i][j] = B.data[i][j];  B12.data[i][j] = B.data[i][j + half];  B21.data[i][j] = B.data[i + half][j];  B22.data[i][j] = B.data[i + half][j + half];  }  
}  
A11.rows = A12.rows = A21.rows = A22.rows = half;  
B11.rows = B12.rows = B21.rows = B22.rows = half;  // 递归计算M1-M7  
strassen(A11, addMatrix(B11, B12, &M1), &M1); // M1 = A11(B11 + B12)  
strassen(addMatrix(A11, A12, &M1), B22, &M2); // M2 = (A11 + A12)B22  
strassen(subtractMatrix(A21, A11, &M1), addMatrix(B11, B22, &M2), &M3); // M3 = (A21 - A11)(B11 + B22)  
strassen(subtractMatrix(A11, A22, &M1), addMatrix(B21, B22, &M2), &M4); // M4 = (A11 - A22)(B21 + B22)  
strassen(addMatrix(A11, A22, &M1), B22, &M5); // M5 = (A11 + A22)B22  
strassen(addMatrix(A21, A22, &M1), B12, &M6); // M6 = (A21 + A22)B12  
strassen(subtractMatrix(A12, A22, &M1), addMatrix(B21, B22, &M2), &M7); // M7 = (A12 - A22)(B21 + B22)  // 计算C11-C22  
addMatrix(M1, subtractMatrix(M4, addMatrix(M5, M7, &M1), &M2), &C11); // C11 = M1 + M4 - M5 + M7  
C11.rows = half;  
assignMatrix(M3, &C21); // C21 = M3  
subtractMatrix(M2, M4, &C12); // C12 = M2 - M4  
C12.rows = half;  
subtractMatrix(addMatrix(M6, subtractMatrix(M2, M3, &M1), &M2), M5, &C22); // C22 = M6 - M2 + M3 + M5  
C22.rows = half;  // 合并C11, C12, C21, C22到C  
for (int i = 0; i < half; i++) {  for (int j = 0; j < half; j++) {  C->data[i][j] = C11.data[i][j];  C->data[i][j + half] = C12.data[i][j];  C->data[i + half][j] = C21.data[i][j];  C->data[i + half][j + half] = C22.data[i][j];  }  
}  
C->rows = n;            
// 注意:上面的代码片段中有一些辅助函数(如assignMatrix)没有给出,因为它们相对简单且容易实现。
// 此外,代码中的内存管理和错误检查也没有详细展示,这在实际应用中是非常重要的。// 主函数示例(需要自行完善)
int main() {
// 创建并初始化矩阵A和B(这里省略了具体实现)
Matrix A, B, C;
// ... 初始化A和B ...
// 调用Strassen算法计算A和B的乘积  
strassen(A, B, &C);  // 打印结果矩阵C(这里省略了具体实现)  
// ... 打印C ...  return 0;
}

请注意,上面的代码是一个简化的示例,用于说明Strassen算法的基本结构和递归过程。在实际应用中,还需要考虑很多其他因素,如输入验证、动态内存分配、错误处理、性能优化等。此外,由于Strassen算法的递归性质,当矩阵规模非常大时,可能会导致递归深度过大而引起栈溢出的问题。因此,在实际应用中,可能需要使用迭代或其他技术来避免这个问题。
为了避免递归过深导致的栈溢出问题,以及提高代码的可读性和可维护性,我们可以对上面的Strassen算法实现进行一些改进。以下是一个更加完整和健壮的Strassen算法实现,包括动态内存分配、错误检查、辅助函数等:

为了避免递归过深导致的栈溢出问题,以及提高代码的可读性和可维护性,我们可以对上面的Strassen算法实现进行一些改进。以下是一个更加完整和健壮的Strassen算法实现,包括动态内存分配、错误检查、辅助函数等:c
#include <stdio.h>  
#include <stdlib.h>  // 定义矩阵结构体  
typedef struct {  int **data; // 指向指针数组的指针,用于存储矩阵元素  int rows;   // 矩阵的行数  
} Matrix;  // 辅助函数:创建新矩阵  
Matrix createMatrix(int rows) {  Matrix mat;  mat.data = (int **)malloc(rows * sizeof(int *));  if (!mat.data) {  fprintf(stderr, "Memory allocation failed!\n");  exit(EXIT_FAILURE);  }  mat.rows = rows;  for (int i = 0; i < rows; i++) {  mat.data[i] = (int *)calloc(rows, sizeof(int));  if (!mat.data[i]) {  fprintf(stderr, "Memory allocation failed!\n");  // 释放已分配的内存并退出  for (int j = 0; j < i; j++) {  free(mat.data[j]);  }  free(mat.data);  exit(EXIT_FAILURE);  }  }  return mat;  
}  // 辅助函数:释放矩阵内存  
void freeMatrix(Matrix *mat) {  for (int i = 0; i < mat->rows; i++) {  free(mat->data[i]);  }  free(mat->data);  mat->data = NULL;  mat->rows = 0;  
}  // 辅助函数:复制矩阵  
Matrix copyMatrix(Matrix mat) {  Matrix newMat = createMatrix(mat.rows);  for (int i = 0; i < mat.rows; i++) {  for (int j = 0; j < mat.rows; j++) {  newMat.data[i][j] = mat.data[i][j];  }  }  return newMat;  
}  // 辅助函数:矩阵加法  
void addMatrix(Matrix A, Matrix B, Matrix *C) {  for (int i = 0; i < A.rows; i++) {  for (int j = 0; j < A.rows; j++) {  C->data[i][j] = A.data[i][j] + B.data[i][j];  }  }  
}  // 辅助函数:矩阵减法  
void subtractMatrix(Matrix A, Matrix B, Matrix *C) {  for (int i = 0; i < A.rows; i++) {  for (int j = 0; j < A.rows; j++) {  C->data[i][j] = A.data[i][j] - B.data[i][j];  }  }  
}  // 辅助函数:将矩阵C的指定部分赋值为矩阵A  
void assignMatrix(Matrix A, Matrix *C, int rowOffset, int colOffset) {  for (int i = 0; i < A.rows; i++) {  for (int j = 0; j < A.rows; j++) {  C->data[i + rowOffset][j + colOffset] = A.data[i][j];  }  }  
}  // Strassen算法的核心实现  
void strassen(Matrix A, Matrix B, Matrix *C) {  int n = A.rows;  // 基准情况:如果矩阵规模足够小,则使用传统乘法  if (n == 1) {  C->data[0][0] = A.data[0][0] * B.data[0][0];  return;  }  // 创建子矩阵和临时矩阵  Matrix A11, A12, A21, A22;  Matrix B11, B12, B21, B22;  Matrix M1, M2, M3, M4, M5, M6, M7;  Matrix C11 = createMatrix(n / 2);  Matrix C12 = createMatrix(n / 2);  Matrix C21 = createMatrix(n / 2);  Matrix C22 = createMatrix(n / 2);  // 划分矩阵A和B  int half = n / 2;  for (int i = 0; i < half; i++) {  for (int j = 0; j < half; j++) {  A11.data[i][j] = A.data[i][j];  A12.data[i][j] = A.data[i][j + half];  A21.data[i][j] = A.data[i + half][j];  A22.data[i][j] = A.data[i + half][j + half];  B11.data[i][j] = B.data[i][j];  B12.data[i][j] = B.data[i][j + half];  B21.data[i][j] = B.data[i + half][j];  B22.data[i][j] = B.data[i + half][j + half];  }  }  A11.rows = A12.rows = A21.rows = A22.rows = B11.rows = B12.rows = B21.rows = B22.rows = half;  // 递归计算M1-M7  strassen(A11, addMatrix(B11, B12, &M1), &M1); // M1 = A11(B11 + B12)  strassen(addMatrix(A11, A12, &M2), B22, &M2); // M2 = (A11 + A12)B22  strassen(subtractMatrix(A21, A11, &M3), addMatrix(B11, B22, &M4), &M3); // M3 = (A21 - A11)(B11 + B22)  strassen(subtractMatrix(A11, A22, &M5), addMatrix(B21, B22, &M6), &M4); // M4 = (A11 - A22)(B21 + B22)  strassen(addMatrix(A11, A22, &M7), B22, &M5); // M5 = (A11 + A22)B22  strassen(addMatrix(A21, A22, &M1), B12, &M6); // M6 = (A21 + A22)B12  strassen(subtractMatrix(A12, A22, &M2), addMatrix(B21, B22, &M3), &M7); // M7 = (A12 - A22)(B21 + B22)  // 计算C11-C22  addMatrix(copyMatrix(M1), subtractMatrix(copyMatrix(M4), addMatrix(copyMatrix(M5), copyMatrix(M7), &M1), &M2), &C11); // C11 = M1 + M4 - M5 + M7  assignMatrix(M3, &C21, 0, 0); // C21 = M3  subtractMatrix(copyMatrix(M2), copyMatrix(M4), &C12); // C12 = M2 - M4  subtractMatrix(addMatrix(copyMatrix(M6), subtractMatrix(copyMatrix(M2), copyMatrix(M3), &M1), &M2), copyMatrix(M5), &C22); // C22 = M6 - M2 + M3 + M5  // 合并C11, C12, C21, C22到C  for (int i = 0; i < half; i++) {  for (int j = 0; j < half; j++) {  C->data[i][j] = C11.data[i][j];  C->data[i][j + half] = C12.data[i][j];  C->data[i + half][j] = C21.data[i][j];  C->data[i + half][j + half] = C22.data[i][j];}}// 释放临时矩阵内存  
freeMatrix(&C11);  
freeMatrix(&C12);  
freeMatrix(&C21);  
freeMatrix(&C22);  
freeMatrix(&M1);  
freeMatrix(&M2);  
freeMatrix(&M3);  
freeMatrix(&M4);  
freeMatrix(&M5);  
freeMatrix(&M6);  
freeMatrix(&M7); 
// 主函数:演示如何使用Strassen算法
int main() {
int n; // 矩阵的大小(n x n)
printf("Enter the size of the matrix (n x n): ");
scanf("%d", &n);
// 创建输入矩阵A和B  
Matrix A = createMatrix(n);  
Matrix B = createMatrix(n);  
printf("Enter the elements of matrix A:\n");  
for (int i = 0; i < n; i++) {  for (int j = 0; j < n; j++) {  scanf("%d", &A.data[i][j]);  }  
}  
printf("Enter the elements of matrix B:\n");  
for (int i = 0; i < n; i++) {  for (int j = 0; j < n; j++) {  scanf("%d", &B.data[i][j]);  }  
}  // 创建结果矩阵C  
Matrix C = createMatrix(n);  // 使用Strassen算法计算C = A * B  
strassen(A, B, &C);  // 打印结果矩阵C  
printf("The product of A and B is:\n");  
for (int i = 0; i < n; i++) {  for (int j = 0; j < n; j++) {  printf("%d ", C.data[i][j]);  }  printf("\n");  
}  // 释放矩阵内存  
freeMatrix(&A);  
freeMatrix(&B);  
freeMatrix(&C);  return 0;
}

// 注意:这个程序片段包含了Strassen算法的递归实现。它应该编译并运行,但需要注意的是,递归可能导致栈溢出,
// 尤其是对于较大的矩阵。对于实际应用,应该使用迭代实现或者确保递归深度不会太大。

// 另外,这里我们没有实现矩阵的大小不是2的幂的情况处理,实际应用中可能需要考虑这个问题。

// 请务必确保在实际运行程序时,输入的矩阵大小是2的幂,否则程序可能不会正常工作。如果需要处理非2的幂大小的矩阵,
// 可以通过填充矩阵至最近的2的幂大小或者修改算法来处理。

五、Strassen算法的时间复杂度分析

Strassen算法的时间复杂度可以通过主定理来分析。设T(n)表示算法计算两个n×n矩阵乘积的时间复杂度,则有以下递归关系式:

T(n) = 7T(n/2) + O(n^2)

其中,7T(n/2)表示7个中间矩阵的递归乘法运算的时间复杂度,O(n^2)表示其他步骤(如矩阵的加减运算和组合运算)的时间复杂度。根据主定理,这个递归关系式的解为:

T(n) = O(n^log2(7)) ≈ O(n^2.807)

这表明Strassen算法的时间复杂度优于传统的O(n^3)算法。然而,需要注意的是,这个时间复杂度是一个理论上限,实际运行时间可能会受到多种因素的影响,如矩阵的稀疏性、计算机系统的内存层次结构等。此外,随着矩阵维度的增加,递归调用的深度也会增加,可能会导致栈溢出的问题。因此,在实际应用中,需要根据具体情况选择合适的矩阵乘法算法。

六、Strassen算法的优缺点及改进

Strassen算法的优点在于它显著降低了矩阵乘法的时间复杂度,使得大规模矩阵的乘法运算变得更为高效。此外,算法的实现相对简单,易于理解和编程实现。这些优点使得Strassen算法在实际应用中具有一定的吸引力。

然而,Strassen算法也存在一些缺点。首先,算法中的常系数加减运算和递归调用可能会导致数值稳定性的问题,特别是在处理具有特殊性质的矩阵(如病态矩阵)时。其次,虽然算法的理论时间复杂度较低,但在实际应用中,由于递归调用的开销和内存访问模式的不连续性,算法的实际性能可能并不总是优于传统算法。此外,随着矩阵维度的增加,递归调用的深度也会增加,可能导致栈溢出的问题。

为了改进Strassen算法的性能,研究者们提出了多种优化方法。例如,可以通过优化递归调用的方式来减少内存开销和提高计算效率;可以通过调整算法中的常系数来改进数值稳定性;还可以结合其他算法(如迭代法、并行计算等)来进一步提高计算速度和精度。这些改进方法使得Strassen算法在实际应用中具有更广泛的适用性。

七、结论

总的来说,Strassen算法是一种基于分治法的有效矩阵乘法算法,它通过构造中间矩阵和递归调用来降低计算成本。虽然算法存在一些缺点和限制,但其在理论和实践之间的平衡以及相对简单的实现方式使得它在许多应用中仍然具有吸引力。随着计算机科学的不断发展和算法研究的深入进行,相信未来会有更多更高效的矩阵乘法算法被提出并应用于实际场景中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/757577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LoRa模块在紧急救援与灾害管理中的作用:连接生命线

在灾害发生时&#xff0c;迅速、精准的信息传递和协调是救援工作中至关重要的一环。LoRa&#xff08;低功耗广域网&#xff09;模块以其长距离通信、低功耗和强大的穿透能力&#xff0c;为紧急救援与灾害管理提供了一种卓越的解决方案&#xff0c;成为连接生命线的关键技术。 1…

多个线程交替打印ABC

多个线程交替打印ABC package 多个线程交替打印ABC;import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;/*** Created with IntelliJ IDEA.** Author: AlenXu* Date: 2024/03/20/10:10* Description:*/ public class ThreadLoopP…

FPGA学习_Xilinx7系列FPGA基本结构

文章目录 前言一、7系列FPGA介绍1.1、芯片编号 二、基本组成单元2.1、可编程逻辑块CLB&#xff08;Configable Logic Block&#xff09;2.2、可编程输入输出单元&#xff08;IOB&#xff09;2.3、嵌入式块RAM&#xff08;Block RAM&#xff09;2.4、底层内嵌功能单元2.5、内嵌专…

罐头鱼AI视频混剪系统|视频矩阵运营获客

罐头鱼AI视频混剪系统 在当今数字化时代&#xff0c;视频内容已经成为吸引观众注意力的重要媒介之一。为了帮助用户更轻松地创建、编辑和发布视频内容&#xff0c;q1977470120罐头鱼AI推出了全新的视频混剪系统&#xff0c;让您的视频制作过程更加智能高效。让我们一起来看看罐…

网络工程师之路由交换技术篇

网络工程师之路由交换技术篇 路由交换之技术篇ARPICMPBPDUIPv6IP编址MAC其他技术点参考 以下均为个人笔记&#xff0c;摘录到csdn做备份 路由交换之技术篇 ARP Operation Code指定了ARP报文的类型&#xff0c; 包括ARP request 和ARP reply&#xff1b;取值为1或者2 &#x…

linux系统------------Mysql数据库

目录 一、数据库基本概念 1.1数据(Data) 1.2表 1.3数据库 1.4数据库管理系统(DBMS) 数据库管理系统DBMS原理 1.5数据库系统&#xff08;DBS) 二、数据库发展史 1、第一代数据库 2、第二代数据库 3、第三代数据库 三、关系型数据库 3.1关系型数据库应用 3.2主流的…

docker pull 镜像 报server misbehaving 异常信息

错误原因&#xff1a; DNS服务器配置问题。 解决方案&#xff1a; 修改DNS配置文件&#xff0c;增加nameserver如下 #打开配置文件并进行编辑 vi /etc/resolv.conf 添加如下nameserver配置&#xff1a; #resolv.conf增加nameserver nameserver 114.114.114.114

Flutter开发进阶之使用Socket实现主机服务(二)

Flutter开发进阶之使用Socket实现主机服务(二) Flutter开发进阶之使用Socket实现主机服务(一) 在完成局域网内设备定位后就可以进入微服务的实操了。 I、构建Socket连接池 一、定义Socket 使用socket_io_client socket_io_client: ^2.0.3+1导入头文件 import packag…

Flink:使用 Faker 和 DataGen 生成测试数据

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

深入理解Sora技术原理

OpenAI 发布的视频生成模型 Sora(https://openai.com/sora)&#xff0c;能根据文本生成长达一分钟的高质量视频&#xff0c;理论上支持任意分辨率&#xff0c;如 1920x1080 、1080x1920 &#xff0c;生成能力远超此前只能生成 25 帧 576x1024 图像的顶尖视频生成模型 Stable Vi…

2078: [蓝桥杯2023初赛] 01 串的熵

对于一个长度为 n 的 01 串 S x1x2x3...xn. 香农信息熵的定义为&#xff1a; 。 其中 p(0), p(1) 表示在这个 01 串中 0 和 1 出现的占比。 比如&#xff0c;对于S 100 来说&#xff0c;信息熵 H(S ) - 1/3 log2(1/3) - 2/3 log2(2/3) - 2/3 log2(2/3) 1.3083。 对于一个…

Harbor镜像仓库的安装和使用

1 Harbor安装 参考文章&#xff1a; 银河麒麟v10离线安装harbor 由于配置了本地私有yum源&#xff0c;因此&#xff0c;直接使用yum命令安装docker和docker-compose 1.1 安装docker yum install docker-ce1.2 安装docker-compose yum install docker-compose1.3 安装harbo…

一起学数据分析_3(模型建立与评估_1)

使用前面清洗好的数据来建立模型。使用自变量数据来预测是否存活&#xff08;因变量&#xff09;&#xff1f; &#xff08;根据问题特征&#xff0c;选择合适的算法&#xff09;算法选择路径&#xff1a; 1.切割训练集与测试集 import pandas as pd import numpy as np impo…

机器学习——编程实现从零构造训练集的决策树

自己搭建一棵决策树【长文预警】 忙了一个周末就写到了“构建决策树”这一步&#xff0c;还没有考虑划分测试集、验证集、“缺失值、连续值”&#xff0c;预剪枝、后剪枝的部分&#xff0c;后面再补吧&#xff08;挖坑&#xff09; 第二节内容&#xff1a;验证集划分\k折交叉…

python爬虫之xpath入门

文章目录 一、前言参考文档&#xff1a; 二、xpath语法-基础语法常用路径表达式举例说明 三、xpath语法-谓语表达式举例注意 四、xpath语法-通配符语法实例 五、选取多个路径实例 六、Xpath Helper安装使用说明例子&#xff1a; 七、python中 xpath 的使用安装xpath 的依赖包xm…

基于yolov2深度学习网络的人脸检测matlab仿真,图像来自UMass数据集

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 网络架构与特征提取 4.2 输出表示 4.3损失函数设计 4.4预测阶段 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 load yolov2.mat% 加载…

爱注讲台三尺案不辞长作“育花”人

—记邵阳市优秀班主任、新宁县优秀教师周芳平 教育是人与人心灵上最美妙的接触&#xff0c;只要用心体察&#xff0c;用情关注&#xff0c;每一位学生都会走向金光大道。 ---题记 “亲爱的妈妈&#xff0c;祝您节日快乐&#xff01;”2024年3月8日&#xff0c;一条从深圳华为…

28-3 文件上传漏洞 -白盒审计绕过

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、upload-labs 靶场的第7关 先进行代码审计 $is_upload = false; $msg = null; if (isset($_POST[submit])) {if (file_exists($UPLOAD_ADDR)) {$deny_ext = array(".php&…

Spring Boot:筑基

Spring Boot 前言概述使用 Intellij idea 快速创建 Spring Boot 项目注意事项 前言 在学习 Spring 、SpringMVC 、MyBatis 和 JPA 框架的过程中&#xff0c;了解到 SSM 框架为 Java Web 开发提供了强大的后端支持&#xff0c;JPA 框架则简化了数据库的操作。然而&#xff0c;S…

Cesium:按行列绘制3DTiles的等分线

作者:CSDN @ _乐多_ 本文将介绍如何使用 Cesium 引擎根据模型的中心坐标,半轴信息,绘制 3DTiles 对象的外包盒等分线。 外包盒是一个定向包围盒(Oriented Bounding Box),它由一个中心点(center)和一个包含半轴(halfAxes)组成。半轴由一个3x3的矩阵表示,这个矩阵是…