Python贪心算法实现搜索推荐

下面是一段用贪心算法做搜索推荐的 Python 示例。

场景:用户输入搜索问题,系统从候选内容中推荐最相关的若干条。贪心策略是:每一步都从剩余候选里选"当前得分最高且满足约束"的内容,同时考虑相关度、质量、新鲜度,以及推荐结果之间的多样性。

```python

import re

from dataclasses import dataclass

from typing import List, Set

@dataclass

class Candidate:

id: int

title: str

content: str

tags: Liststr

quality: float = 0.8 # 内容质量分

freshness: float = 0.8 # 新鲜度

click_rate: float = 0.5 # 历史点击率

length: int = 100 # 内容长度,可用于控制总推荐长度

def tokenize(text: str) -> Setstr:

"""

简单分词:

  • 英文/数字按单词切分

  • 中文按连续片段 + 2-gram 切分

不依赖 jieba,方便直接运行。

"""

text = text.lower()

tokens = set(re.findall(r"a-z0-9+", text))

chinese_seqs = re.findall(r"\\u4e00-\\u9fa5+", text)

for seq in chinese_seqs:

if len(seq) == 1:

tokens.add(seq)

else:

tokens.add(seq)

for i in range(len(seq) - 1):

tokens.add(seqi:i + 2)

return tokens

def jaccard(a: Setstr, b: Setstr) -> float:

"""计算两个集合的 Jaccard 相似度"""

if not a or not b:

return 0.0

return len(a & b) / len(a | b)

def base_score(query_tokens: Setstr, cand: Candidate) -> float:

"""

计算候选内容的基础得分:

相关性 + 质量 + 新鲜度 + 点击率

"""

cand_text = cand.title + " " + cand.content + " " + " ".join(cand.tags)

cand_tokens = tokenize(cand_text)

relevance = jaccard(query_tokens, cand_tokens)

标签命中额外加权

tag_tokens = tokenize(" ".join(cand.tags))

tag_relevance = jaccard(query_tokens, tag_tokens)

relevance = max(relevance, tag_relevance)

score = (

0.55 * relevance +

0.20 * cand.quality +

0.15 * cand.freshness +

0.10 * cand.click_rate

)

return score

def greedy_recommend(

query: str,

candidates: ListCandidate,

top_k: int = 5,

max_similarity: float = 0.7, # 与已选内容标签相似度超过该值则跳过

diversity_penalty: float = 0.3, # 相似度带来的得分惩罚

max_total_length: int = 1000 # 推荐内容总长度限制

) -> ListCandidate:

"""

贪心推荐:

每一步从剩余候选中选择当前得分最高、且满足多样性/长度约束的内容。

"""

query_tokens = tokenize(query)

selected: ListCandidate = \[\]

selected_tag_sets: ListSet\[str] = \[\]

total_length = 0

预计算基础得分

remaining = (base_score(query_tokens, c), c) for c in candidates

while len(selected) < top_k and remaining:

best_idx = -1

best_score = -1.0

best_cand = None

for i, (base_s, cand) in enumerate(remaining):

cand_tags = tokenize(" ".join(cand.tags))

计算与已选内容的最大标签相似度

max_overlap = 0.0

for tag_set in selected_tag_sets:

max_overlap = max(max_overlap, jaccard(tag_set, cand_tags))

多样性约束:太相似就不选

if selected and max_overlap > max_similarity:

continue

长度约束

if total_length + cand.length > max_total_length:

continue

当前得分 = 基础得分 - 多样性惩罚

current_score = base_s - diversity_penalty * max_overlap

if current_score > best_score:

best_score = current_score

best_idx = i

best_cand = cand

没有满足约束的候选,结束

if best_idx == -1:

break

选中当前最优

selected.append(best_cand)

selected_tag_sets.append(tokenize(" ".join(best_cand.tags)))

total_length += best_cand.length

remaining.pop(best_idx)

return selected

if name == "main":

candidates = [

Candidate(1, "贪心算法入门", "贪心算法是一种局部最优策略...", "算法", "贪心算法", "基础", 0.90, 0.70, 0.60, 120),

Candidate(2, "活动选择问题详解", "用贪心算法解决活动选择...", "贪心算法", "活动选择", "区间调度", 0.85, 0.80, 0.70, 150),

Candidate(3, "动态规划与贪心算法区别", "对比动态规划和贪心算法...", "算法", "动态规划", "贪心算法", 0.88, 0.60, 0.65, 180),

Candidate(4, "霍夫曼编码实现", "霍夫曼编码的贪心构造...", "贪心算法", "霍夫曼编码", "压缩", 0.80, 0.75, 0.55, 200),

Candidate(5, "Dijkstra 最短路", "Dijkstra 算法是贪心思想...", "贪心算法", "图论", "最短路", 0.90, 0.65, 0.75, 160),

Candidate(6, "0/1 背包问题", "0/1 背包不能用贪心...", "动态规划", "背包", 0.95, 0.90, 0.80, 140),

Candidate(7, "分数背包贪心解法", "分数背包可以按单位价值贪心...", "贪心算法", "背包", "分数背包", 0.82, 0.70, 0.60, 130),

Candidate(8, "贪心算法常见错误", "贪心算法不是万能的...", "贪心算法", "算法", "反例", 0.87, 0.80, 0.72, 110),

]

query = "贪心算法 活动选择"

recs = greedy_recommend(query, candidates, top_k=4, max_total_length=600)

print(f"查询:{query}\n推荐结果:")

for i, c in enumerate(recs, 1):

print(f"{i}. {c.title} | 标签:{', '.join(c.tags)} | 质量:{c.quality} | 长度:{c.length}")

```

这段代码的核心贪心逻辑在 greedy_recommend 里:

  1. 先计算每个候选内容的基础得分;

  2. 每一步遍历剩余候选;

  3. 计算它和已选内容的标签相似度,并做多样性惩罚;

  4. 选择当前得分最高、且满足相似度阈值和长度限制的内容;

  5. 加入推荐列表,重复直到选够 top_k 或没有可用候选。

你可以根据实际业务调整这些参数:

· top_k:推荐几条;

· max_similarity:控制内容多样性;

· diversity_penalty:相似内容的降权幅度;

· max_total_length:控制推荐总长度;

· base_score 里的权重:相关性、质量、新鲜度、点击率各占多少。

文章仅供参考用。

相关推荐
特立独行的猫A30 分钟前
用仓颉语言 cj-tauri 框架,五分钟给娃做了个鸿蒙形近字游戏
java·后端·python
大可不必了5 小时前
03 函数定义和调用
开发语言·python
承渊政道6 小时前
【Python编程—从入门到实践】(记分与闯关:用Python+Pygame,打造越战越精彩的太空大战!)
开发语言·python·pycharm·游戏程序·pygame
爱学习的程序媛6 小时前
Python常用框架梳理
开发语言·后端·python·ai·web·后端框架·python框架
imgsq6 小时前
GESP Python 一到四级:备考路线与真题特点
python·青少年编程
eggwyw8 小时前
Anaconda安装卸载的详细流程指南
开发语言·python
2601_967979128 小时前
纯手写:Python安装pip完整教程,搞了几小时,终于成功
python·教程·pip·安装·包管理
Norna8 小时前
缓存文件从没写出来:一个 .json 被当成目录用了
python
aifannihao11 小时前
AI 学习第 14 课:CNN 训 6 轮卡在 0.5352,数据没打乱
pytorch·python·深度学习·cnn·学习路线
yuyuxun111 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计