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 里的权重:相关性、质量、新鲜度、点击率各占多少。

文章仅供参考用。

相关推荐
benchmark_cc1 小时前
A股量化尾盘筛选对数据时效敏感:行情链路变慢时先排查哪里?
python·数据分析·pandas·量化交易·股票数据·quantdash
vx_Biye_Design1 小时前
expressDeepSeek社团咨询助手的学生社团管理系统60746-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·课程设计·express
泡茶喝茶写代码1 小时前
A股量化数据工程:从 REST 接口到策略信号(第 5 篇):板块成分股映射与对齐
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据接口
vx_Biye_Design1 小时前
django就业信息推荐系统61953-计算机课程设计、毕业设计
java·vue.js·spring boot·python·架构·django·课程设计
零基础1231 小时前
VoiceStudio 开源项目深度解析:特性、对比与实战测试
人工智能·经验分享·python·开源
2601_962885721 小时前
如何用 Python 做均线粘合选股?(多头发散前的变盘候选)
开发语言·python
zhangzeyuaaa2 小时前
Ruby 方法参数完全指南:默认值、可变参数与关键字参数
前端·python·ruby
小小张说故事2 小时前
pytest 写了用例却不跑、fixture 不生效、参数化全失效?9 个高频坑对照表
python
外收内放2 小时前
08 | 优化篇③ 60 张动作立绘和 50 张特效贴图是怎么进游戏的
python·游戏·pygame