数据结构和算法

DSA 主页 DSA 概述 DSA 环境设置 DSA 算法基础 DSA 渐近分析

数据结构

DSA 数据结构基础 DSA 数据结构和类型 DSA 数组数据结构

链接列表

DSA 链接列表数据结构 DSA 双向链接列表数据结构 DSA 循环链表数据结构

堆栈 &队列

DSA 堆栈数据结构 DSA 表达式解析 DSA 队列数据结构

搜索算法

DSA 搜索算法 DSA 线性搜索算法 DSA 二分搜索算法 DSA 插值搜索 DSA 跳跃搜索算法 DSA 指数搜索 DSA 斐波那契搜索 DSA 子列表搜索 DSA 哈希表

排序算法

DSA 排序算法 DSA 冒泡排序算法 DSA 插入排序算法 DSA 选择排序算法 DSA 归并排序算法 DSA 希尔排序算法 DSA 堆排序 DSA 桶排序算法 DSA 计数排序算法 DSA 基数排序算法 DSA 快速排序算法

图形数据结构

DSA 图形数据结构 DSA 深度优先遍历 DSA 广度优先遍历 DSA 生成树

树数据结构

DSA 树数据结构 DSA 树遍历 DSA 二叉搜索树 DSA AVL 树 DSA 红黑树 DSA B树 DSA B+ 树 DSA 伸展树 DSA 尝试 DSA 堆数据结构

递归

DSA 递归算法 DSA 使用递归的汉诺塔 DSA 使用递归的斐波那契数列

分而治之

DSA 分而治之 DSA 最大最小问题 DSA 施特拉森矩阵乘法 DSA Karatsuba 算法

贪婪算法

DSA 贪婪算法 DSA 旅行商问题(贪婪方法) DSA Prim 最小生成树 DSA Kruskal 最小生成树 DSA Dijkstra 最短路径算法 DSA 地图着色算法 DSA 分数背包问题 DSA 作业排序截止日期 DSA 最佳合并模式算法

动态规划

DSA 动态规划 DSA 矩阵链乘法 DSA Floyd Warshall 算法 DSA 0-1 背包问题 DSA 最长公共子序列算法 DSA 旅行商问题(动态方法)

近似算法

DSA 近似算法 DSA 顶点覆盖算法 DSA 集合覆盖问题 DSA 旅行商问题(近似方法)

随机算法

DSA 随机算法 DSA 随机快速排序算法 DSA Karger 最小割算法 DSA Fisher-Yates 洗牌算法

DSA 有用资源

DSA 问答 DSA 快速指南


Karatsuba 算法


Karatsuba 算法用于对两个 n 位数字执行快速乘法,即系统编译器计算乘积所需的时间比普通乘法所需的时间更短。

通常的乘法方法需要 n2 次计算才能得到最终乘积,因为必须对两个数字的所有数字组合进行乘法运算,然后将子积相加即可得到最终乘积。这种乘法方法称为朴素乘法。

为了更好地理解这种乘法,我们考虑两个四位数整数:1456 和 6533,并使用朴素方法计算它们的乘积。

那么,1456 × 6533 =?

朴素乘法

在这种朴素乘法中,假设两个数字的位数均为 4,则需要进行 16 次个位数 × 个位数乘法。因此,该方法的时间复杂度为 O(42),因为计算最终乘积需要 42 步。

但是,当 n 的值不断增加时,问题的时间复杂度也会不断增加。因此,采用 Karatsuba 算法来执行更快的乘法运算。

Karatsuba 算法

Karatsuba 算法的主要思想是将多个子问题的乘法简化为三个子问题的乘法。其他计算则使用加法和减法等算术运算。

该算法以两个 n 位数字作为输入,并将这两个数字的乘积作为输出。

步骤 1 −在该算法中,我们假设 n 是 2 的幂。

步骤 2 − 如果 n = 1,则我们使用乘法表求出 P = XY。

步骤 3 − 如果 n > 1,则将 n 位数字一分为二,并使用以下公式表示数字 −

X = 10n/2X1 + X2
Y = 10n/2Y1 + Y2

其中,X1、X2、Y1、Y2 各有 n/2 位数字。

步骤 4 − 取变量 Z = W – (U + V)

其中,

U = X1Y1,V = X2Y2
W = (X1 + X2) (Y1 + Y2),Z = X1Y2 + X2Y1。

步骤 5 −然后,将公式中的值代入 − 中,得到乘积 P。

P = 10n(U) + 10n/2(Z) + V
P = 10n (X1Y1) + 10n/2 (X1Y2 + X2Y1) + X2Y2。

步骤 6 −通过分别传递子问题 (X1, Y1)、(X2, Y2) 和 (X1 + X2, Y1 + Y2) 来递归调用算法。将返回值分别存储在变量 U、V 和 W 中。

示例

让我们使用 Karatsuba 方法解决上面给出的相同问题,1456 × 6533 −

Karatsuba 方法采用分治法,将问题分解为多个子问题,并应用递归使乘法更简单。

步骤 1

