编程实践:算法与数据结构案例解析

内容分享8小时前发布
0 0 0

table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}

1、请自行尝试重写单词搜索解决方案,以适应电路板布局问题。

可以复用包括网格代码在内的大部分代码。电路板布局问题与单词搜索问题类似,只是将

1×N

的矩形(单词)换成了

M×N

的矩形,且矩形不能重叠、不能放在对角线上,实际上比单词搜索问题更简单。

2、为井字棋添加单元测试,以确保 legal_moves、is_win 和 is_draw 属性能够正确工作。

添加新的测试用例来测试

legal_moves


is_win


is_draw

属性。以下是示例代码,假设已有

Board

类及其子类

TTTBoard

实现了这些属性:


import unittest
from typing import List

from minimax import find_best_move
from tictactoe import TTTPiece, TTTBoard
from board import Move


class TTTBoardTestCase(unittest.TestCase):
    def test_legal_moves(self):
        position: List[TTTPiece] = [TTTPiece.X, TTTPiece.E, TTTPiece.E,
                                    TTTPiece.E, TTTPiece.E, TTTPiece.O,
                                    TTTPiece.E, TTTPiece.X, TTTPiece.O]
        test_board: TTTBoard = TTTBoard(position, TTTPiece.X)
        legal_moves = test_board.legal_moves
        # 可以根据预期的合法移动进行断言
        self.assertEqual(isinstance(legal_moves, list), True)

    def test_is_win(self):
        win_position: List[TTTPiece] = [TTTPiece.X, TTTPiece.X, TTTPiece.X,
                                        TTTPiece.E, TTTPiece.E, TTTPiece.O,
                                        TTTPiece.E, TTTPiece.E, TTTPiece.O]
        test_board: TTTBoard = TTTBoard(win_position, TTTPiece.X)
        self.assertEqual(test_board.is_win, True)

    def test_is_draw(self):
        draw_position: List[TTTPiece] = [TTTPiece.X, TTTPiece.O, TTTPiece.X,
                                         TTTPiece.X, TTTPiece.O, TTTPiece.O,
                                         TTTPiece.O, TTTPiece.X, TTTPiece.X]
        test_board: TTTBoard = TTTBoard(draw_position, TTTPiece.X)
        self.assertEqual(test_board.is_draw, True)


if __name__ == '__main__':
    unittest.main()

上述代码添加了三个新的测试用例,分别测试

legal_moves


is_win


is_draw

属性。可以根据实际情况调整测试用例中的棋盘状态和预期结果。

3、编写一个适用于任意数量圆盘的汉诺塔问题求解器。

以下是适用于任意数量圆盘的汉诺塔问题求解代码:


class Stack:
    def __init__(self):
        self.items = []
    def push(self, item):
        self.items.append(item)
    def pop(self):
        if not self.is_empty():
            return self.items.pop()
    def is_empty(self):
        return len(self.items) == 0
    def __str__(self):
        return str(self.items)

def hanoi(begin: Stack, end: Stack, temp: Stack, n: int) -> None:
    if n == 1:
        end.push(begin.pop())
    else:
        hanoi(begin, temp, end, n - 1)
        hanoi(begin, end, temp, 1)
        hanoi(temp, end, begin, n - 1)

if __name__ == "__main__":
    num_discs = 3
    tower_a: Stack = Stack()
    tower_b: Stack = Stack()
    tower_c: Stack = Stack()
    for i in range(1, num_discs + 1):
        tower_a.push(i)
    hanoi(tower_a, tower_c, tower_b, num_discs)
    print(tower_a)
    print(tower_b)
    print(tower_c)

上述代码定义了一个

Stack

类来模拟汉诺塔的柱子,

hanoi

函数用于解决汉诺塔问题,在主程序中进行了简单的调用测试。

4、使用一次性密码本对图像进行加密和解密。

一般步骤为:

加密时,先将图像数据转换为字节序列,生成与图像字节序列长度相同的随机密钥,使用异或(XOR)操作将图像字节序列和随机密钥进行运算得到加密后的图像数据;

解密时,将加密后的图像数据与随机密钥再次进行异或(XOR)操作,再将结果转换回图像格式。

5、通过创建一个包含一百万个数字的列表,并计时线性查找和二分查找函数在列表中查找不同数字所需的时间,来展示二分查找相对于线性查找的性能优势。

以下是调整为 Markdown 格式的文本内容:


线性查找会遍历列表中的每个元素,时间复杂度为 O(n);而二分查找要求列表有序,每次将搜索范围缩小一半,时间复杂度为 O(lg n)。当列表规模很大(如一百万个数字)时,二分查找的性能优势会很明显。

要实现该功能,可按以下步骤编写代码:

创建一个包含一百万个数字的有序列表;

实现线性查找和二分查找函数;

选择不同数字,分别使用这两个函数进行查找,并使用 Python 的

