Skip to content

250508 实验一 分治法

TIP

一、 实验目的

1、理解“分治法”算法设计思想及其实现步骤 2、掌握分治算法效率递归分析方法 3、掌握主方式求解递归式方法

二、 实验条件

硬件:计算机 软件:计算机程序语言开发平台,如C、C++、Java、Matlab。 学生:至少掌握一门计算机程序设计语言,如C、C++、Java、 Matlab,Python。

三、 实验内容及要求

1、利用计算机程序设计语言,实现 “Strassen’s 矩阵乘法算法”, 自主生成两个16×16 的矩阵,检验算法的正确性并输出算法结果。 2、利用计算机程序设计语言,实现 “最近点对分治算法”,在随 机生成的二维空间点集上,与蛮力法比较来检验算法的正确性,输 出算法结果。 3、请用分治策略设计一个算法,绘制以下图形。图中内部最小的等 边三角形的边长为 1。

T1

利用计算机程序设计语言,实现 “Strassen’s 矩阵乘法算法”,自主生成两个16×16 的矩阵,检验算法的正确性并输出算法结果。

算法如下:

js
function splitMatrix(matrix) {
    const n = matrix.length;
    const halfN = n / 2;
    const A11 = [], A12 = [], A21 = [], A22 = [];
    for (let i = 0; i < halfN; i++) {
        A11[i] = matrix[i].slice(0, halfN);
        A12[i] = matrix[i].slice(halfN);
    }
    for (let i = halfN; i < n; i++) {
        A21[i - halfN] = matrix[i].slice(0, halfN);
        A22[i - halfN] = matrix[i].slice(halfN);
    }
    return [A11, A12, A21, A22];
}

function addMatrices(A, B) {
    const n = A.length;
    const result = [];
    for (let i = 0; i < n; i++) {
        result[i] = [];
        for (let j = 0; j < n; j++) {
            result[i][j] = A[i][j] + B[i][j];
        }
    }
    return result;
}

function subtractMatrices(A, B) {
    const n = A.length;
    const result = [];
    for (let i = 0; i < n; i++) {
        result[i] = [];
        for (let j = 0; j < n; j++) {
            result[i][j] = A[i][j] - B[i][j];
        }
    }
    return result;
}

function strassen(A, B) {
    const n = A.length;
    if (n === 1) {
        return [[A[0][0] * B[0][0]]];
    }
    const [A11, A12, A21, A22] = splitMatrix(A);
    const [B11, B12, B21, B22] = splitMatrix(B);
    const P1 = strassen(A11, subtractMatrices(B12, B22));
    const P2 = strassen(addMatrices(A11, A12), B22);
    const P3 = strassen(addMatrices(A21, A22), B11);
    const P4 = strassen(A22, subtractMatrices(B21, B11));
    const P5 = strassen(addMatrices(A11, A22), addMatrices(B11, B22));
    const P6 = strassen(subtractMatrices(A12, A22), addMatrices(B21, B22));
    const P7 = strassen(subtractMatrices(A11, A21), addMatrices(B11, B12));
    const C11 = addMatrices(subtractMatrices(addMatrices(P5, P4), P2), P6);
    const C12 = addMatrices(P1, P2);
    const C21 = addMatrices(P3, P4);
    const C22 = subtractMatrices(subtractMatrices(addMatrices(P5, P1), P3), P7);
    const result = [];
    for (let i = 0; i < n; i++) {
        result[i] = [];
        for (let j = 0; j < n; j++) {
            if (i < n / 2) {
                if (j < n / 2) {
                    result[i][j] = C11[i][j];
                } else {
                    result[i][j] = C12[i][j - n / 2];
                }
            } else {
                if (j < n / 2) {
                    result[i][j] = C21[i - n / 2][j];
                } else {
                    result[i][j] = C22[i - n / 2][j - n / 2];
                }
            }
        }
    }
    return result;
}

function generateRandomMatrix(size) {
    const matrix = [];
    for (let i = 0; i < size; i++) {
        matrix[i] = [];
        for (let j = 0; j < size; j++) {
            matrix[i][j] = Math.floor(Math.random() * 10);
        }
    }
    return matrix;
}

function multiplyMatrices(A, B) {
    const n = A.length;
    const result = [];
    for (let i = 0; i < n; i++) {
        result[i] = [];
        for (let j = 0; j < n; j++) {
            let sum = 0;
            for (let k = 0; k < n; k++) {
                sum += A[i][k] * B[k][j];
            }
            result[i][j] = sum;
        }
    }
    return result;
}

function isEqual(A, B) {
    const n = A.length;
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            if (A[i][j]!== B[i][j]) {
                return false;
            }
        }
    }
    return true;
}

// 生成两个 16x16 的矩阵
const A = generateRandomMatrix(16);
const B = generateRandomMatrix(16);

// 使用 Strassen 算法计算矩阵乘法
const resultStrassen = strassen(A, B);

// 使用传统方法计算矩阵乘法来验证结果
const resultTraditional = multiplyMatrices(A, B);