假设 n 是 2 的幂,将 n 位数字重写为 − 的形式。

X = 10n/2X1 + X2 Y = 10n/2Y1 + Y2

得出:

1456 = 102(14) + 56
6533 = 102(65) + 33

首先,我们尝试简化数学表达式,得到:

(1400 × 6500) + (56 × 33) + (1400 × 33) + (6500 × 56) = 
104 (14 × 65) + 102 [(14 × 33) + (56 × 65)] + (33 × 56)

上述表达式是给定乘法问题的简化版本,因为将两个两位数相乘比将两个四位数相乘更容易解决。

然而,这对于人类思维来说是正确的。但对于系统编译器来说,上述表达式的时间复杂度仍然与普通的朴素乘法相同。由于它有 4 次两位数 × 两位数的乘法,因此时间复杂度为 −

14 × 65 → O(4)
14 × 33 → O(4)
65 × 56 → O(4)
56 × 33 → O(4)
= O(16)

因此,计算需要进一步简化。

步骤 2

X = 1456
Y = 6533

由于 n 不等于 1,算法跳至步骤 3。

X = 10n/2X1 + X2
Y = 10n/2Y1 + Y2

由此可得:

1456 = 102(14) + 56
6533 = 102(65) + 33

计算 Z = W – (U + V) 减

Z = (X1 + X2) (Y1 + Y2) – (X1Y1 + X2Y2)
Z = X1Y2 + X2Y1
Z = (14 × 33) + (65 × 56)

最终结果

P = 10n. U + 10n/2. Z + V 
   = 10n (X1Y1) + 10n/2 (X1Y2 + X2Y1) + X2Y2 
   = 104 (14 × 65) + 102 [(14 × 33) + (65 × 56)] + (56 × 33)

子问题可以进一步分解为更小的问题;因此,该算法再次以递归方式调用。

步骤 3

X1 和 Y1 作为参数 X 和 Y 传递。

因此,X = 14,Y = 65

X = 10n/2X1 + X2
Y = 10n/2Y1 + Y2
14 = 10(1) + 4
65 = 10(6) + 5

计算 Z = W – (U + V) −

Z = (X1 + X2) (Y1 + Y2) – (X1Y1 + X2Y2) 
Z = X1Y2 + X2Y1 
Z = (1 × 5) + (6 × 4) = 29 

P = 10n (X1Y1) + 10n/2 (X1Y2 + X2Y1) + X2Y2 
   = 102 (1 × 6) + 101 (29) + (4 × 5) 
   = 910

步骤 4

X2 和 Y2 作为参数 X 和 Y 传递。

因此,X = 56,Y = 33

X = 10n/2X1 + X2
Y = 10n/2Y1 + Y2
56 = 10(5) + 6
33 = 10(3) + 3

计算 Z = W – (U + V) 减

Z = (X1 + X2) (Y1 + Y2) – (X1Y1 + X2Y2)
Z = X1Y2 + X2Y1
Z = (5 × 3) + (6 × 3) = 33

P = 10n (X1Y1) + 10n/2 (X1Y2 + X2Y1) + X2Y2
= 102 (5 × 3) + 101 (33) + (6 × 3)
= 1848

步骤 5

X1 + X2 和 Y1 + Y2 作为参数 X 和 Y 传递。

因此,X = 70,Y = 98

X = 10n/2X1 + X2 
Y = 10n/2Y1 + Y2 
70 = 10(7) + 0 
98 = 10(9) + 8

计算 Z = W – (U + V) 减

Z = (X1 + X2) (Y1 + Y2) – (X1Y1 + X2Y2)
Z = X1Y2 + X2Y1
Z = (7 × 8) + (0 × 9) = 56

P = 10n (X1Y1) + 10n/2 (X1Y2 + X2Y1) + X2Y2
= 102 (7 × 9) + 101 (56) + (0 × 8)
=

步骤 6

最终结果

P = 10n. U + 10n/2. Z + V
U = 910
V = 1848
Z = W – (U + V) = 6860 – (1848 + 910) = 4102

代入公式中的值,

P = 10n. U + 10n/2. Z + V
P = 104 (910) + 102 (4102) + 1848
P = 91,00,000 + 4,10,200 + 1848
P = 95,12,048

分析

Karatsuba 算法是一种递归算法;因为它在执行过程中会调用自身的较小实例。

根据算法,它仅对 n/2 位数字调用自身三次,即可获得两个 n 位数字的最终乘积。

现在,如果 T(n) 表示执行乘法时所需的位数乘法次数,

T(n) = 3T(n/2)

该等式是一个简单的递归关系,可以解为 −

将 T(n/2) = 3T(n/4) 代入上述等式,可得:
T(n) = 9T(n/4)
T(n) = 27T(n/8)
T(n) = 81T(n/16)
.
.
.
.
T(n) = 3i T(n/2i) 是 Karatsuba 算法的递归关系的一般形式。

递归关系可以使用大师定理求解,因为我们有一个形式为 − 的除法函数。