time

模块记录查找时间;

对比两个函数的查找时间。

6、为不同数量的初始传教士和食人族找到传教士与食人族问题的解决方案。提示:你可能需要为MCState类添加__eq__()和__hash__()方法的重写。

要解决此问题,可按以下步骤操作:

修改

MAX_NUM

变量以改变传教士和食人族的初始数量;


MCState

类中添加

__eq__()


__hash__()

方法的重写;

重新运行求解代码,使用修改后的初始状态调用搜索算法(如

bfs

)来寻找解决方案。

7、修改 WordSearchConstraint 类,使其允许字母重叠。

一般而言,需要修改

satisfied

方法逻辑,当前该方法通过检查所有位置去重后的长度是否等于原长度来判断是否有重叠,若要允许重叠,需修改此逻辑,比如可添加额外条件判断重叠是否符合特定规则,或者直接返回

True

以允许所有重叠情况。示例修改后的

satisfied

方法可能如下:


class WordSearchConstraint(Constraint[str, List[GridLocation]]):
    def __init__(self, words: List[str]) -> None:
        super().__init__(words)
        self.words: List[str] = words

    def satisfied(self, assignment: Dict[str, List[GridLocation]]) -> bool:
        # 允许字母重叠,直接返回 True
        return True

8、构建一个电路板布局问题求解器,该问题类似单词搜索问题,只是将 1×N 的矩形换成了 M×N 的矩形,且矩形不能重叠、不能放在对角线上。

可将单词搜索问题的解决方案进行改写以适应电路板布局问题,复用包括网格代码在内的许多代码。

9、使用约束满足问题解决框架构建一个可以解决数独问题的程序。

可使用简单回溯、深度优先搜索问题解决框架来解决

还可通过添加启发式方法(如 A* 算法)或采用约束传播技术来改进

10、为图框架添加对有向图的支持,应该怎么做?

可按以下步骤为图框架添加有向图支持:


修改

Edge





当前

Edge

类适用于无向图,对于有向图,

reversed()

方法可能不需要,或者在有向图中该方法的意义不同。可以添加一个标志来区分有向和无向边。


修改图类



在图类中,添加对有向图的支持。例如,在添加边时,需要考虑边的方向。


调整算法



一些图算法(如最短路径算法)在有向图和无向图中的实现可能不同,需要相应调整。


以下是修改

Edge

类以支持有向图的示例代码:


from __future__ import annotations
from dataclasses import dataclass

@dataclass
class Edge:
    u: int  # the "from" vertex
    v: int  # the "to" vertex
    directed: bool = False  # 标志是否为有向边

    def reversed(self) -> Edge:
        if self.directed:
            return self  # 有向边反转后还是自身
        return Edge(self.v, self.u)

    def __str__(self) -> str:
        if self.directed:
            return f"{self.u} -> {self.v}"
        return f"{self.u} <-> {self.v}"

在图类中添加边时,可以根据

directed

标志来处理有向和无向边。

11、为GeneticAlgorithm添加对一种高级锦标赛选择形式的支持,该形式有时会根据递减概率选择第二或第三优的染色体。

以下是一个实现思路及示例代码。实现思路是在锦标赛选择过程中,根据递减概率选择第二或第三优的染色体。示例代码如下:


import random
from heapq import nlargest
from typing import List, Tuple
from chromosome import Chromosome
from enum import Enum
from typing import TypeVar, Callable

C = TypeVar('C', bound=Chromosome)

class GeneticAlgorithm:
    SelectionType = Enum("SelectionType", "ROULETTE TOURNAMENT")

    def __init__(self, initial_population: List[C], threshold: float, max_generations: int = 100, mutation_chance: float = 0.01, crossover_chance: float = 0.7, selection_type: SelectionType = SelectionType.TOURNAMENT) -> None:
        self._population: List[C] = initial_population
        self._threshold: float = threshold
        self._max_generations: int = max_generations
        self._mutation_chance: float = mutation_chance
        self._crossover_chance: float = crossover_chance
        self._selection_type: GeneticAlgorithm.SelectionType = selection_type
        self._fitness_key: Callable = type(self._population[0]).fitness

    def _pick_tournament(self, num_participants: int) -> Tuple[C, C]:
        participants = random.choices(self._population, k=num_participants)
        sorted_participants = sorted(participants, key=self._fitness_key, reverse=True)
        # 定义递减概率
        probabilities = [0.8, 0.15, 0.05]
        index = random.choices(range(min(len(sorted_participants), len(probabilities))), weights=probabilities)[0]
        parent1 = sorted_participants[index]
        # 再次选择第二个父染色体
        remaining_participants = [p for p in sorted_participants if p != parent1]
        index2 = random.choices(range(min(len(remaining_participants), len(probabilities))), weights=probabilities)[0]
        parent2 = remaining_participants[index2]
        return (parent1, parent2)