// 检验结果是否一致
const isCorrect = isEqual(resultStrassen, resultTraditional);

console.log("Strassen 算法计算结果:");
console.log(resultStrassen);
console.log("传统方法计算结果:");
console.log(resultTraditional);
console.log("算法结果是否正确:", isCorrect);

输出为:

js
Strassen 算法计算结果:
[
  [
    281, 244, 264, 200,
    245, 197, 143, 200,
    206, 147, 174, 264,
    247, 209, 241, 227
  ],
  [
    472, 393, 364, 298,
    375, 333, 286, 363,
    333, 281, 297, 397,
    321, 305, 367, 363
  ],
  [
    467, 383, 386, 269,
    431, 338, 268, 371,
    318, 186, 297, 434,
    410, 338, 425, 343
  ],
  [
    449, 359, 339, 231,
    323, 337, 243, 290,
    322, 220, 235, 336,
    341, 276, 319, 295
  ],
  [
    493, 382, 425, 357,
    353, 390, 347, 360,
    356, 295, 310, 411,
    421, 367, 349, 397
  ],
  [
    515, 397, 444, 274,
    407, 411, 339, 378,
    352, 278, 297, 449,
    383, 323, 381, 368
  ],
  [
    355, 315, 294, 220,
    325, 286, 230, 304,
    263, 197, 285, 307,
    303, 325, 340, 369
  ],
  [
    355, 287, 306, 247,
    313, 305, 211, 279,
    244, 203, 268, 345,
    355, 230, 310, 282
  ],
  [
    401, 296, 360, 339,
    329, 244, 257, 298,
    283, 202, 233, 376,
    318, 293, 334, 294
  ],
  [
    468, 366, 395, 315,
    398, 380, 340, 447,
    325, 304, 318, 486,
    404, 358, 383, 376
  ],
  [
    451, 392, 357, 288,
    359, 308, 267, 328,
    287, 228, 284, 387,
    313, 276, 359, 336
  ],
  [
    322, 190, 236, 228,
    218, 214, 209, 247,
    188, 202, 166, 313,
    236, 230, 177, 214
  ],
  [
    448, 346, 369, 254,
    357, 312, 316, 338,
    300, 201, 269, 411,
    340, 361, 371, 353
  ],
  [
    369, 303, 300, 241,
    311, 286, 233, 327,
    205, 238, 260, 363,
    264, 190, 321, 323
  ],
  [
    458, 339, 329, 332,
    298, 370, 286, 338,
    305, 270, 295, 404,
    418, 303, 285, 332
  ],
  [
    385, 359, 367, 230,
    373, 333, 303, 422,
    345, 294, 322, 344,
    331, 319, 369, 388
  ]
]
传统方法计算结果:
[
  [
    281, 244, 264, 200,
    245, 197, 143, 200,
    206, 147, 174, 264,
    247, 209, 241, 227
  ],
  [
    472, 393, 364, 298,
    375, 333, 286, 363,
    333, 281, 297, 397,
    321, 305, 367, 363
  ],
  [
    467, 383, 386, 269,
    431, 338, 268, 371,
    318, 186, 297, 434,
    410, 338, 425, 343
  ],
  [
    449, 359, 339, 231,
    323, 337, 243, 290,
    322, 220, 235, 336,
    341, 276, 319, 295
  ],
  [
    493, 382, 425, 357,
    353, 390, 347, 360,
    356, 295, 310, 411,
    421, 367, 349, 397
  ],
  [
    515, 397, 444, 274,
    407, 411, 339, 378,
    352, 278, 297, 449,
    383, 323, 381, 368
  ],
  [
    355, 315, 294, 220,
    325, 286, 230, 304,
    263, 197, 285, 307,
    303, 325, 340, 369
  ],
  [
    355, 287, 306, 247,
    313, 305, 211, 279,
    244, 203, 268, 345,
    355, 230, 310, 282
  ],
  [
    401, 296, 360, 339,
    329, 244, 257, 298,
    283, 202, 233, 376,
    318, 293, 334, 294
  ],
  [
    468, 366, 395, 315,
    398, 380, 340, 447,
    325, 304, 318, 486,
    404, 358, 383, 376
  ],
  [
    451, 392, 357, 288,
    359, 308, 267, 328,
    287, 228, 284, 387,
    313, 276, 359, 336
  ],
  [
    322, 190, 236, 228,
    218, 214, 209, 247,
    188, 202, 166, 313,
    236, 230, 177, 214
  ],
  [
    448, 346, 369, 254,
    357, 312, 316, 338,
    300, 201, 269, 411,
    340, 361, 371, 353
  ],
  [
    369, 303, 300, 241,
    311, 286, 233, 327,
    205, 238, 260, 363,
    264, 190, 321, 323
  ],
  [
    458, 339, 329, 332,
    298, 370, 286, 338,
    305, 270, 295, 404,
    418, 303, 285, 332
  ],
  [
    385, 359, 367, 230,
    373, 333, 303, 422,
    345, 294, 322, 344,
    331, 319, 369, 388
  ]
]
算法结果是否正确: true

T2

