输入输出 单个按空格键分开
1 2 3 4 data = sys.stdin.read().strip().split() if not data: return it = iter (data)
按行读(适用于不确定行数的)
1 2 3 4 lines = sys.stdin.read().strip().splitlines() for line in lines: parts = line.split()
如果确定行数可以直接用input
输出数组
1 2 for row in arr: print (" " .join(map (str , row)))
Python 的 round() 不是传统四舍五入,而是“遇到 .5 舍到偶数”的银行家舍入
要做传统“四舍五入”,不要用 Python 内置 round(),推荐用 Decimal + ROUND_HALF_UP
1 2 3 4 from decimal import Decimal, ROUND_HALF_UPdef round_half_up (x ): return Decimal(str (x)).quantize(Decimal('1' ), rounding=ROUND_HALF_UP)
np库 创建数组
np.zeros(size):全 0 数组
np.full(size, val):填充值数组
对角矩阵
1 2 3 4 5 np.diag([1 , 2 , 3 ]) np.diag(A) np.eye(n) np.triu(np.ones((m, h))) np.tril(np.ones((m, h)))
形状变换
.reshape(-1) :把数组拉平成一维数组,长度自动推断
向量距离
np.linalg.norm(diff, axis=2):沿第 2 维计算欧氏距离
np.bincount(labels, minlength=k):统计非负整数数组中每个整数出现次数,从 0 开始,统计到k-1,长度为k
np.argmin(distance, axis=1):返回每一行最小值的位置
np.argsort(x):返回排序后的下标
条件筛选
X[labels == k]:取满足条件的行,label==k用于创造mask
X_new = np.delete(X, index, axis=0):删除index行,可为列表
拼接:np.concatenate() 沿已有轴拼,注意要带个[]
1 c = np.concatenate([a, b], axis=1 )
类型转换:X.astype(),方便修改输出形式
矩阵计算
a @ b:矩阵乘法,等价于 np.matmul(a, b) $$ (m, n) @ (n, p) \rightarrow (m, p) $$
* 是逐元素乘法
np.dot(a, b):一维时是点积/内积,二维时是矩阵乘法
np.outer(a, b):外积,把两个向量变成一个矩阵
输入(m,) (n,) 输出 (m, n)
rank = np.linalg.matrix_rank(A) 矩阵的秩
A_inv = np.linalg.inv(A) 矩阵的逆
np.linalg.eig(A) 特征值和特征向量
np.linalg.cond(A) 条件数
哈希表 创建字典:
用in判断key是否存在
访问 value 可以用 get
1 2 dic.get("apple" , 0 ) dic[word] = dic.get(word, 0 ) + 1
删除 key
1 2 if dic.get(word) == 0 : del dic[word]
遍历key
遍历 key 和 value:
1 2 for k, v in dic.items(): print (k, v)
sorted 返回的是一个 list,需要接收,不动原来的dict ,因为dict本身无序
dict.items() 提供的是 (key, value) 的键值对序列(可迭代对象),直接对 dict 排序只会得到 键的序列
按 key
1 sorted (d, key=lambda x: x[0 ])
按 value,需要用.items()提取出来
1 2 3 sorted (dic.items(), key=lambda x: x[1 ]) sorted (dic.items(), key=lambda x: -x[1 ]) sorted (dic.items(), key=lambda x: (-x[1 ], x[0 ]))
提取排序后的内容
1 2 keys = [k for k, v in items] values = [v for k, v in items]
dfs DFS 有几种常见写法:
返回值型 DFS:return 数据
适合:每个子问题有明确答案,当前节点 / 当前状态的答案是多少?
比如树的子树和
1 2 3 4 5 6 7 8 def dfs (node ): if node is None : return 0 left = dfs(node.left) right = dfs(node.right) return left + right + node.val
记忆化搜索,比如0-1背包问题
1 2 3 4 5 6 7 8 9 10 11 from functools import cache@cache def dfs (i, budget ): if i == n: return 0 return max ( dfs(i + 1 , budget), dfs(i + 1 , budget - cost[i]) + value[i] )
判断标准:父递归是否需要子递归的结果?
需要 -> return
不需要 -> 不一定 return
全局更新型 DFS:nonlocal ans
适合:遍历所有方案,在搜索过程中更新全局最优答案
什么时候用 path
具体选了哪些东西 / 走了哪条路 / 当前排列是什么?
枚举所有子集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 path = [] ans = [] def dfs (i ): if i == n: ans.append(path[:]) return dfs(i + 1 ) path.append(i) dfs(i + 1 ) path.pop()
P4963.第2题-LLM 多源语料分级清洗预算分配 第2题-LLM 多源语料分级清洗预算分配 - problem_ide - CodeFun2000
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 from functools import cachedef main (): N, B, T = map (int , input ().split()) w = [] r = [] c = [] t = [] for _ in range (N): data = list (map (float , input ().split())) w.append(data[0 ]) r.append(data[1 ]) c.append(data[2 ]) t.append(data[3 ]) options = [ (0 , 0 , 1.0 ), (1 , 1 , 0.6 ), (3 , 2 , 0.2 ), (6 , 3 , 0.0 ), ] @cache def dfs (i, use_B, use_T ): if use_B > B or use_T > T: return float ("inf" ) if i == N: return 0.0 base_risk = w[i] * r[i] ans = float ("inf" ) for kb, kt, kr in options: ans = min ( ans, base_risk * kr + dfs( i + 1 , use_B + kb * c[i], use_T + kt * t[i] ) ) return ans ans = dfs(0 , 0.0 , 0.0 ) print (f"{ans:.6 f} " ) if __name__ == "__main__" : main()
动态规划 P4568.第2题-模型推理量化加速优化问题 第2题-模型推理量化加速优化问题 - problem_ide - CodeFun2000
从每一层里选一个方案,所以状态天然是dp[i][loss],表示前i层累计损失不超过/等于 t 时的最小内存占用
注意点:
缩放因子为S,将阈值转为整数容量,方便dp $$ dp[i][new_loss] = \min(dp[i][new_loss],dp[i-1][old_loss]+mem_j) $$ $j$ 代表可选方案
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 import sysdef solve (): data = sys.stdin.read().strip().split() if not data: return p = 0 L = int (data[0 ]) p+=1 T = float (data[1 ]) p+=1 S = 100 Itotal_loss = int (round (S*T)) layers = [] for _ in range (L): K = int (data[p]) p+=1 options = [] for _ in range (K): bit_desc = data[p] p+=1 loss = float (data[p]) p+=1 mem = float (data[p]) p+=1 Iloss = int (round (S*loss)) options.append((Iloss, mem)) layers.append(options) INF = float ('inf' ) dp = [[INF] * (Itotal_loss + 1 ) for _ in range (L + 1 )] dp[0 ][0 ] = 0.0 for i in range (1 , L+1 ): for Iloss, mem in layers[i-1 ]: if Iloss <= Itotal_loss: for old_loss in range (Itotal_loss - Iloss+ 1 ): new_loss = old_loss + Iloss dp[i][new_loss] = min ( dp[i][new_loss], dp[i-1 ][old_loss] + mem ) ans = min (dp[L]) print (f"{ans:.2 f} " ) if __name__ == "__main__" : solve()
递归写法,选哪个
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 import sysfrom functools import cachedef main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) L = int (next (it)) S = 100 T = round (float (next (it)) * S) layers = [] for i in range (L): K = int (next (it)) options = [] for _ in range (K): _ = next (it) loss = round (float (next (it)) * S) mem = float (next (it)) options.append((loss, mem)) layers.append(options) @cache def dfs (index, loss: int ): if loss > T: return float ("inf" ) if index == L: return 0 n = len (layers[index]) options = layers[index] return min (dfs(index+1 , loss + options[i][0 ]) + options[i][1 ] for i in range (n)) min_mem = dfs(0 , 0 ) print (f"{min_mem:.2 f} " ) if __name__ == "__main__" : main()
P4274.第2题-最大能量路径 第2题-最大能量路径 - problem_ide - CodeFun2000
注意点:
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 import sysdef conv2d_padding_zero (img, kernel ): img = np.asarray(img, dtype=float ) kernel = np.asarray(kernel, dtype=float ) H, W = np.shape(img) KH, KW = np.shape(kernel) pad_top = KH // 2 pad_bottom = KH - 1 - pad_top pad_left = KW // 2 pad_right = KW - 1 - pad_left img_pad = np.pad( img, ((pad_top, pad_bottom), (pad_left, pad_right)), mode="constant" , constant_values=0 ) energy = np.zeros((H, W), dtype=float ) for i in range (H): for j in range (W): window = img_pad[i:i + KH, j:j + KW] energy[i, j] = np.sum (window * kernel) return energy def max_energy_path (energy ): H, W = len (energy), len (energy[0 ]) dp = [[0.0 ]* W for _ in range (H)] for i in range (H): dp[i][0 ] = energy[i][0 ] for j in range (1 , W): for i in range (0 , H): dp[i][j] = max (dp[i][j],dp[i][j-1 ]+energy[i][j]) if i-1 >=0 : dp[i][j] = max (dp[i][j], dp[i-1 ][j-1 ]+energy[i][j]) if i+1 <H: dp[i][j] = max (dp[i][j], dp[i+1 ][j-1 ]+energy[i][j]) ans = dp[0 ][W-1 ] for i in range (1 , H): ans = max (ans, dp[i][W-1 ]) return ans def solve (): data = sys.stdin.read().split() if not data: return idx = 0 H = int (data[idx]) idx += 1 W = int (data[idx]) idx += 1 KH = int (data[idx]) idx += 1 KW = int (data[idx]) idx += 1 img = [] for _ in range (H): row = [] for _ in range (W): row.append(int (data[idx])) idx += 1 img.append(row) kernel = [] for _ in range (KH): row = [] for _ in range (KW): row.append(int (data[idx])) idx += 1 kernel.append(row) energy = conv2d_padding_zero(img, kernel) ans = max_energy_path(energy) print (f"{ans:.1 f} " ) if __name__ == '__main__' : solve()
P3713.第3题-大模型分词 第3题-大模型分词 - problem_ide - CodeFun2000
将匹配的内容以字典的形式读取,方便后续查找和匹配
需要记录上一个匹配状态,也就是上一个能拆分的词
第一版写法,能通过
问题在于:后续转移分数依赖上一个词,所以对于同一个位置 i,只保留一个最大 dp[i] 会丢状态
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 import sysdef calculate_max_score (text, words, rules ): n = len (text) dp = [float ('-inf' )] * (n + 1 ) prev = [None ] * (n+1 ) dp[0 ] = 0 for i in range (1 , n+1 ): for j in range (i): word = text[j:i] if word in words and dp[j]!=float ('-inf' ): score = words[word] if prev[j] and (prev[j],word) in rules: score += rules[(prev[j],word)] if dp[j] + score >= dp[i]: dp[i] = dp[j] + score prev[i] = word return dp[n] if dp[n]!=float ('-inf' ) else 0 def solve (): data = sys.stdin.read().split() if not data: return idx = 0 text = data[idx] idx += 1 n = int (data[idx]) idx += 1 words = {} for _ in range (n): word = data[idx] idx += 1 score = int (data[idx]) idx += 1 words[word] = score m = int (data[idx]) rules = {} idx += 1 for _ in range (m): word1 = data[idx] idx += 1 word2 = data[idx] idx += 1 score = int (data[idx]) idx += 1 rules[(word1, word2)] = score max_score = calculate_max_score(text, words, rules) print (max_score) if __name__ == '__main__' : solve()
反例
1 2 3 4 5 6 7 8 abc 4 a 0 b 100 ab 99 c 0 1 ab c 100
正确切分应该是:
得分:
但代码在位置 2 会认为:
所以它保留:
1 2 dp[2] = 100 prev[2] = "b"
然后接 "c" 时,因为没有规则:
所以只能得到:
这不对,所以需要给dp加上最后一个词状态dp[i][last],这样才不会丢掉“最后一个词不同”的状态
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 import sysdef calculate_max_score (text, words, rules ): n = len (text) dp = [dict () for _ in range (n + 1 )] dp[0 ][None ] = 0 max_len = 0 for w in words: max_len = max (max_len, len (w)) for i in range (1 , n + 1 ): start = max (0 , i - max_len) for j in range (start, i): word = text[j:i] if word not in words: continue for prev_word, old_score in dp[j].items(): score = old_score + words[word] if prev_word is not None : score += rules.get((prev_word, word), 0 ) if word not in dp[i] or score > dp[i][word]: dp[i][word] = score if not dp[n]: return 0 return max (dp[n].values()) def solve (): data = sys.stdin.read().split() if not data: return idx = 0 text = data[idx] idx += 1 n = int (data[idx]) idx += 1 words = {} for _ in range (n): word = data[idx] idx += 1 score = int (data[idx]) idx += 1 words[word] = score m = int (data[idx]) rules = {} idx += 1 for _ in range (m): word1 = data[idx] idx += 1 word2 = data[idx] idx += 1 score = int (data[idx]) idx += 1 rules[(word1, word2)] = score max_score = calculate_max_score(text, words, rules) print (max_score) if __name__ == '__main__' : solve()
P4625.第2题-大模型训练显存优化算法 第2题-大模型训练显存优化算法 - problem_ide - CodeFun2000
注意点:
dp 完全展开的话会超时,需要压一下dp范围
注意不要忘记不选择的情况
dp[i][j]其中j代表的是容量
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 import sysdef calulate_cost (M, storage_space, cost ): INF = float ('inf' ) if sum (storage_space) < M: return INF N = len (storage_space) dp = [[INF] * (M+1 ) for _ in range (N+1 )] dp[0 ][0 ] = 0 for i in range (1 , N+1 ): space = storage_space[i-1 ] cst = cost[i-1 ] for j in range (M + 1 ): dp[i][j] = min (dp[i][j], dp[i - 1 ][j]) if dp[i - 1 ][j] != INF: next_space = min (M, j + space) dp[i][next_space] = min ( dp[i][next_space], dp[i-1 ][j] + cst ) return dp[N][M] def solve (): data = sys.stdin.read().split() idx = 0 M = int (data[idx]) idx += 1 N = int (data[idx]) idx += 1 storage_space = [] for _ in range (N): storage_space.append(int (data[idx])) idx += 1 swap_cost = [] for _ in range (N): swap_cost.append(int (data[idx])) idx += 1 recalulate_cost = [] for _ in range (N): recalulate_cost.append(int (data[idx])) idx += 1 cost = [] for i in range (N): cost.append(min (swap_cost[i], recalulate_cost[i])) min_cost = calulate_cost(M, storage_space, cost) print (min_cost) if min_cost!=float ('inf' ) else print ("error" ) if __name__ == '__main__' : solve()
但其实这题的思路就是选或不选,用dfs很简单
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 import sysfrom functools import cachedef calulate_cost (M, storage_space, cost ): INF = float ('inf' ) if sum (storage_space) < M: return INF N = len (storage_space) @cache def dfs (index, need ): if need <= 0 : return 0 if index == N: return INF not_choose = dfs(index+1 , need) choose = cost[index] + dfs( index+1 , max (0 , need-storage_space[index]) ) return min (not_choose, choose) return dfs(0 , M) def solve (): data = sys.stdin.read().split() idx = 0 M = int (data[idx]) idx += 1 N = int (data[idx]) idx += 1 storage_space = [] for _ in range (N): storage_space.append(int (data[idx])) idx += 1 swap_cost = [] for _ in range (N): swap_cost.append(int (data[idx])) idx += 1 recalulate_cost = [] for _ in range (N): recalulate_cost.append(int (data[idx])) idx += 1 cost = [] for i in range (N): cost.append(min (swap_cost[i], recalulate_cost[i])) min_cost = calulate_cost(M, storage_space, cost) print (min_cost) if min_cost != float ("inf" ) else print ("error" ) if __name__ == "__main__" : solve()
P4539.第3题-RAG系统最大收益 第3题-RAG系统最大收益 - problem_ide - CodeFun2000
注意点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def main (): n, d = map (int , input ().split()) update_cost = list (map (int , input ().split())) query_reward = list (map (int , input ().split())) dp = [[float ("-inf" )] * (n + 1 ) for _ in range (n + 1 )] for i in range (n + 1 ): dp[i][0 ] = 0 for i in range (1 , n + 1 ): dp[i][i] = max (dp[i - 1 ]) - update_cost[i - 1 ] + query_reward[i - 1 ] for T in range (1 , i): if i - T >= d: dp[i][T] = dp[i - 1 ][T] else : dp[i][T] = dp[i - 1 ][T] + query_reward[i - 1 ] return max (dp[n]) if __name__ == "__main__" : main()
能通过80%,复杂度太高了,但是好写
P4569.第3题-AIGC缓存复用加速策略 第3题-AIGC缓存复用加速策略 - problem_ide - CodeFun2000
在 0...i 这 i+1 个位置里,最多能选 (i + 2) // 2 个互不相邻元素
T个步骤,首先第一个步骤必须不能跳过,所以其实就转化为后面T-1个步骤里选k个不相邻的步骤,对应loss也是T-1的长度
dfs转为选或不选问题,很直观
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 import sysfrom functools import cachedef main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) T = int (next (it)) k = int (next (it)) loss = [int (next (it)) for _ in range (T-1 )] n = len (loss) if k == 0 : print (0 ) return if k > (n + 1 ) // 2 : print (-1 ) return @cache def dfs (i, remain ): if remain == 0 : return 0 if i < 0 : return float ("inf" ) if remain > (i + 2 ) // 2 : return float ("inf" ) choose = loss[i] + dfs(i - 2 , remain - 1 ) not_choose = dfs(i - 1 , remain) return min (choose, not_choose) ans = dfs(n-1 , k) print (ans) if __name__ == "__main__" : main()
Kmeans 普通 K-means 模板
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 import numpy as npdef kmeans (X, K, T=100 , eps=1e-6 ): """ X: (N, D), N 个样本,每个样本 D 维 K: 聚类数量 T: 最大迭代次数 eps: 收敛阈值 """ N, D = X.shape centers = X[:K].copy() for _ in range (T): old_centers = centers.copy() diff = X[:, None , :] - centers[None , :, :] dist = np.linalg.norm(diff, axis=2 ) labels = np.argmin(dist, axis = 1 ) for k in range (K): members = X[labels == k] if len (members) > 0 : centers[k] = np.mean(members, axis=0 ) shift = np.linalg.norm(centers - old_centers) if shift < eps: break diff = X[:, None , :] - centers[None , :, :] dist = np.linalg.norm(diff, axis=2 ) labels = np.argmin(dist, axis=1 ) counts = np.bincount(labels, minlength=K) return labels, centers, counts
P3842.第2题-Yolo检测器中的anchor聚类 第2题-Yolo检测器中的anchor聚类 - problem_ide - CodeFun2000
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 import sysimport numpy as npdef iou_wh (boxes, centers ): """ boxes: shape (N, 2) -> [w, h] centers: shape (K, 2) -> [W, H] return: iou matrix, shape (N, K) """ bw = boxes[:, 0 :1 ] bh = boxes[:, 1 :2 ] cw = centers[:, 0 ] ch = centers[:, 1 ] inter = np.minimum(bw, cw) * np.minimum(bh, ch) union = bw * bh + cw * ch - inter return inter / (union + 1e-16 ) def main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) N = int (next (it)) K = int (next (it)) T = int (next (it)) boxes = np.array([[int (next (it)), int (next (it))] for _ in range (N)], dtype=np.float64) centers = boxes[:K].copy() for _ in range (T): old_centers = centers.copy() iou_mat = iou_wh(boxes, centers) distances = 1.0 - iou_mat labels = np.argmin(distances, axis=1 ) new_centers = centers.copy() for k in range (K): members = boxes[labels == k] if len (members) > 0 : new_centers[k] = np.floor(np.mean(members, axis=0 )) center_iou = iou_wh(old_centers, new_centers) diag_iou = np.diag(center_iou) shift = np.sum (1.0 - diag_iou) centers = new_centers if shift < 1e-4 : break centers = centers.astype(np.int64) areas = centers[:, 0 ] * centers[:, 1 ] order = np.argsort(-areas) centers = centers[order] out = [] for w, h in centers: out.append(f"{w} {h} " ) print ("\n" .join(out)) if __name__ == "__main__" : main()
P4475.第2题-终端款型聚类识别 第2题-终端款型聚类识别 - problem_ide - CodeFun2000
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 import sysimport numpy as npdef kmeans (X, K, T ): centers = X[:K].copy() for _ in range (T): old_centers = centers.copy() diff = X[:, None ,:] - centers[None ,:,:] distances = np.linalg.norm(diff, axis=2 ) labels = np.argmin(distances, axis=1 ) for k in range (K): members = X[labels == k] if len (members) >0 : centers[k] = members.mean(axis=0 ) shift = np.linalg.norm(centers - old_centers) if shift < 1e-8 : break counts = np.bincount(labels, minlength=K) return labels, centers, counts def main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) K = int (next (it)) m = int (next (it)) n = int (next (it)) X = np.array( [[float (next (it)) for _ in range (4 )] for _ in range (m)], dtype=np.float64 ) _, _, counts = kmeans(X, K, n) sorted_counts = np.sort(counts) print (" " .join(map (str , sorted_counts))) if __name__ == "__main__" : main()
如果要有序输出 标签 + 数量
1 2 3 order = np.argsort(counts) for k in order: print (f"cluster {k} : {counts[k]} " )
P4571.第2题-网络流量分析 第2题-网络流量分析 - problem_ide - CodeFun2000
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 import numpy as npdef main (): k = int (input ()) centers = np.array( [list (map (float , input ().split())) for _ in range (k)], dtype=np.float64 ) T = int (input ()) m = int (input ()) X = np.array( [list (map (float , input ().split())) for _ in range (m)], dtype=np.float64 ) for _ in range (T): diff = X[:, None , :] - centers[None , :, :] dist = np.linalg.norm(diff, axis=2 ) labels = np.argmin(dist, axis = 1 ) for i in range (k): members = X[labels == i] if len (members) > 0 : centers[i] = np.mean(members, axis=0 ) for center in centers: print (" " .join(f"{x:.2 f} " for x in center)) if __name__ == "__main__" : main()
P3791.第2题-无线网络优化中的基站聚类分析 第2题-无线网络优化中的基站聚类分析 - problem_ide - CodeFun2000
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 import numpy as npfrom decimal import Decimal, ROUND_HALF_EVENdef half_even (x ): return Decimal(str (x)).quantize( Decimal("0.00" ), rounding=ROUND_HALF_EVEN ) def main (): n, K = map (int , input ().split()) X = np.array( [list (map (float , input ().split())) for _ in range (n)], dtype=np.float64 ) centers = X[:K].copy() for _ in range (100 ): old_centers = centers.copy() dist = np.linalg.norm( X[:, None , :] - centers[None , :, :], axis=2 ) label = np.argmin(dist, axis=1 ) for k in range (K): members = X[label == k] if len (members) > 0 : centers[k] = np.mean(members, axis=0 ) shift = np.linalg.norm(centers - old_centers, axis=1 ) if np.all (shift < 1e-6 ): break idx = np.arange(n) clusters = [] for k in range (K): clusters.append(idx[label == k]) dist_X = np.linalg.norm( X[:, None , :] - X[None , :, :], axis=2 ) point_score = np.zeros(n) for i in range (n): label_i = label[i] same_cluster = clusters[label_i] if len (same_cluster) <= 1 : a = 1.0 else : other = same_cluster[same_cluster != i] a = np.mean(dist_X[i, other]) b = float ("inf" ) for k in range (K): if k == label_i: continue if len (clusters[k]) == 0 : continue avg_dist = np.mean(dist_X[i, clusters[k]]) b = min (b, avg_dist) if b == float ("inf" ): s = 0.0 else : s = (b - a) / max (a, b) point_score[i] =s clusters_score = np.zeros(K) for k in range (K): if len (clusters[k]) == 0 : clusters_score[k] = float ("inf" ) else : clusters_score[k] = np.mean(point_score[clusters[k]]) min_score_idx = np.argmin(clusters_score) new_p = centers[min_score_idx] print (f"{half_even(new_p[0 ])} ,{half_even(new_p[1 ])} " ) if __name__ == "__main__" : main()
模拟 P3712.第2题-大模型Attention模块开发 第2题-大模型Attention模块开发 - problem_ide - CodeFun2000
注意矩阵乘法的写法
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 import sysimport numpy as npdef main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) n = int (next (it)) m = int (next (it)) h = int (next (it)) X = np.ones((n, m)) W = np.triu(np.ones((m,h))) Q = X @ W K = X @ W V = X @ W M = ((Q @ K.T)/np.sqrt(h)) Attn = M / M.sum (axis=1 , keepdims=True ) Y = Attn @ V print (f"{round (np.sum (Y))} " ) if __name__ == '__main__' : main()
P3553.第2题-大模型训练MOE场景路由优化算法 第2题-大模型训练MOE场景路由优化算法 - problem_ide - CodeFun2000
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 import sysimport numpy as npdef main (): data = sys.stdin.read().strip().split() it = iter (data) n = int (next (it)) m = int (next (it)) p = int (next (it)) k = int (next (it)) prob = [float (next (it)) for _ in range (n)] if n%m or p>m: print ("error" ) return group_size = n // m if k > group_size*p: print ("error" ) return group = [] for i in range (m): start = i * group_size end = (i + 1 ) * group_size max_prob = max (prob[start:end]) group.append((max_prob, i)) group.sort(key = lambda x: (-x[0 ], x[1 ])) select_group = set (i for _, i in group[:p]) candidates = [] for group_id in select_group: start = group_id*group_size end = (group_id+1 )*group_size for expert_id in range (start, end): candidates.append((prob[expert_id], expert_id)) candidates.sort(key = lambda x: (-x[0 ], x[1 ])) select_experts = [expert_id for _, expert_id in candidates[:k]] select_experts.sort() print (" " .join(map (str , select_experts))) if __name__ == '__main__' : main()
P4938.第2题-LLM 基石 - BPE 分词算法模拟 第2题-LLM 基石 - BPE 分词算法模拟 - problem_ide - CodeFun2000
注意:
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 def bpe (k, s ): arr = list (s) for _ in range (k): if len (arr) < 2 : break cnt = {} for i in range (len (arr) - 1 ): p = (arr[i], arr[i + 1 ]) cnt[p] = cnt.get(p, 0 ) + 1 best = None best_cnt = 0 for p in cnt: if cnt[p] > best_cnt: best_cnt = cnt[p] best = p elif cnt[p] == best_cnt and p < best: best = p if best_cnt == 1 : break new_arr = [] i = 0 while i < len (arr): if i + 1 < len (arr) and arr[i] == best[0 ] and arr[i + 1 ] == best[1 ]: new_arr.append(arr[i] + arr[i + 1 ]) i += 2 else : new_arr.append(arr[i]) i += 1 arr = new_arr return arr def main (): k = int (input ().strip()) s = input ().strip() ans = bpe(k, s) print (" " .join(ans)) if __name__ == "__main__" : main()
list 排序用法 list.sort() 列表原地排序方法,默认升序,加reverse=True为降序
key 接收一个函数,这个函数对每个元素计算一个“排序依据”
可以用 lambda 自定义排序规则
多关键字排序:返回一个元组,这是最常用、最重要的用法
1 data.sort(key=lambda x: (-x[0 ], x[1 ]))
reverse=True:整体反转
-x[0]:只让第一个字段降序
排序字符串如果想忽略大小写
卷积 无 padding、无 dilation 时 $$ H_{\text{out}}= \left\lfloor \frac{H_{\text{in}}-K_h}{s_h} \right\rfloor + 1 \qquad W_{\text{out}} \left\lfloor \frac{W_{\text{in}}-K_w}{s_w} \right\rfloor + 1 $$ 对输出位置 (i, j),实际输入窗口左上角是
1 2 h_start = i * stride_h w_start = j * stride_w
无 dilation 时输出大小 $$ H_{\text{out}} \left\lfloor \frac{H_{\text{in}}+2p-K_h}{s_h} \right\rfloor + 1 $$ 如果 stride = 1,想让输出高度等于输入高度 $$ p = \frac{K_h-1}{2} $$
1 2 3 4 5 6 padded_img = np.pad( img, pad_width=((pad_top, pad_bottom), (pad_left, pad_right)), mode="constant" , constant_values=0 )
如果没有pad则
1 out_h, out_w = img_h - k_h +1 , img_w - k_w +1
dilation 控制卷积核内部采样点之间的间隔
普通卷积 dilation = 1,比如3*3
如果 dilation = 2,则 kernel 在输入采样位置变为
1 2 3 4 5 x . x . x . . . . . x . x . x . . . . . x . x . x
有效卷积核大小是 $$ K_{\text{eff}} = d(K-1)+1 $$ 通用输出尺寸公式 $$ H_{\text{out}} = \left\lfloor \frac{H_{\text{in}}+2p_h-K_{\text{eff},h}}{s_h} \right\rfloor + 1 \qquad W_{\text{out}} \left\lfloor \frac{W_{\text{in}}+2p_w-K_{\text{eff},w}}{s_w} \right\rfloor + 1 $$
P4939.第3题-2D 单通道空洞卷积(Dilated Convolution)底层实现 第3题-2D 单通道空洞卷积(Dilated Convolution)底层实现 - problem_ide - CodeFun2000
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 import numpy as npdef main (): H, W = map (int , input ().split()) img = np.array( list (map (int , input ().split())), dtype=int ).reshape((H,W)) K = int (input ()) kernel = np.array( list (map (int , input ().split())), dtype=int ).reshape((K,K)) stride = int (input ()) pad = int (input ()) d = int (input ()) K_eff = K + (K-1 ) * (d-1 ) H_out = (H + 2 *pad - K_eff)//stride + 1 W_out = (W + 2 *pad - K_eff)//stride + 1 img_pad = np.pad( img, ((pad, pad), (pad, pad)), mode="constant" , constant_values=0 ) out = np.zeros((H_out, W_out), dtype=int ) for i in range (H_out): for j in range (W_out): h_start = i * stride w_start = j * stride patch = np.zeros((K, K), dtype=int ) for ik in range (K): for jk in range (K): patch[ik, jk] = img_pad[ h_start + ik * d, w_start + jk * d ] out[i, j] = np.sum (patch * kernel) for row in out: print (" " .join(map (str , row))) if __name__ == "__main__" : main()
P3493.第2题-Group卷积实现 第2题-Group卷积实现 - problem_ide - CodeFun2000
普通卷积:每个输出通道看所有输入通道
假设输入有 4 个通道:
输出有 2 个通道:
普通卷积的group就是1
连接关系是
1 2 3 x0 x1 x2 x3 y0 ✓ ✓ ✓ ✓ y1 ✓ ✓ ✓ ✓
普通卷积的 kernel shape 是
Group 卷积:每个输出通道只看本组输入通道
还是 in_c = 4 out_c = 2,设置groups = 2
输入通道分成 2 组
1 2 Group 0 输入通道: x0, x1 Group 1 输入通道: x2, x3
1 2 3 x0 x1 x2 x3 y0 ✓ ✓ y1 ✓ ✓
1 2 3 4 5 6 7 Group 0: 输入 x0, x1 输出 y0 Group 1: 输入 x2, x3 输出 y1
最后把所有 group 的输出通道拼起来
注意,始终保持kernel_c的大小和实际进来的数据通道数相同,这里kernel_c = in_c//group
如果不相等,就会出现二者不能逐元素相乘的情况
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 import numpy as npdef error (): print ("-1" ) print ("-1" ) def main (): img = np.array(list (map (int , input ().split())), dtype= int ) batch_size, in_c, img_h, img_w = map (int , input ().split()) kernel = np.array(list (map (int , input ().split())), dtype= int ) out_c, k_c, k_h, k_w = map (int , input ().split()) group = int (input ()) if in_c%group or out_c%group: error() return if in_c // group != k_c: error() return img = img.reshape((batch_size, in_c, img_h, img_w)) kernel = kernel.reshape((out_c, k_c, k_h, k_w)) out_h, out_w = img_h - k_h +1 , img_w - k_w +1 out = np.zeros((batch_size, out_c, out_h, out_w), dtype = int ) in_c_group = in_c//group out_c_group = out_c//group for batch in range (batch_size): for g in range (group): in_c_start = in_c_group*g in_c_end = in_c_group*(g + 1 ) out_c_start = out_c_group*g out_c_end = out_c_group*(g + 1 ) img_group = img[batch, in_c_start:in_c_end, :, :] kernel_group = kernel[out_c_start:out_c_end, :, :,:] for oc in range (out_c_group): for i in range (out_h): for j in range (out_w): window = img_group[:, i:i+k_h, j:j+k_w] out[batch, out_c_start+oc, i, j] = np.sum (window * kernel_group[oc]) out_data = out.reshape(-1 ).tolist() out_shape = [batch_size, out_c, out_h, out_w] print (" " .join(map (str , out_data))) print (" " .join(map (str , out_shape))) if __name__ == "__main__" : main()
P4482.第3题-带Padding的卷积计算 第3题-带Padding的卷积计算 - problem_ide - CodeFun2000
pad函数
1 2 3 4 5 6 pad_img = np.pad( img_group, pad_width=((0 , 0 ), (pad, pad), (pad, pad)), mode="constant" , constant_values=0 )
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 import sysimport numpy as npdef main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) m = int (next (it)) n = int (next (it)) kernel = np.array([[int (next (it)) for _ in range (m)] for _ in range (m)]) img = np.array([[int (next (it)) for _ in range (n)] for _ in range (n)]) pad = m // 2 img_pad = np.pad( img, ((pad, pad), (pad, pad)), mode='constant' , constant_values=0 ) res = np.zeros_like(img) for i in range (n): for j in range (n): window = img_pad[i:i+m, j:j+m] res[i][j] = np.sum (window * kernel) for row in res: print (" " .join(map (str , row))) if __name__ == '__main__' : main()
逻辑回归 二分类 logistic regression 对于二分类任务,标签只有两类: $$ y_i\in{0,1} $$ 设: $$ X\in\mathbb{R}^{N\times d},\quad w\in\mathbb{R}^{d\times 1},\quad b\in\mathbb{R},\quad y\in\mathbb{R}^{N\times 1} $$
前向传播:单样本形式 $$ z_i=X_iw+b $$ 预测概率为(表示样本 i 属于正类 1 的概率): $$ p_i=\hat y_i=\sigma(z_i)=\frac{1}{1+e^{-z_i}}\qquad p_i=P(y_i=1\mid X_i) $$ batch 形式 $$ z=Xw+b,\quad p=\sigma(z) $$ 对于单个样本,二分类交叉熵为 $$ L_i=-\left[y_i\log p_i+(1-y_i)\log(1-p_i)\right] $$ Sigmoid 函数为: $$ \sigma(z)=\frac{1}{1+e^{-z}} $$ 其导数为: $$ \frac{\partial \sigma(z)}{\partial z}=\sigma(z)(1-\sigma(z)) $$ 因为$p_i = \sigma(z)$,所以 $$ \frac{\partial p_i}{\partial z_i}=p_i(1-p_i) $$ 先对$p_i$求导 $$ \frac{\partial L_i}{\partial p_i} = -\left[ \frac{y_i}{p_i} - \frac{1-y_i}{1-p_i} \right] $$ 根据链式法则,并且代入得到 $$ \frac{\partial L_i}{\partial z_i} = \frac{\partial L_i}{\partial p_i} \frac{\partial p_i}{\partial z_i}= \frac{p_i-y_i}{p_i(1-p_i)} \cdot p_i(1-p_i)= p_i-y_i $$ 所以二分类 sigmoid + BCE 有一个重要结论 $$ \color{red} \frac{\partial L_i}{\partial z_i}=p_i-y_i $$ 对参数 w 和 b 求导 $$ \frac{\partial z_i}{\partial w}=X_i^\top,\quad \frac{\partial z_i}{\partial b}=1 $$
注意,这里默认$X_i\in\mathbb{R}^{1\times d}$,在代码中是(d,)
根据链式法则 $$ \frac{\partial L_i}{\partial w}= X_i^\top(p_i-y_i) \qquad \frac{\partial L_i}{\partial b} p_i-y_i $$Batch Loss $$ \frac{\partial J}{\partial w} = \frac{1}{N}X^\top(p-y) \qquad \frac{\partial J}{\partial b} \frac{1}{N}\mathbf{1}^\top(p-y) \frac{1}{N}\sum_{i=1}^{N}(p_i-y_i) $$ 通常只对 w 正则化,不对 b 正则化 $$ w \leftarrow w - \alpha(\hat y-y)x\\ b \leftarrow b - \alpha(\hat y-y) $$ 正则项梯度为 $$ \frac{\partial}{\partial w} \frac{\lambda}{2N}|w|2^2 = \frac{\lambda}{N}w $$ 不加正则时 $$ w\leftarrow w-\eta\frac{1}{N}X^\top(p-y)\qquad b\leftarrow b-\eta\frac{1}{N}\sum {i=1}^{N}(p_i-y_i) $$ 加正则时 $$ w \leftarrow w-\eta \left[ \frac{1}{N}X^\top(p-y) + \frac{\lambda}{N}w \right] $$ 从形式上,softmax的和sigmoid的梯度更新形式相同
代码模板 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 def standardize (train_x, test_x, eps=1e-8 ): """ 用训练集统计量标准化 train_x 和 test_x 注意:mean/std 只能从 train_x 计算,不能从 test_x 计算,否则会数据泄漏 """ mean = np.mean(train_x, axis=0 , keepdims=True ) std = np.std(train_x, axis=0 , keepdims=True , ddof=0 ) std = np.where(std < eps, 1.0 , std) train_x_std = (train_x - mean) / std test_x_std = (test_x - mean) / std return train_x_std, test_x_std def cross_entropy (p, y ): return -np.mean( y * np.log(p + 1e-12 ) + (1 - y) * np.log(1 - p + 1e-12 ) ) def sigmoid (z ): """ 数值稳定版 sigmoid """ z = np.clip(z, -500 , 500 ) return 1.0 / (1.0 + np.exp(-z)) def train_binary_sigmoid ( train_x, train_y, learning_rate=0.1 , lam=1e-4 , epochs=600 , ): """ 二分类 Logistic Regression: z = Xw + b p = sigmoid(z) Loss: J = BCE + lambda/(2N) * ||w||_2^2 梯度: grad_w = X^T(p-y)/N + lambda/N * w grad_b = mean(p-y) """ N, D = train_x.shape X = train_x.astype(np.float64) y = train_y.astype(np.float64).reshape(-1 ) lr = learning_rate W = np.zeros(D, dtype=np.float64) b = 0.0 for epoch in range (epochs): z = X @ W + b p = sigmoid(z) delta = p - y grad_W = X.T @ delta / N + (lam / N) * W grad_b = np.mean(delta) W -= lr * grad_W b -= lr * grad_b return W, b def predict (test_x, W, b ): logits = test_x @ W + b prob = softmax(logits) return np.argmax(prob, axis=1 )
P3872.第3题-基于逻辑回归的意图分类器 第3题-基于逻辑回归的意图分类器 - problem_ide - CodeFun2000
注意one-hot编码,feat的定义
SGD 随机梯度下降,batch为1,所以需要在循环里嵌套一个单样本更新,比较少见,大部分情况下还是一个batch更新一次
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 import sysimport numpy as npdef one_hot (s ): x = np.zeros(7 , dtype=np.float64) for ch in s: idx = ord (ch)-ord ('A' ) if 0 <= idx < 7 : x[idx] = 1.0 return x def sigmoid (z ): z = np.clip(z, -500 , 500 ) return 1.0 / (1.0 + np.exp(-z)) def main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) N = int (next (it)) M = int (next (it)) X = [] y = [] for i in range (N): feat = one_hot(next (it)) X.append(feat) y.append(float (next (it))) X = np.array(X, dtype=np.float64) y = np.array(y, dtype=np.float64) X_test = [] for _ in range (M): feat = one_hot(next (it)) X_test.append(feat) X_test = np.array(X_test, dtype=np.float64) w = np.zeros(7 , dtype=np.float64) b = 0.0 alpha = 0.1 max_iter = 20 for _ in range (max_iter): for i in range (N): x_i = X[i] y_i = y[i] z = x_i @ w + b p = sigmoid(z) delta = p - y_i w -= alpha * delta * x_i b -= alpha * delta probs = sigmoid(X_test @ w + b) for p in probs: label = 1 if p > 0.5 else 0 print (label) if __name__ == '__main__' : main()
P4344.第3题-商品购买预测 第3题-商品购买预测 - problem_ide - CodeFun2000
全样本作为一个batch
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 import numpy as npdef sigmoid (z ): z = np.clip(z, -500 , 500 ) return 1.0 /(1.0 +np.exp(-z)) def cross_entropy (p, y ): return -np.mean( y * np.log(p + 1e-12 ) + (1 - y) * np.log(1 - p + 1e-12 ) ) def main (): data = input ().split() n = int (data[0 ]) max_iter = int (data[1 ]) lr = float (data[2 ]) lam = float (data[3 ]) tol = float (data[4 ]) x = [] y = [] for _ in range (n): data = input ().split() x.append(data[:-1 ]) y.append(data[-1 ]) x = np.array(x, dtype=np.float64) y = np.array(y, dtype=np.float64) n, D = x.shape W = np.zeros(D, dtype=np.float64) b = 0.0 prev_loss = None for _ in range (max_iter): z = x @ W + b p = sigmoid(z) ce_loss = cross_entropy(p, y) l2_loss = lam/(2 *n) *np.sum (W*W) loss = ce_loss + l2_loss if prev_loss is not None and abs (loss- prev_loss)< tol: break prev_loss = loss delta = p - y g_W = x.T @ delta / n + lam * W/ n g_b = np.mean(delta) W -= lr * g_W b -= lr * g_b m = int (input ()) x_test = [] for _ in range (m): x_test.append(input ().split()) x_test = np.array(x_test, dtype=np.float64) for feat in x_test: z = feat @ W + b p = sigmoid(z) pred = 1 if p>=0.5 else 0 print (f"{pred} {p:.4 f} " ) if __name__ == "__main__" : main()
P4905.第2题-5G基站设备故障风险预警 第2题-5G基站设备故障风险预警 - problem_ide - CodeFun2000
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 import numpy as npdef sigmoid (z ): z = np.clip(z, -500 , 500 ) return 1.0 /(1.0 +np.exp(-z)) def main (): data = input ().split() N = int (data[0 ]) iter = int (data[1 ]) lr = float (data[2 ]) x1 = [] x2 = [] y = [] for _ in range (N): data = input ().split() x1.append(float (data[0 ])) x2.append(float (data[1 ])) y.append(float (data[2 ])) x1 = np.array(x1, dtype=np.float64).reshape((N,1 )) x2 = np.array(x2, dtype=np.float64).reshape((N,1 )) y = np.array(y, dtype=np.float64) w1 = np.zeros(1 ) w2 = np.zeros(1 ) b = 0.0 for _ in range (iter ): z = x1 @ w1 + x2 @ w2 + b p = sigmoid(z) delta = p - y dw1 = x1.T @ delta /N dw2 = x2.T @ delta /N db = np.mean(delta, axis=0 ) w1 -= lr*dw1 w2 -= lr*dw2 b -= lr*db data = list (map (float , input ().split())) feat1 = np.array(data[0 ], dtype=np.float64).reshape((1 ,1 )) feat2 = np.array(data[1 ], dtype=np.float64).reshape((1 ,1 )) z = feat1 @ w1 + feat2 @ w2 + b pred = sigmoid(z) print (f"{pred[0 ]:.4 f} " ) if __name__ == "__main__" : main()
决策树 决策树可以理解成一串自动生成的 if-else 判断规则
每次选择一个特征和一个阈值,把样本分成更“纯”的两部分,不断重复,直到叶子节点足够纯,最后用叶子节点中的多数类别作为预测结果
根据决策规则来判断往左还是往右,或者说是直接输出
P3492.第1题-基于决策树预判资源调配优先级 第1题-基于决策树预判资源调配优先级 - problem_ide - CodeFun2000
理解定义,把node类写出来,然后按顺序遍历就行
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 class Node : def __init__ (self, feature_index, threshold, left, right, label ): self .feature_index = feature_index self .threshold = threshold self .left = left self .right = right self .label = label def main (): f, m, n = map (int , input ().split()) tree = [] for _ in range (m): feature_index, threshold, left, right, label = input ().split() tree.append(Node(int (feature_index), float (threshold), int (left), int (right), int (label))) for _ in range (n): feat = list (map (float , input ().split())) current = 0 while True : node = tree[current] if node.feature_index == -1 : print (node.label) break if feat[node.feature_index] <= node.threshold: current = node.left else : current = node.right if __name__ == '__main__' : main()
P4969.第2题-随机森林交易风控算法 第2题-随机森林交易风控算法 - problem_ide - CodeFun2000
注意给的输入的idx, left, right是否从0开始,如果下标方便,可以在创建树的时候减1
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 class TreeNode : def __init__ (self, idx = None , val = None , left = None , right = None , label = None ): self .idx = idx self .val = val self .left = left self .right = right self .label = label def decision (feat, tree ): out = [] for single_tree in tree: node = single_tree[0 ] while True : if node.val is None : out.append(node.label) break elif feat[node.idx] <= node.val: node = single_tree[node.left] else : node = single_tree[node.right] return out def main (): T, M, N = map (int , input ().split()) tree = [] for _ in range (T): single_tree = [] K = int (input ()) for _ in range (K): node_data = list (map (int , input ().split())) if node_data[0 ] == 1 : single_tree.append( TreeNode( idx = node_data[1 ]-1 , val = node_data[2 ], left = node_data[3 ]-1 , right = node_data[4 ]-1 ) ) elif node_data[0 ] == 0 : single_tree.append( TreeNode(label=node_data[1 ]) ) tree.append(single_tree) for _ in range (N): feat = list (map (int , input ().split())) out = decision(feat, tree) pre_1 = out.count(1 ) pre_0 = out.count(0 ) print ("0" ) if pre_0 >= pre_1 else print ("1" ) if __name__ == "__main__" : main()
P3480.第3题-F1值最优的决策树剪枝 第3题-F1值最优的决策树剪枝 - problem_ide - CodeFun2000
碰到混淆矩阵可以转numpy来做
注意index的取值,从0还是1开始
剪枝递归按每个node走一遍就行了
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 import numpy as npclass TreeNode : def __init__ (self, left, right, idx, val, label ): self .left = left self .right = right self .idx = idx self .val = val self .label = label def Tree_decision (feat, tree ): y_pred = [] for emb in feat: node = tree[0 ] while True : if node.left == 0 : y_pred.append(node.label) break elif emb[node.idx-1 ] <= node.val: node = tree[node.left-1 ] else : node = tree[node.right-1 ] return y_pred def F1_score (y_pred, y_true ): y_pred = np.asarray(y_pred, dtype=np.float64) y_true = np.asarray(y_true, dtype=np.float64) tp = np.sum ((y_pred == 1 ) & (y_true == 1 )) fp = np.sum ((y_pred == 1 ) & (y_true == 0 )) fn = np.sum ((y_pred == 0 ) & (y_true == 1 )) if tp == 0 : f1 = 0 else : f1 = 2 *tp/(2 *tp + fp + fn) return f1 def main (): N, M, K = map (int , input ().split()) tree = [] for _ in range (N): node_data = list (map (int , input ().split())) node = TreeNode( left = node_data[0 ], right = node_data[1 ], idx = node_data[2 ], val = node_data[3 ], label = node_data[4 ] ) tree.append(node) feat = [] y_true = [] for _ in range (M): sample_data = list (map (int , input ().split())) emb = sample_data[:K] label = sample_data[-1 ] feat.append(emb) y_true.append(label) best_f1 = 0.0 for node in tree: old_left, old_right = node.left, node.right node.left = 0 node.right = 0 y_pred = Tree_decision(feat, tree) f1 = F1_score(y_pred, y_true) best_f1 = max (best_f1, f1) node.left, node.right = old_left, old_right print (f"{best_f1:.6 f} " ) if __name__ == "__main__" : main()
并查集 最重要的是定义class UnionFind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class UnionFind : def __init__ (self, n ): self .parent = [i for i in range (n)] self .size = [1 ] * n self .count = n def find (self, x ): while self .parent[x] != x: self .parent[x] = self .parent[self .parent[x]] x = self .parent[x] return x def union (self, x, y ): rx = self .find(x) ry = self .find(y) if rx == ry: return if self .size[rx] < self .size[ry]: rx, ry = ry, rx self .parent[ry] = rx self .size[rx] += self .size[ry] self .count -= 1
P4238.第2题-利用大规模预训练模型实现智能告警聚类与故障诊断 第2题-利用大规模预训练模型实现智能告警聚类与故障诊断 - problem_ide - CodeFun2000
只要知道相似度的定义就好,并且对零向量做额外处理 $$ \mathrm{sim}(i,j) = \frac{v_1\cdot v_2}{||v_1||||v_2||} $$
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 import sysimport numpy as npclass UnionFind : def __init__ (self, n ): self .parents = [i for i in range (n)] self .size = [1 ] * n def find (self, x: int ) -> int : while self .parents[x] != x: self .parents[x] = self .parents[self .parents[x]] x = self .parents[x] return x def union (self, x, y ): rx = self .find(x) ry = self .find(y) if rx == ry: return if self .size[rx] < self .size[ry]: rx, ry = ry, rx self .parents[ry] = rx self .size[rx] += self .size[ry] def main (): data = sys.stdin.readlines() n = len (data) if n == 0 : print ("0" ) return emb = [] dim = None for i in range (n): warning_data = data[i].split() emb_data = warning_data[1 :] if dim is None : dim = len (emb_data) if dim != len (emb_data): print ("0" ) return emb.append(list (map (float , emb_data))) emb = np.array(emb, dtype=np.float64) uf = UnionFind(n) l2 = np.sqrt(np.sum (emb**2 , axis= 1 )) for i in range (n): for j in range (i+1 , n): if l2[i] == 0 or l2[j] == 0 : continue sim = np.dot(emb[i], emb[j])/(l2[i]*l2[j]) if sim>=0.95 : uf.union(i,j) print (max (uf.size)) if __name__ == "__main__" : main()
P3874.第2题-数据聚类及噪声点识别[重点] 第2题-数据聚类及噪声点识别 - problem_ide - CodeFun2000
DBSCAN 是一种基于密度的聚类算法,不要求你提前指定簇的数量,而是用两个参数判断“哪里足够密集”
参数
含义
eps
邻域半径:两个点距离小于等于这个值,就认为它们是邻居
min_samples
一个点的邻域内至少有多少个点,才算“核心
分为三类点:核心点、边界点、噪声点
对比项
K-means
DBSCAN
是否需要指定簇数量
需要指定 k
不需要
是否能发现非球形簇
很弱
可以
是否能识别噪声
不直接支持
支持
对参数敏感吗
对 k 和初值敏感
对 eps 和 min_samples 敏感
适合什么形状
球形、凸形簇
任意形状簇
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 import sysimport numpy as npclass UnionFind : def __init__ (self, n ): self .parent = [i for i in range (n)] self .size = [1 ] * n def find (self, x ) -> int : while self .parent[x] != x: self .parent[x] = self .parent[self .parent[x]] x = self .parent[x] return x def union (self, x, y ): rx = self .find(x) ry = self .find(y) if rx == ry: return if self .size[rx] < self .size[ry]: rx, ry = ry, rx self .parent[ry] = rx self .size[rx] += self .size[ry] def main (): data = input ().split() eps, min_samples, N = float (data[0 ]), int (data[1 ]), int (data[2 ]) X = [] for _ in range (N): x = list (map (float , input ().split())) X.append(x) X = np.array(X, dtype=np.float64) dist = np.linalg.norm(X[:,None ,:] - X[None ,:,:], axis=2 ) neighbors_count = np.sum (dist < eps, axis=1 ) centers = [] for i in range (N): if neighbors_count[i] >= min_samples: centers.append(i) if len (centers) == 0 : print (f"0 {N} " ) return uf = UnionFind(N) for a in range (len (centers)): for b in range (a + 1 , len (centers)): i = centers[a] j = centers[b] if dist[i][j] < eps: uf.union(i, j) labels = np.full(N, -1 ) root_to_label = {} next_label = 0 for i in range (N): if i in centers: root = uf.find(i) if root not in root_to_label: root_to_label[root] = next_label next_label+=1 labels[i] = root_to_label[root] for i in range (N): if i in centers: continue for j in range (N): if j in centers and dist[i][j] < eps: labels[i] = labels[j] break cluster_count = next_label noise_count = np.sum (labels == -1 ) print (cluster_count, noise_count) if __name__ == "__main__" : main()
多写了一个统计和输出,可能会涉及到
P4343.第2题-实体匹配结果合并问题 第2题-实体匹配结果合并问题 - problem_ide - CodeFun2000
几个注意点:
set的融合用.update()
set的查交集用&
其余流程类似,查完并集以后设置输出
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 class UnionFind : def __init__ (self, n ): self .parent = [i for i in range (n)] self .size = [1 ] * n def find (self, x ): while self .parent[x] != x: self .parent[x] = self .parent[self .parent[x]] x = self .parent[x] return x def union (self, x, y ): rx = self .find(x) ry = self .find(y) if rx == ry: return if self .size[rx] < self .size[ry]: rx, ry = ry, rx self .parent[ry] = rx self .size[rx] += self .size[ry] def main (): N = int (input ()) data = [] for _ in range (N): query = input ().split() data.append(set (query)) uf = UnionFind(N) for i in range (N): for j in range (i + 1 , N): if data[i] & data[j]: uf.union(i, j) merged = {} for i in range (N): root = uf.find(i) if root not in merged: merged[root] = set () merged[root].update(data[i]) ans = [] for group in merged.values(): ans.append(sorted (group)) ans.sort() for group in ans: print (" " .join(group)) if __name__ == '__main__' : main()
矩阵 P4538.第2题-基于混淆矩阵,推导分类模型的核心评估指标 第2题-基于混淆矩阵,推导分类模型的核心评估指标 - problem_ide - CodeFun2000
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 import numpy as npdef cal_func (y_pred, y_true, weight ): c = len (weight) P = np.zeros_like(weight) R = np.zeros_like(weight) F1 = np.zeros_like(weight) for label in range (c): tp = np.sum ((y_pred == label) & (y_true == label)) fp = np.sum ((y_pred == label) & (y_true != label)) fn = np.sum ((y_pred != label) & (y_true == label)) if tp > 0 : P[label] = tp / (tp+fp) R[label] = tp / (tp+fn) F1[label] = 2 *tp/(2 *tp+fp+fn) w_P = np.sum (weight * P, axis=0 ) w_R = np.sum (weight * R, axis=0 ) w_F1 = np.sum (weight * F1, axis=0 ) return w_P, w_R, w_F1 def main (): y_pred = np.array(list (map (int , input ().split())), dtype=int ) y_true = np.array(list (map (int , input ().split())), dtype=int ) weight = np.array(list (map (float , input ().split())), dtype=np.float64) w_P, w_R, w_F1 = cal_func(y_pred, y_true, weight) print (f"{w_P:.2 f} {w_R:.2 f} {w_F1:.2 f} " ) if __name__ == "__main__" : main()
线性回归 在最小二乘法有解析解的情况下使用
读入训练样本 → 构造设计矩阵 X → 用最小二乘求参数 W → 对新样本补截距项 → X_pred @ W 预测 → 按题意取整输出
1 W = np.linalg.lstsq(X, Y, rcond=None )[0 ]
$$ \hat{Y} = XW $$
假设每个样本有 3 个特征,线性回归可以写成 $$ \hat{y} = w_0 + w_1x_1 + w_2x_2 + w_3x_3 $$ 所以一条训练数据:[x1 x2 x3 price] -> [1.0, x1, x2, x3]
训练数据一般不可能完全满足$XW = Y$
所以线性回归求的是最小化平方误差 $$ \min_W |XW - Y|_2^2 $$ 如果要用np.linalg.solve需要先通过正规方程转成方阵 $$ X^\top X W = X^\top Y $$ 然后写:
1 W = np.linalg.solve(X.T @ X, X.T @ Y)
如果是非线性,在构建的时候需要注意输入的构建
比如一个二阶多项式 $$ \hat y = w_0 + w_1x+w_2x^2 $$ 这里一条训练数据就是:[x] -> [1.0 x x**2]
注意,这里可以变换格式,不变的就是要对每个地方的数做对应的处理,也就是保证后面输入的X就是对应的x幂次
P4532.第2题-使用线性回归预测手机售价 第2题-使用线性回归预测手机售价 - problem_ide - CodeFun2000
注意四舍五入的写法
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 import sysimport numpy as npfrom decimal import Decimal, ROUND_HALF_UPdef round_half_up (x ): return Decimal(str (x)).quantize(Decimal('1' ), rounding=ROUND_HALF_UP) def main (): data = sys.stdin.read().strip().split() if not data: return it = iter (data) K = int (next (it)) x = [] y = [] for _ in range (K): x1 = int (next (it)) x2 = int (next (it)) x3 = int (next (it)) price = int (next (it)) x.append([1.0 , x1, x2, x3]) y.append(price) X = np.array(x, dtype=np.float64) Y = np.array(y, dtype=np.float64) W = np.linalg.solve(X.T @ X, X.T @ Y) N = int (next (it)) ans = [] for _ in range (N): x1 = int (next (it)) x2 = int (next (it)) x3 = int (next (it)) feat = np.array([1.0 , x1, x2, x3], dtype=np.float64) pred = feat @ W ans.append(round_half_up(pred)) print (" " .join(map (str , ans))) if __name__ == '__main__' : main()
P4572.第3题-动态区间的多项式岭回归 第3题-动态区间的多项式岭回归 - problem_ide - CodeFun2000 40min
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 import numpy as npdef main (): M, N = map (int , input ().split()) X = [] Y = [] miss_idx = [] for i in range (N): data = input () if data[0 ] == "M" : miss_idx.append(i) Y.append(0 ) else : Y.append(float (data)) X.append([1.0 , i+1 , (i+1 )**2 ]) X = np.array(X, dtype=np.float64) Y = np.array(Y, dtype=np.float64) for i in range (M): last = miss_idx[i - 1 ] if i!=0 else -1 next = miss_idx[i + 1 ] if i!=M-1 else N interval_X = np.concatenate([ X[last+1 :miss_idx[i], :], X[miss_idx[i]+1 :next , :] ]) interval_Y = np.concatenate([ Y[last+1 :miss_idx[i]], Y[miss_idx[i]+1 :next ] ]) interval_W = np.linalg.solve( interval_X.T @ interval_X + 0.1 * np.eye(3 ), interval_X.T @ interval_Y ) Y[miss_idx[i]] = X[miss_idx[i]] @ interval_W for i in range (len (miss_idx)): print (f"Missing_{i+1 } : {Y[miss_idx[i]]:.2 f} " ) if __name__ == "__main__" : main()
KNN 一个最小模板
算距离 → 排序取前 K 个 → 取标签 → 投票 → 输出票数最多的标签
1 2 3 4 5 6 7 8 9 10 diff = X - feat[None , :] dist = np.linalg.norm(diff, axis=1 ) neighbors_idx = np.argsort(dist)[:K] neighbors_label = Y[neighbors_idx] votes = np.bincount(neighbors_label) ans = np.argmax(votes) print (ans)
P3479.第2题-标签样本数量 第2题-标签样本数量 - problem_ide - CodeFun2000
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 import numpy as npdef main (): k, m, n, s = map (int , input ().split()) feat = np.array(list (map (float , input ().split())), dtype=np.float64) x = [] y = [] for _ in range (m): data = list (map (float , input ().split())) x.append(data[:-1 ]) y.append(data[-1 ]) x = np.array(x, dtype=np.float64) y = np.array(y, dtype=int ) dist = np.linalg.norm(x - feat, axis=1 ) neighbors = np.argsort(dist)[:k] neighbors_label = y[neighbors] votes = np.bincount(neighbors_label, minlength=s) label = np.argmax(votes) print (f"{label} {votes[label]} " ) if __name__ == "__main__" : main()
基于KNN的语音数据分类 第3题-基于KNN的语音数据分类 - problem_ide - CodeFun2000
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 import numpy as npdef main (): N, K = map (int , input ().split()) X = [] Y = [] for _ in range (N): data = list (map (float , input ().split())) X.append(data[:-1 ]) Y.append(data[-1 ]) X = np.array(X, dtype=np.float64) Y = np.array(Y, dtype=int ) feat = np.array(list (map (float , input ().split())), dtype=np.float64) dist = np.linalg.norm((X-feat), axis= 1 ) neighbors = np.argsort(dist)[:K] neighbors_label = Y[neighbors] votes = np.bincount(neighbors_label) ans = np.argmax(votes) print (ans) if __name__ == '__main__' : main()
双指针 P4547.第2题-基于样本纯净度指标的大模型训练数据清洗方法 第2题-基于样本纯净度指标的大模型训练数据清洗方法 - problem_ide - CodeFun2000
就是最长无重复子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def main (): s = input () n = len (s) window = set () left = ans = 0 for right in range (n): while s[right] in window: window.remove(s[left]) left+=1 window.add(s[right]) ans = max (ans, right - left+1 ) print (ans) if __name__ == "__main__" : main()
二叉树 1 2 3 4 5 6 class TreeNode : def __init__ (self, val=0 , left=None , right=None ): self .val = val self .left = left self .right = right
建树时有两种输入和输出,一种是紧凑的,一种是非紧凑的
完全二叉树数组下标建树 用固定下标关系建树 $$ left = 2i + 1,\quad right = 2i + 2 $$ 比如[1, None, 2, None, None, 3, 4]
建树:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def build_full_tree (arr ): """ 按全 null 站位数组建树: 左孩子下标 = 2*i+1 右孩子下标 = 2*i+2 """ def dfs (i ): if i >= len (arr) or arr[i] is None : return None node = TreeNode(arr[i]) node.left = dfs(2 * i + 1 ) node.right = dfs(2 * i + 2 ) return node return dfs(0 )
输出数组:
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 def arr_full_tree (root ): """ 输出带全 None 站位的数组 """ if root is None : return [] val = {} q = deque([(root, 0 )]) max_idx = 0 while q: node, idx = q.popleft() if node is None : continue val[idx] = node.val max_idx = max (max_idx, idx) q.append((node.left, 2 * idx + 1 )) q.append((node.right, 2 * idx + 2 )) arr = [val.get(i, None ) for i in range (max_idx + 1 )] return arr
层序遍历数组建树 只给真实节点继续分配左右孩子,None 节点不再展开孩子
建树:
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 def build_tree_compact (arr ): """ 按不带全 None 的层序数组建树 例: [1, 2, 3, None, 4, None, 5, 6] 注意: None 节点不会继续展开孩子 """ if not arr or arr[0 ] is None : return None root = TreeNode(arr[0 ]) q = deque([root]) idx = 1 while q: node = q.popleft() if idx < len (arr) and arr[idx] is not None : node.left = TreeNode(arr[idx]) q.append(node.left) idx+=1 if idx < len (arr) and arr[idx] is not None : node.right = TreeNode(arr[idx]) q.append(node.right) idx+=1 return root
输出数组:
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 def serialize_compact (root ): """ 输出不带全 None 的层序数组 会保留中间必要的 None, 但会删除末尾多余的 None """ def arr_compact_tree (root ): if root is None : return [] arr = [] q = deque([root]) while q: node = q.popleft() if node is None : q.append(None ) continue arr.append(node.val) q.append(node.left) q.append(node.right) while arr and arr[-1 ] is None : arr.pop() return arr
格式化输出 把None换为”null”形式,也许需要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def format_tree_array (arr ): """ 把 Python 数组输出成题目常见格式: None -> null 例: [1, None, 2] -> "[1,null,2]" """ items = [] for x in arr: if x is None : items.append("null" ) else : items.append(str (x)) return "[" + "," .join(items) + "]"
P3657.第2题-二叉树中序遍历的第k个祖先节点 第2题-二叉树中序遍历的第k个祖先节点 - problem_ide - CodeFun2000
两个目标:
这两件事情可以在一个dfs里实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def dfs (node ): if node is None : return False if node.val == find_val: return True if dfs(node.left): return True path.append(node.val) if dfs(node.right): return True path.pop() return False
然后要知道怎么建树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class TreeNode : def __init__ (self, val = 0 , left = None , right =None ): self .val = val self .left = left self .right = right def build_tree_compact (arr ): root = TreeNode(arr[0 ]) q = deque([root]) i = 1 while q and i<len (arr): node = q.popleft() if i < len (arr) and arr[i] is not None : node.left = TreeNode(arr[i]) q.append(node.left) i+=1 if i < len (arr) and arr[i] is not None : node.right = TreeNode(arr[i]) q.append(node.right) i+=1 return root
拼起来就是这道题了
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 from collections import dequeclass TreeNode : def __init__ (self, val = 0 , left = None , right =None ): self .val = val self .left = left self .right = right def build_tree_compact (arr ): root = TreeNode(arr[0 ]) q = deque([root]) i = 1 while q and i<len (arr): node = q.popleft() if i < len (arr) and arr[i] is not None : node.left = TreeNode(arr[i]) q.append(node.left) i+=1 if i < len (arr) and arr[i] is not None : node.right = TreeNode(arr[i]) q.append(node.right) i+=1 return root def main (): tree_data = input ().split() find_val, k = map (int , input ().split()) tree_arr = [] for x in tree_data: if x == "#" : tree_arr.append(None ) else : tree_arr.append(int (x)) tree_root = build_tree_compact(tree_arr) path = [] def dfs (node ): if node is None : return False if node.val == find_val: return True if dfs(node.left): return True path.append(node.val) if dfs(node.right): return True path.pop() return False dfs(tree_root) if len (path) < k: print ("-1" ) return print (path[k-1 ]) if __name__ == "__main__" : main()
P4476.第3题-Prompt上下文信息精简:找出二叉树中的最大值子树 第3题-Prompt上下文信息精简:找出二叉树中的最大值子树 - problem_ide - CodeFun2000
熟悉建树和输出数组的写法
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 from collections import dequeclass TreeNode : def __init__ (self, val = None , left = None , right = None ): self .val = val self .left = left self .right = right def is_null (x ): return x is None or x =="null" def build_full_null_tree (arr ): def dfs (i ): if i >= len (arr) or is_null(arr[i]): return None node = TreeNode(arr[i]) node.left = dfs(i*2 +1 ) node.right = dfs(i*2 +2 ) return node return dfs(0 ) def arr_full_null_tree (root ): q = deque([(root, 0 )]) pos = {} max_idx = 0 while q: node, idx = q.popleft() if node is None : continue pos[idx] = node.val max_idx = max (max_idx, idx) q.append((node.left, 2 *idx+1 )) q.append((node.right, 2 *idx+2 )) return [pos.get(i, None ) for i in range (max_idx+1 )] def main (): data = input () if data == "[]" : print ("[]" ) return data = data[1 :-1 ] tree_list = data.strip().split(',' ) tree_arr = [] for node_val in tree_list: if is_null(node_val): tree_arr.append(None ) else : tree_arr.append(int (node_val)) root = build_full_null_tree(tree_arr) best_root = None best_sum = float ("-inf" ) def dfs (node ): nonlocal best_root, best_sum if node is None : return 0 left_sum = dfs(node.left) right_sum = dfs(node.right) if left_sum <= 0 : node.left = None if right_sum <= 0 : node.right = None cur_sum = node.val if left_sum > 0 : cur_sum +=left_sum if right_sum > 0 : cur_sum +=right_sum if cur_sum > best_sum: best_sum = cur_sum best_root = node return cur_sum dfs(root) best_arr = arr_full_null_tree(best_root) ans = [] for x in best_arr: if x is None : ans.append("null" ) else : ans.append(str (x)) print ("[" + "," .join(ans) + "]" ) if __name__ == "__main__" : main()
结构化剪枝 P4518.第2题-基于剪枝的神经网络模型压缩 第2题-基于剪枝的神经网络模型压缩 - problem_ide - CodeFun2000
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 import numpy as npfrom decimal import Decimal, ROUND_HALF_DOWNdef round_half_down (x ): x = Decimal(str (x)) return int (x.quantize(Decimal('1' ), rounding=ROUND_HALF_DOWN)) def pred (h ): h_norm = h - np.max (h, axis = 1 , keepdims=True ) y_pred = np.exp(h_norm) / np.sum (np.exp(h_norm), axis = 1 , keepdims=True ) label = np.argmax(y_pred, axis=1 ) return label def main (): n, d, c = map (int , input ().strip().split()) X = np.array( [list (map (float , input ().strip().split())) for _ in range (n)], dtype=np.float64 ) W = np.array( [list (map (float , input ().strip().split())) for _ in range (d)], dtype=np.float64 ) ratio = float (input ()) k = round_half_down(d*ratio) if ratio>0 and k == 0 : k = 1 W_l1 = np.sum (np.abs (W), axis = 1 ) delete_dim = np.argsort(W_l1)[:k] W_new = np.delete(W, delete_dim, axis = 0 ) X_new = np.delete(X, delete_dim, axis = 1 ) h = np.dot(X_new, W_new) label = pred(h) print (" " .join(map (str , label))) if __name__ == "__main__" : main()
熟练使用np去操作维度
线性代数 P4277.第2题-人脸关键点对齐 第2题-人脸关键点对齐 - problem_ide - CodeFun2000
仿射变换需要逆变换,正变会出现空洞
矩阵求逆
二阶矩阵 $$ A = \begin{bmatrix} a & b \\ c & d \end{bmatrix} $$ 如果$\det(A) \neq 0$
那么逆矩阵为
$$ A^{-1} {}={} \frac{1}{ad - bc} \begin{bmatrix} d & -b \\ -c & a \end{bmatrix} $$
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 import numpy as npdef print_output (O, row_O ): flat = O.reshape(-1 ) col_O = len (flat) // row_O for r in range (row_O): start = r * col_O end = start + col_O print (" " .join(map (str , flat[start:end]))) def main (): row_A, row_M, row_O = map (int , input ().split()) A = np.array( [list (map (int , input ().split())) for _ in range (row_A)], dtype=int ) M = np.array( [list (map (float , input ().split())) for _ in range (row_M)], dtype=float ) oh, ow = map (int , input ().split()) Ah, Aw = A.shape a, b, tx = M[0 ] c, d, ty = M[1 ] det = a * d - b * c O = np.zeros((oh, ow), dtype=int ) if abs (det) < 1e-12 : print_output(O, row_O) return for i in range (oh): for j in range (ow): xp = j yp = i dx = xp - tx dy = yp - ty x = (d * dx - b * dy) / det y = (-c * dx + a * dy) / det src_j = int (np.floor(x + 0.5 )) src_i = int (np.floor(y + 0.5 )) if 0 <= src_i < Ah and 0 <= src_j < Aw: O[i][j] = A[src_i][src_j] print_output(O, row_O) if __name__ == "__main__" : main()
P4998.第3题-回归任务反向传播机理 第3题-回归任务反向传播机理 - problem_ide - CodeFun2000
对着题目写就行了,主要是搞明白维度,内积外积
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 import numpy as npdef main (): d_in, d_hidden, d_out = map (int , input ().split()) W_1 = np.array( [list (map (float , input ().split())) for _ in range (d_hidden)], dtype=np.float64 ) b_1 = np.array(list (map (float , input ().split())), dtype=np.float64) W_2 = np.array( [list (map (float , input ().split())) for _ in range (d_out)], dtype=np.float64 ) b_2 = np.array(list (map (float , input ().split())), dtype=np.float64) x = np.array(list (map (float , input ().split())), dtype=np.float64) y = np.array(list (map (float , input ().split())), dtype=np.float64) eta = float (input ()) z_1 = x @ W_1.T + b_1 alpha_1 = np.maximum(z_1, 0 ) y_pred = alpha_1 @ W_2.T + b_2 delta_2 = y_pred - y g_W2 = np.outer(delta_2, alpha_1) g_b2 = delta_2 delta_1 = delta_2 @ W_2 delta_1[z_1 <=0 ] = 0 g_W1 = np.outer(delta_1, x) g_b1 = delta_1 W_1_new = W_1 - eta * g_W1 b_1_new = b_1 - eta * g_b1 W_2_new = W_2 - eta * g_W2 b_2_new = b_2 - eta * g_b2 for row in W_1_new: print (" " .join(f"{v:.6 f} " for v in row)) print (" " .join(f"{v:.6 f} " for v in b_1_new)) for row in W_2_new: print (" " .join(f"{v:.6 f} " for v in row)) print (" " .join(f"{v:.6 f} " for v in b_2_new)) print (" " .join(f"{v:.6 f} " for v in y_pred)) if __name__ == "__main__" : main()
VIT 假设输入图片是: $$ X \in \mathbb{R}^{B \times C \times H \times W} $$ ViT 会把图片切成大小为 $P\times P$ 的小块 $$ N = \frac{H}{P} \times \frac{W}{P} $$ 将第 b 张图片中的第 i 个 patch 展平成一个向量,记为: $$ \mathbf{p}_{b,i} \in \mathbb{R}^{P^2C} $$ 假设图片尺寸是 224×224,patch size 是 16×16,那么 patch 数量是 $$ N = \frac{224}{16} \times \frac{224}{16} = 14 \times 14 = 196 $$ 每个 patch 的原始形状是 $C \times P \times P$
如果是 RGB 图像,那么一个 patch 展平成向量后长度是 $$ 3 \times 16 \times 16 = 768 $$ 然后用一个线性层把它映射到 Transformer 的 embedding 维度 $D$ $$ \mathbf{z}{b,i} = \mathbf{p} {b,i} W_E + \mathbf{b}E $$ 其中 $$ \mathbf{p} {b,i} \in \mathbb{R}^{P^2C}, \quad W_E \in \mathbb{R}^{P^2C \times D}, \quad \mathbf{b}E \in \mathbb{R}^{D}, \quad \mathbf{z} {b,i} \in \mathbb{R}^{D} $$ 所以 Patch Embedding 的本质是:把每个图像小块展平成向量,然后通过一个线性层映射成 Transformer 可以处理的 token embedding
对整个 batch 来说,Patch Embedding 之后得到 $$ Z_p \in \mathbb{R}^{B \times N \times D} $$ 其中$N$是patch的数量
ViT 中还有一个可学习的 CLS token,记为 $$ \mathbf{z}{\text{cls}} \in \mathbb{R}^{1 \times 1 \times D} $$ 训练时它是一个可学习参数,对于 batch 输入,需要把它复制到 batch 维度上 $$ Z {\text{cls}} \in \mathbb{R}^{B \times 1 \times D} $$ 然后把 CLS token 拼接到所有 patch tokens 的最前面 $$ Z = [Z_{\text{cls}}; Z_p] \in \mathbb{R}^{B \times (N+1) \times D} $$ 接着给整个序列加上位置编码,位置编码也是可学习参数,记为: $$ E_{\text{pos}} \in \mathbb{R}^{1 \times (N+1) \times D} $$
INT8量化
类型
量化范围
0 是否映射到整数 0
优点
缺点
对称量化
围绕 0 对称
是
计算简单,矩阵乘法方便
对偏移数据可能浪费范围
非对称量化
由 min/max 决定
不一定
更充分利用 int8 范围
计算时要处理 offset / zero point
P4464.第2题-全连接层INT8非对称量化实现 第2题-全连接层INT8非对称量化实现 - problem_ide - CodeFun2000 30min
注意点:
反量化的时候用的scale 和 min(v)是量化时候的,需要把数据存出来
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 import numpy as npfrom decimal import Decimal, ROUND_HALF_UPdef quant (X ): max_v = np.max (X) min_v = np.min (X) if max_v == min_v: scale = 0 X_quant = np.full_like(X, -128 ) else : scale = (max_v - min_v)/255 X_quant = np.round ((X-min_v)/scale) - 128 X_quant[X_quant < -128 ] = -128 X_quant[X_quant > 127 ] = 127 return X_quant, scale, min_v def dequant (X_quant, scale, min_v ): if scale == 0 : X_dequant = np.full_like(X_quant, min_v) else : X_dequant = (X_quant + 128 )* scale + min_v return X_dequant def main (): n = int (input ()) x = np.array(list (map (float , input ().split())),dtype = np.float64) m, _ = map (int , input ().split()) W = np.array([list (map (float , input ().split())) for _ in range (m)],dtype = np.float64) Y = x @ W.T x_quant, scale_x, min_v_x= quant(x) W_quant, scale_W, min_v_W = quant(W) Y_quant = x_quant @ W_quant.T print (" " .join(map (str , Y_quant.astype(np.int64)))) x_dequant = dequant(x_quant, scale_x, min_v_x) W_dequant = dequant(W_quant, scale_W, min_v_W) Y_dequant = x_dequant @ W_dequant.T MSE = np.mean((Y- Y_dequant)**2 ) * 100000 MSE = round (MSE) print (MSE) if __name__ =="__main__" : main()