T(n) = aT(n/b) + f(n),其中 a = 3,b = 2,f(n) = 0
从而得出 k = 0。

由于 f(n) 表示递归之外的工作,这些工作在 Karatsuba 中属于加减算术运算,因此这些算术运算不会增加时间复杂度。

检查 a 和 bk 之间的关系。

a > bk = 3 > 20

根据大师定理,应用案例 1。

T(n) = O(nlogb a)
T(n) = O(nlog 3)

Karatsuba 算法的快速乘法时间复杂度为 O(nlog 3)。

示例

在 Karatsuba 算法的完整实现中,我们尝试将两个数值较高的数字相乘。由于 long 数据类型最多可容纳 18 位小数,因此我们将输入视为 long 值。Karatsuba 函数将递归调用,直到获得最终乘积。

#include <stdio.h>
#include <math.h>
int get_size(long);
long karatsuba(long X, long Y){
   
   // Base Case
   if (X < 10 && Y < 10)
      return X * Y;
   
   // 确定 X 和 Y 的大小
   int size = fmax(get_size(X), get_size(Y));
   if(size < 10)
      return X * Y;
   
   // 将最大长度向上舍入
   size = (size/2) + (size%2);
   long multiplier = pow(10, size);
   long b = X/multiplier;
   long a = X - (b * multiplier);
   long d = Y / multiplier;
   long c = Y - (d * size);
   long u = karatsuba(a, c);
   long z = karatsuba(a + b, c + d);
   long v = karatsuba(b, d);
   return u + ((z - u - v) * multiplier) + (v * (long)(pow(10, 2 * size)));
}
int get_size(long value){
   int count = 0;
   while (value > 0) {
      count++;
      value /= 10;
   }
   return count;
}
int main(){

   // two numbers
   long x = 145623;
   long y = 653324;
   printf("The final product is: %ld
", karatsuba(x, y));
   return 0;
}

输出

The final product is: 95139000852
#include <iostream>
#include <cmath>
using namespace std;
int get_size(long);
long karatsuba(long X, long Y){

   // Base Case
   if (X < 10 && Y < 10)
      return X * Y;

   // 确定 X 和 Y 的大小
   int size = fmax(get_size(X), get_size(Y));
   if(size < 10)
      return X * Y;

   // 将最大长度向上舍入
   size = (size/2) + (size%2);
   long multiplier = pow(10, size);
   long b = X/multiplier;
   long a = X - (b * multiplier);
   long d = Y / multiplier;
   long c = Y - (d * size);
   long u = karatsuba(a, c);
   long z = karatsuba(a + b, c + d);
   long v = karatsuba(b, d);
   return u + ((z - u - v) * multiplier) + (v * (long)(pow(10, 2 * size)));
}
int get_size(long value){
   int count = 0;
   while (value > 0) {
      count++;
      value /= 10;
   }
   return count;
}
int main(){

   // two numbers
   long x = 145623;
   long y = 653324;
   cout << "The final product is: " << karatsuba(x, y) << endl;
   return 0;
}

输出

The final product is: 95139000852
import java.io.*;
public class Main {
   static long karatsuba(long X, long Y) {
      // Base Case
      if (X < 10 && Y < 10)
         return X * Y;
      // 确定 X 和 Y 的大小
      int size = Math.max(get_size(X), get_size(Y));
      if(size < 10)
         return X * Y;
      // 将最大长度向上舍入
      size = (size/2) + (size%2);
      long multiplier = (long)Math.pow(10, size);
      long b = X/multiplier;
      long a = X - (b * multiplier);
      long d = Y / multiplier;
      long c = Y - (d * size);
      long u = karatsuba(a, c);
      long z = karatsuba(a + b, c + d);
      long v = karatsuba(b, d);
      return u + ((z - u - v) * multiplier) + (v * (long)(Math.pow(10, 2 * size)));
   }
   static int get_size(long value) {
      int count = 0;
      while (value > 0) {
         count++;
         value /= 10;
      }
      return count;
   }
   public static void main(String args[]) {
      // two numbers
      long x = 145623;
      long y = 653324;
      System.out.print("The final product is: ");
      long product = karatsuba(x, y);
      System.out.println(product);
   }
}

输出

The final product is: 95139000852
import math
def karatsuba(X, Y):
    if X < 10 and Y < 10:
        return X * Y
    size = max(get_size(X), get_size(Y))
    if size < 10:
        return X * Y
    size = (size // 2) + (size % 2)
    multiplier = 10 ** size
    b = X // multiplier
    a = X - (b * multiplier)
    d = Y // multiplier
    c = Y - (d * size)
    u = karatsuba(a, c)
    z = karatsuba(a + b, c + d)
    v = karatsuba(b, d)
    return u + ((z - u - v) * multiplier) + (v * (10 ** (2 * size)))
def get_size(value):
    count = 0
    while value > 0:
        count += 1
        value //= 10
    return count
x = 145623
y = 653324
print("The final product is: ", end="")
product = karatsuba(x, y)
print(product)

输出

The final product is: 95139000852