-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAlignment.py
More file actions
123 lines (98 loc) · 5.58 KB
/
Alignment.py
File metadata and controls
123 lines (98 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# -*- coding: utf-8 -*-
"""
序列比对(Sequence Alignment)——只使用 NumPy 的实现(全局比对 / Needleman–Wunsch 思想)
目标:对比两段英文句子(按“单词”为单位),找出最优对齐方式,并输出对齐结果与最终得分。
核心思路:
1) 动态规划 DP 表:dp[i, j] 表示“前 i 个单词的句子1”和“前 j 个单词的句子2”的最优对齐分数。
2) 转移:来自三种情况的最大值
- 对角线(↘):把 words1[i-1] 与 words2[j-1] 对齐(匹配或错配)
- 上方(↑):words1[i-1] 与 GAP 对齐(相当于句子2这边空一个)
- 左方(←):GAP 与 words2[j-1] 对齐(相当于句子1这边空一个)
3) 回溯:从右下角开始,根据 dp 的来源方向,反推出具体的对齐路径(即何处加 GAP)。
说明:
- 评分规则可以自由调整:match=+2, mismatch=-1, gap=-2(示例)
- 这里采用“全局对齐”(两边都要对齐到头和尾),适合长度接近、需要整体比对的场景。
"""
import numpy as np
def sequence_alignment_numpy(sentence1, sentence2):
"""
功能:对两个英文句子做“按单词”的全局序列比对,并打印最优对齐与得分。
参数:
sentence1, sentence2: 字符串,英文句子。
"""
# 1) 预处理:把句子按空格切分为“单词序列”(列表)
words1 = sentence1.split()
words2 = sentence2.split()
# 两个序列的长度(m 表示句子1的单词数,n 表示句子2的单词数)
m, n = len(words1), len(words2)
# 2) 定义评分规则(可根据业务调整)
match_score = 2 # 单词完全相同时的加分
mismatch_score = -1 # 单词不同(错配)时的扣分
gap_penalty = -2 # 引入空位 GAP(插入/删除)的惩罚
# 3) 初始化 DP 表((m+1) x (n+1)),类型为 int
# dp[i, j] 含义:句子1前 i 个单词 与 句子2前 j 个单词 的最优对齐分数
dp = np.zeros((m + 1, n + 1), dtype=int)
# 3.1) 初始化第一行:dp[0, j]
# 含义:句子1为空串,与句子2前 j 个单词对齐 → 只能不断对句子2加 GAP 在句子1这边
# 因此得分是 j 次 gap_penalty 的累加:0, -2, -4, ...
dp[0, :] = np.arange(0, (n + 1) * gap_penalty, gap_penalty)
# 3.2) 初始化第一列:dp[i, 0]
# 含义:句子2为空串,与句子1前 i 个单词对齐 → 只能不断对句子1加 GAP
dp[:, 0] = np.arange(0, (m + 1) * gap_penalty, gap_penalty)
# 4) 填表(自顶向下,自左向右)
# 对于每个 (i, j),考虑三种转移来源:↘、↑、←,取最大值
for i in range(1, m + 1):
for j in range(1, n + 1):
# 当前要对齐的两个单词(注意下标 -1)
a = words1[i - 1]
b = words2[j - 1]
# 对角线情况(↘):把 a 与 b 对齐
# 若相同 → match_score;不同 → mismatch_score
diag = dp[i - 1, j - 1] + (match_score if a == b else mismatch_score)
# 上方情况(↑):把 a 与 GAP 对齐(等价于在句子2这一侧插入一个空位)
up = dp[i - 1, j] + gap_penalty
# 左方情况(←):把 GAP 与 b 对齐(等价于在句子1这一侧插入一个空位)
left = dp[i, j - 1] + gap_penalty
# 取三者中的最大值作为 dp[i, j]
dp[i, j] = max(diag, up, left)
# 5) 回溯(Traceback):从右下角 (m, n) 出发,反向找出具体对齐路径
aligned1, aligned2 = [], [] # 分别存放回溯构造出的“句子1对齐序列”和“句子2对齐序列”
i, j = m, n
# 只要还有任一序列未回溯完,就继续
while i > 0 or j > 0:
current = dp[i, j] # 当前格子的最优得分
# 情况A:来自对角线(↘)——把 words1[i-1] 与 words2[j-1] 对齐
# 这里分“匹配”和“错配”两种计分,但来源判断都属于对角线
if i > 0 and j > 0 and (
(words1[i - 1] == words2[j - 1] and current == dp[i - 1, j - 1] + match_score)
or (words1[i - 1] != words2[j - 1] and current == dp[i - 1, j - 1] + mismatch_score)
):
aligned1.insert(0, words1[i - 1]) # 回溯是反向构造,所以用 insert(0, ...)
aligned2.insert(0, words2[j - 1])
i -= 1
j -= 1
# 情况B:来自上方(↑)——words1[i-1] 与 GAP 对齐(句子2“缺了一个”)
elif i > 0 and current == dp[i - 1, j] + gap_penalty:
aligned1.insert(0, words1[i - 1])
aligned2.insert(0, "_") # 这里用下划线 '_' 表示 GAP(空位)
i -= 1
# 情况C:来自左方(←)——GAP 与 words2[j-1] 对齐(句子1“缺了一个”)
else:
aligned1.insert(0, "_")
aligned2.insert(0, words2[j - 1])
j -= 1
# 6) 打印结果(对齐后的两行与最终得分)
print("\n=== Sequence Alignment Result ===")
print("Sentence 1:", " ".join(aligned1))
print("Sentence 2:", " ".join(aligned2))
print("Final Alignment Score:", dp[m, n])
# 可选:打印 DP 矩阵形状与内容,方便调试/学习
print("\nDP matrix shape:", dp.shape)
print(dp)
# === 演示入口 ===
if __name__ == "__main__":
# 两段英文句子(你可以自由修改)
s1 = "I love learning computer science every day"
s2 = "I love to learn computer science everyday"
# 调用主函数,执行序列比对并打印结果
sequence_alignment_numpy(s1, s2)