利用计算机程序设计语言,实现 “最近点对分治算法”,在随 机生成的二维空间点集上,与蛮力法比较来检验算法的正确性,输 出算法结果。

算法如下:

js
// 计算两点之间的欧几里得距离
function distance(p1, p2) {
    return Math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2);
}

// 蛮力法求最近点对距离
function bruteForce(points) {
    let minDist = Infinity;
    for (let i = 0; i < points.length; i++) {
        for (let j = i + 1; j < points.length; j++) {
            const dist = distance(points[i], points[j]);
            if (dist < minDist) {
                minDist = dist;
            }
        }
    }
    return minDist;
}

// 按 x 坐标排序
function compareX(a, b) {
    return a[0] - b[0];
}

// 按 y 坐标排序
function compareY(a, b) {
    return a[1] - b[1];
}

// 分治算法求最近点对距离
function closestUtil(points, n) {
    if (n <= 3) {
        return bruteForce(points);
    }
    const mid = Math.floor(n / 2);
    const midPoint = points[mid];
    const dl = closestUtil(points.slice(0, mid), mid);
    const dr = closestUtil(points.slice(mid), n - mid);
    const d = Math.min(dl, dr);
    const strip = [];
    for (let i = 0; i < n; i++) {
        if (Math.abs(points[i][0] - midPoint[0]) < d) {
            strip.push(points[i]);
        }
    }
    strip.sort(compareY);
    for (let i = 0; i < strip.length; i++) {
        for (let j = i + 1; j < strip.length && (strip[j][1] - strip[i][1]) < d; j++) {
            const dist = distance(strip[i], strip[j]);
            if (dist < d) {
                d = dist;
            }
        }
    }
    return d;
}

function closest(points) {
    points.sort(compareX);
    return closestUtil(points, points.length);
}

// 随机生成二维空间点集
function generateRandomPoints(numPoints) {
    const points = [];
    for (let i = 0; i < numPoints; i++) {
        const x = Math.random() * 100;
        const y = Math.random() * 100;
        points.push([x, y]);
    }
    return points;
}

// 生成随机点集
const numPoints = 10;
const points = generateRandomPoints(numPoints);

// 使用分治算法计算最近点对距离
const resultDivideConquer = closest(points);

// 使用蛮力法计算最近点对距离
const resultBruteForce = bruteForce(points);

// 检验结果是否一致
const isCorrect = Math.abs(resultDivideConquer - resultBruteForce) < 1e-9;

console.log("随机生成的二维空间点集:");
console.log(points);
console.log("分治算法计算的最近点对距离:");
console.log(resultDivideConquer);
console.log("蛮力法计算的最近点对距离:");
console.log(resultBruteForce);
console.log("算法结果是否正确:", isCorrect);

输出结果为:

js
随机生成的二维空间点集:
[
  [ 9.662777846407833, 5.890489277390887 ],
  [ 11.842921251234273, 42.04567392921512 ],
  [ 19.53540639604048, 94.67075087519201 ],
  [ 34.423845284196396, 34.56349449258771 ],
  [ 46.01500035463677, 93.02130864472761 ],
  [ 55.14330330502011, 74.60908199111272 ],
  [ 55.910476900337926, 10.028233027458944 ],
  [ 57.27823400105467, 69.38549485449647 ],
  [ 62.53960580802544, 90.49206627138415 ],
  [ 62.58379107018719, 74.61572851061837 ]
]
分治算法计算的最近点对距离:
5.6430303606035395
蛮力法计算的最近点对距离:
5.6430303606035395
算法结果是否正确: true

T3

请用分治策略设计一个算法,绘制以下图形。图中内部最小的等 边三角形的边长为 1

使用 JavaScript 和 HTML 实现绘制比较麻烦,这里转用 Python 的 turtle 库来实现:

python
import turtle

def draw_triangle(t, side_length):
    """绘制等边三角形"""
    for _ in range(3):
        t.forward(side_length)
        t.left(120)


def divide_triangle(t, side_length, depth):
    """分治绘制三角形"""
    if depth == 0:
        draw_triangle(t, side_length)
    else:
        half_side = side_length / 2
        # 绘制左下角三角形
        divide_triangle(t, half_side, depth - 1)
        t.forward(half_side)
        # 绘制右下角三角形
        divide_triangle(t, half_side, depth - 1)
        t.backward(half_side)
        t.left(60)
        t.forward(half_side)
        t.right(60)
        # 绘制顶部三角形
        divide_triangle(t, half_side, depth - 1)
        t.left(60)
        t.backward(half_side)
        t.right(60)

# 设置画布和画笔
screen = turtle.Screen()
t = turtle.Turtle()
t.speed(0)

# 初始边长
initial_side_length = 256
# 最大递归深度
max_depth = 5

# 移动画笔到起始位置
t.penup()
t.goto(-initial_side_length / 2, -initial_side_length * (3**0.5) / 6)
t.pendown()

# 开始绘制
divide_triangle(t, initial_side_length, max_depth)

# 完成绘制
screen.mainloop()

运行即可见到 窗口内三角形的绘制过程.