12、向一个约束满足框架中添加一个新函数,该函数使用遗传算法解决任何任意的约束满足问题(CSP)。一个可能的适应度衡量标准是染色体解决的约束数量。

可按以下思路实现:

明确遗传算法的基本要素,如种群、染色体、适应度函数、选择、交叉和变异操作。

将CSP问题进行编码,使染色体能够表示问题的解。

实现适应度函数,以染色体解决的约束数量作为适应度值。

实现选择、交叉和变异操作,不断迭代更新种群,直到找到满足要求的解。

13、使用matplotlib库创建一个函数,该函数能对二维数据集上KMeans算法的运行结果创建一个颜色编码的散点图。

以下是一个使用Python的matplotlib库创建颜色编码散点图展示KMeans聚类结果的示例代码:


import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

# 定义函数
def plot_kmeans_results(X, n_clusters=3):
    # 运行KMeans聚类
    kmeans = KMeans(n_clusters=n_clusters, random_state=42)
    kmeans.fit(X)

    # 获取聚类标签
    labels = kmeans.labels_

    # 创建颜色编码的散点图
    plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis')

    # 显示聚类中心
    centers = kmeans.cluster_centers_
    plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='X', s=200)

    # 添加标题和标签
    plt.title('KMeans Clustering Results')
    plt.xlabel('X - axis')
    plt.ylabel('Y - axis')

    # 显示图形
    plt.show()

# 生成示例二维数据集
X = np.random.rand(100, 2)

# 调用函数
plot_kmeans_results(X)

上述代码定义了一个函数

plot_kmeans_results

,它接收一个二维数据集和聚类的数量作为参数,使用

sklearn

库中的

KMeans

进行聚类,接着使用

matplotlib


scatter

函数创建散点图,不同聚类的点用不同颜色表示,最后显示聚类中心并用红色的’X’标记。

14、为KMeans创建一个新的初始化方法,该方法接受初始质心位置,而不是随机分配它们。


# KMeans 类的初始化方法编写思路

可以按以下思路编写新的初始化方法:在 `KMeans` 类中创建一个新的 `__init__` 方法,该方法接收 `k`、`points` 和 `initial_centroids`(初始质心位置列表)作为参数。

## 实现步骤

1. **检查 k 的合法性**:确保 `k` 大于等于 1。
2. **数据归一化**:对数据点进行 z-score 归一化处理。
3. **初始化簇**:使用传入的 `initial_centroids` 来初始化簇,而不是调用 `_random_point` 方法生成随机质心。

## 示例代码

```python
from typing import List
from dataclasses import dataclass
from typing import TypeVar
import statistics
from random import uniform

Point = TypeVar('Point', bound='DataPoint')

class KMeans:
    @dataclass
    class Cluster:
        points: List['Point']
        centroid: 'DataPoint'

    def __init__(self, k: int, points: List['Point'], initial_centroids: List['DataPoint']) -> None:
        if k < 1:
            raise ValueError("k must be >= 1")
        self._points: List['Point'] = points
        self._zscore_normalize()
        self._clusters: List['KMeans.Cluster'] = []
        if len(initial_centroids) != k:
            raise ValueError("The number of initial centroids must be equal to k")
        for centroid in initial_centroids:
            cluster = KMeans.Cluster([], centroid)
            self._clusters.append(cluster)

    def _dimension_slice(self, dimension: int) -> List[float]:
        return [x.dimensions[dimension] for x in self._points]

    def _zscore_normalize(self) -> None:
        num_points = len(self._points)
        num_dimensions = self._points[0].num_dimensions
        zscored = [[] for _ in range(num_points)]
        for dimension in range(num_dimensions):
            dimension_slice = self._dimension_slice(dimension)
            mean_val = statistics.mean(dimension_slice)
            std_dev = statistics.pstdev(dimension_slice)
            for index, value in enumerate(dimension_slice):
                if std_dev == 0:
                    zscore = 0
                else:
                    zscore = (value - mean_val) / std_dev
                zscored[index].append(zscore)
        for i in range(num_points):
            self._points[i].dimensions = tuple(zscored[i])

    def _random_point(self) -> 'DataPoint':
        rand_dimensions = []
        num_dimensions = self._points[0].num_dimensions
        for dimension in range(num_dimensions):
            values = self._dimension_slice(dimension)
            rand_value = uniform(min(values), max(values))
            rand_dimensions.append(rand_value)
        return DataPoint(rand_dimensions)

class DataPoint:
    def __init__(self, dimensions):
        self.dimensions = dimensions
        self.num_dimensions = len(dimensions)

其中

DataPoint

类用于表示数据点。这里补充了

_zscore_normalize

函数的具体实现逻辑,通过计算均值和标准差来进行 z-score 归一化。

“`

© 版权声明

相关文章

暂无评论

none
暂无评论...