文章目录
-
- 题目
- 答案
- [datas = [list(map(float , input().split())) for _ in range(n)]](#datas = [list(map(float , input().split())) for _ in range(n)])
- [datas.sort(key = lambda x : dist(x[:-1] , test))](#datas.sort(key = lambda x : dist(x[:-1] , test)))
-
- 匿名函数的用法
- [List 的切片操作](#List 的切片操作)
- [列表的 .sort() 方法](#列表的 .sort() 方法)
- [if label not in mp:](#if label not in mp:)
- [res = list(mp.items())](#res = list(mp.items()))
题目
在终端穿戴场景中,往往涉及到用户和终端设备之间的语音交互,通常由用户唤醒相关的设备并进行语音命令控制设备完成相应的操作,设备在收到语音命令时会对用户的意图进行识别,例如识别"拍照""摄像"等意图,从而进行对应的操作。要求使用 KNN 算法对用户输出的语音特征进行分类:语音片段经过特征提取后是一个 3 3 3 维的向量所有距离使用欧式距离公式计算,即对于两个 N N N 维向量 x ⃗ = ( x 1 , x 2 , ... , x N ) , y ⃗ = ( y 1 , y 2 , ... , y N ) \vec{x} = (x_1, x_2, \dots, x_N), \quad \vec{y} = (y_1, y_2, \dots, y_N) x =(x1,x2,...,xN),y =(y1,y2,...,yN)两个向量间的距离为: d ( x ⃗ , y ⃗ ) = ∑ k = 1 N ( x k − y k ) 2 d_{(\vec{x}, \vec{y})} = \sqrt{\sum_{k=1}^{N} (x_k - y_k)^2} d(x ,y )=∑k=1N(xk−yk)2 。
- 输入描述第一行:正整数 N N N 和 K K K,分别表示给定的已知语音特征向量的数量和邻居数量
- K K K第 2 ~ 13 行:已知语音的特征向量和 l a b e l label label,其中最后一位表示类别 l a b e l label label
- 第 14 行:待分类的语音特征向量
输入
python
10 3
0.5 0.3 0.4 0
0.6 0.2 0.5 0
0.4 0.3 0.3 0
0.7 0.4 0.6 0
2.1 2.3 2.2 1
2.3 2.2 2.4 1
2.2 2.4 2.3 1
4.5 4.3 4.4 2
4.4 4.5 4.6 2
4.6 4.4 4.5 2
2.2 2.1 2.3
输出
python
1
答案
python
#1.读入数据
n , k = map(int , input().split())
datas = [list(map(float , input().split())) for _ in range(n)]
test = list(map(float , input().split()))
#2.封装距离函数
def dist (x , y):
n = len(x)
ans = 0
# 对每个维度求平方差的和
for i in range(n):
ans += (x[i] - y[i]) ** 2
# 注意,为了获得更高的精度和效率,我们可以直接返回平方差的和,而不进行开方操作。
return ans
# 3.排序并选取前k个最近邻
datas.sort(key = lambda x : dist(x[:-1] , test))
# 4.统计前k个最近邻的标签出现频率,并选取出现频率最高的标签作为预测结果
# 注意:这里有可能k大于n,所以我们需要取min(n , k)来保证不会越界
real_k = min(n , k)
target_datas = datas[:real_k]
# 5.哈希统计: 统计每个标签出现的频率 (类别,频率)
mp = {}
for data in target_datas:
label = data[-1]
if label not in mp:
mp[label] = 1
else:
mp[label] += 1
# 6.选取出现频率最高的标签作为预测结果
res = list(mp.items())
res.sort(key = lambda x : -x[1])
print(int(res[0][0]))
datas = [list(map(float , input().split())) for _ in range(n)]
- input():
从控制台读取一行输入。
结果:得到一个字符串 "1.5 2.5 3.5" - .split():
将这个字符串按空格分割成多个子串。
结果:得到一个字符串列表 ['1.5', '2.5', '3.5'] - map(float, ...):
map 函数会将前面得到的列表中的每一个字符串,都执行一次 float()(转换为浮点数)操作。
结果:得到一个包含 1.5、2.5、3.5 的 map 迭代器对象。 - list(...):
因为 map 返回的是一个迭代器,我们需要用 list() 把它真正转换成 Python 的列表。
结果:得到一个纯数字列表 [1.5, 2.5, 3.5]
datas.sort(key = lambda x : dist(x[:-1] , test))
匿名函数的用法
python
lambda 参数列表 : 表达式
假设我们要写一个求两个数字平方和的函数:
使用传统的 def 定义:
python
def square_sum(x, y):
result = x**2 + y**2
return result
print(square_sum(3, 4)) # 输出 25
使用 lambda 一行搞定:
python
square_sum_lambda = lambda x, y : x**2 + y**2
print(square_sum_lambda(3, 4)) # 输出 25
List 的切片操作
提取从开头到倒数第一个元素之前的所有元素
python
x[:-1]
Python 的切片(Slicing)操作不仅优雅,而且极其强大。它的完整语法其实包含三个参数:
sequence[start : stop : step]
-
start:起始索引(包含该位置)。默认是 0。
-
stop:结束索引(不包含该位置)。默认是序列的结尾。
-
step:步长(每次跳过几个元素)。默认是 1。
如果数据是 data = [10, 20, 30, 40, 50, 60, 70, 80, 90]
负数索引(从后往前数)
- 去掉首尾的边界数据(比如去除可能存在的噪声边缘):
python
print(data[1:-1])
# 输出: [20, 30, 40, 50, 60, 70, 80] (去掉了首部的 10 和尾部的 90)
- 获取最后 3 个数据点:
python
print(data[-3:])
# 输出: [70, 80, 90]
列表的 .sort() 方法
- 使用内置函数作为 key
python
words = ["apple", "banana", "kiwi", "pear"]
# 默认排序(按首字母)
words.sort()
print(words) # 输出: ['apple', 'banana', 'kiwi', 'pear']
# 使用 key=len(按长度排序)
words.sort(key=len)
print(words) # 输出: ['kiwi', 'pear', 'apple', 'banana']
- 结合 lambda 处理复合数据
python
students = [("Alice", 88), ("Bob", 95), ("Charlie", 72)]
# x 代表列表中的每一个元组,x[1] 提取出了成绩作为排序依据
students.sort(key=lambda x: x[1])
print(students)
# 输出: [('Charlie', 72), ('Alice', 88), ('Bob', 95)]
- 多重条件排序
python
students = [("Alice", 88), ("Bob", 95), ("Charlie", 88)]
# 成绩取负数 (-x[1]) 实现数值的降序,名字 x[0] 默认升序
students.sort(key=lambda x: (-x[1], x[0]))
print(students)
# 输出: [('Bob', 95), ('Alice', 88), ('Charlie', 88)]
注意比较多个条件的时候,需要将多个值打包为一个单独的数据结构,常见的是 元组 Tuple。
简单来说,我们不能写成 key = lambda x: a, b(这样 Python 会因为参数解析混乱而报语法错误),你必须加上括号,写成 key = lambda x: (a, b)。
if label not in mp:
多条件分支
python
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
结合逻辑运算符 (and, or, not, in, not in)
python
is_vip = False
if not is_vip:
print("您还不是会员,请先充值")
隐式布尔值判断
Python 规定,以下这些值在 if 判断中会自动被当成 False(称为假值):
-
数字零:0, 0.0
-
空序列:空字符串 ""、空列表 []、空元组 ()
-
空字典:{}
-
空值:None
除了上述假值,其他所有的值都会被当成 True。
推导式中的过滤守卫 (Filter in Comprehensions)
基于某个条件从一个列表中筛选出符合要求的数据
python
data = [12, -5, 30, -8, 55, 0]
# 需求:提取出所有大于 0 的数字,并把它们乘以 2
# 语法:[计算逻辑 for 变量 in 集合 if 过滤条件]
positive_doubled = [x * 2 for x in data if x > 0]
print(positive_doubled)
# 输出: [24, 60, 110]
res = list(mp.items())
-
mp.items():这是字典(Dictionary)自带的一个非常重要的方法。它会把字典里所有的键值对,打包成一个个元组(Tuple)。
-
假设前面统计的 mp 是 {'类别0': 2, '类别1': 5}。
-
mp.items() 就会把它变成类似 [('类别0', 2), ('类别1', 5)] 的结构。
-
-
list(...):强制类型转换。因为 mp.items() 返回的是一个动态的"视图(View)"对象,不能直接用来排序和切片。所以用 list() 把它固化成一个真正的 Python 列表,赋值给变量 res。
在 PyCharm 中(或者说在任何标准的 Python 3 环境下)运行 mp.items(),它返回的并不是一个列表(List),而是一个特殊的内置对象类型:dict_items(字典视图对象)。
python
mp = {'正常流量': 100, 'DDoS攻击': 5}
items_obj = mp.items()
print(type(items_obj)) # 输出: <class 'dict_items'>
print(items_obj) # 输出: dict_items([('正常流量', 100), ('DDoS攻击', 5)])
当我们执行 res = list(mp.items()) 时,Python 生成的是这个结构:
python
res = [('正常流量', 100), ('DDoS攻击', 5)]
-
外层是一个中括号 [],代表这是一个列表。
-
内层是一个个小括号 (),代表里面装的元素是元组(Tuple)。