import math import random from typing import List, Dict, Optional #首页数据 class indexData: def __init__(self): # 通栏权重设置 self.sectors_config = [ {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条" , "random": False}, {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图" , "random": False}, {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板" , "random": False}, {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1" , "random": True}, {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2" , "random": True}, {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3" , "random": True}, {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合" , "random": True}, {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告" , "random": False} ] # 通栏数据 self.sectors_data = { #广告通栏 "adSector": { "sectorName": "adSector", "sectorCanvasHeight": 12, #占据画布高度 "componentList": [ { "component_type": 2, "component_style": 1, "sort": 1, "componentData": {} } ], "ad": { "width": 1200, "height": 90, "name": "", "price": 0, "introduce":"", "website_id": "", "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250530/1748588901281358.png", "typeid": 2, "ad_tag": "" } }, #头条通栏 "headLineSector": { "sectorName": "headLineSector", "sectorCanvasHeight": 17, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "level": 1, "imgSize": 0, "textSize": 4, "child": { "id": "", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } } ] }, #轮播图通栏 "bannerSector": { "sectorName": "bannerSector", "sectorCanvasHeight": 44, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "level": 2, "imgSize": 5, "textSize": 0, "child": { "id": "", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, { "component_type": 1, "component_style": 1, "sort": 2, "componentData": { "level": 6, "imgSize": 0, "textSize": 10, "child": { "id": "", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } } ] }, #新闻组合通栏1 "manyPictureSector": { "sectorName": "manyPictureSector", "sectorCanvasHeight": 47, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 3, "textSize": 9, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, { "component_type": 1, "component_style": 1, "sort": 2, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 3, "textSize": 6, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } } ] }, #新闻组合通栏2 "commentSector": { "sectorName": "commentSector", "sectorCanvasHeight": 47, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 2, "textSize": 12, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, { "component_type": 1, "component_style": 1, "sort": 2, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 1, "textSize": 3, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } } ] }, #新闻组合通栏3 "listSector": { "sectorName": "listSector", "sectorCanvasHeight": 98, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 2, "textSize": 6, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, { "component_type": 1, "component_style": 1, "sort": 2, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 2, "textSize": 14, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, { "component_type": 1, "component_style": 1, "sort": 3, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 7, "textSize": 0, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } } ] }, #广告新闻混合通栏 "onlyImgSector": { "sectorName": "onlyImgSector", "sectorCanvasHeight": 51, "componentList": [ { "component_type": 1, "component_style": 1, "sort": 1, "componentData": { "category_id":"", "category_arr":"", "name":"请选择导航..", "level":"", "imgSize": 3, "textSize": 4, "child":{ "id":"", "imgSize": "", "textSize": "" }, "listType": [ "id", "title", "imgurl", "author", "updated_at", "introduce", "islink", "linkurl", "copyfrom", "cat_arr_id", "catid", "pinyin" ], } }, ], "ad": { "width": 450, "height": 290, "name": "", "price": 0, "introduce":"", "website_id": "", "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250609/174945725555092.png", "typeid": 2, "ad_tag": "" } }, #外链通栏 "linkSector": { "sectorName": "linkSector", "sectorCanvasHeight": 26, "componentList": [ { "component_type": 3, "component_style": 1, "sort": 1, "componentData": {} } ] }, } #模板数据 self.sectors_webSiteData = { "base": { "websiteId": "" }, "style": { "styleId": "" }, "template": { "index": [], "class": [], "list": [], "article": [], "search": [], "aloneList": [], "aloneArticle": [] }, "ad": { "top":{ "width": 830, "height": 110, "name": "", "price": 0, "introduce":"", "website_id": "", "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250527/1748332370111555.png", "typeid": 2, "ad_tag": "" }, "index": [], "class": [], "list": [], "article": [], "search": [], "aloneList": [], "aloneArticle": [] }, "isSearch": True } #画布数据 self.canvas_data = { "topAd":{ "width": 830, "height": 110, "name": "", "price": "", "introduce": "", "website_id": "", "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250928/1759038686452927.png", "typeid": 2, "ad_tag": "", "ad_url": "" }, "template":{ "index":[], "class": [], "list": [], "article": [], "search": [], "aloneList": [], "aloneArticle": [] } } #初始化通栏权重 def reset_sectors_config(self): self.sectors_config = [ {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条"}, {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图"}, {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板"}, {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1"}, {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2"}, {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3"}, {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合"}, {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告"} ] #创建一个templateData实例 templateData = indexData() # 动态权重计算类 class Sector: def __init__(self, name: str, total: int, weight: int, CNname: str): self.name = name # 名称 self.total = total # 总数 self.weight = weight # 权重 self.remaining = total # 剩余数量 后面我们要对比 剩余数据/总数 来计算权重 self.used_count = 0 # 记录已使用次数 self.CNname = CNname # 中文名称 """ 思路: 1.按照权限的高低选择通栏 2.如果权限一样,随机选择 3.每选择两次新闻通栏后,adSector权重提升 """ class SectorScheduler: def __init__(self, sectors_config: List[Dict]): # 过滤掉total为0的sector(即不存在的sector) self.sectors = [ Sector(sector['name'], sector['total'], sector['weight'], sector['CNname']) for sector in sectors_config if sector['total'] > 0 ] self.history = [] # 记录选择历史 self.consecutive_count = 0 # 连续选择相同权重sector的次数 self.last_weight = None # 上一次选择的权重 # 定义需要随机选择的sector组 self.random_sector_group = {sector['name'] for sector in sectors_config if sector.get('random', False)} def calculate_dynamic_weight(self, sector: Sector) -> float: """计算动态权重 - 更复杂的权重计算""" if sector.remaining <= 0: return 0 base_weight = sector.weight # 1. 基于剩余数量的权重调整 usage_ratio = sector.remaining / sector.total quantity_factor = math.pow(usage_ratio, 0.4) # 剩余数量越多,权重相对越高 # 2. 基于使用频率的权重调整(避免过度使用) if sector.used_count > 0: usage_frequency = 1.0 / (1 + math.log(1 + sector.used_count)) else: usage_frequency = 1.0 # 3. 对于adSector的特殊处理 if sector.name == "adSector": # 检查是否需要插入adSector(每两个相同权重后) if self.consecutive_count >= 2 and self.last_weight == 7: ad_boost = 2.0 # 大幅提升adSector权重 else: # 根据历史中adSector的出现频率调整 recent_ad_count = self.history[-3:].count("adSector") if len(self.history) >= 3 else 0 if recent_ad_count == 0: ad_boost = 1.5 # 一段时间没出现,适当提升 else: ad_boost = 0.8 # 最近出现较多,适当降低 else: ad_boost = 1.0 # 4. 对于高权重sector的保护(确保它们优先被选择) if base_weight >= 8: priority_boost = 1.5 else: priority_boost = 1.0 dynamic_weight = base_weight * quantity_factor * usage_frequency * ad_boost * priority_boost return dynamic_weight def get_random_sector_from_group(self, target_weight: int) -> Optional[Sector]: """从随机组中随机选择一个指定权重的sector""" available_sectors = [ sector for sector in self.sectors if sector.name in self.random_sector_group and sector.weight == target_weight and sector.remaining > 0 ] if available_sectors: return random.choice(available_sectors) return None def select_next(self) -> Optional[str]: """选择下一个sector""" best_sector = None best_weight = -1 current_weight = 7 # 随机选择组的权重 # 首先检查是否需要强制插入adSector if (self.consecutive_count >= 2 and self.last_weight == current_weight and any(s.name == "adSector" and s.remaining > 0 for s in self.sectors)): ad_sector = next(s for s in self.sectors if s.name == "adSector" and s.remaining > 0) ad_sector.remaining -= 1 ad_sector.used_count += 1 # 更新历史记录 self.history.append("adSector") self.consecutive_count = 0 self.last_weight = ad_sector.weight return "adSector" # 正常选择逻辑 for sector in self.sectors: if sector.remaining > 0: # 对于随机选择组的sector,使用统一的权重计算 if sector.name in self.random_sector_group and sector.weight == current_weight: # 使用组内统一的权重值进行比较 dynamic_weight = current_weight else: dynamic_weight = self.calculate_dynamic_weight(sector) if dynamic_weight > best_weight: best_weight = dynamic_weight best_sector = sector if best_sector: # 如果最佳sector属于随机选择组,则随机选择该组中的一个 if best_sector.name in self.random_sector_group and best_sector.weight == current_weight: random_sector = self.get_random_sector_from_group(current_weight) if random_sector: best_sector = random_sector best_sector.remaining -= 1 best_sector.used_count += 1 # 更新连续选择计数 if self.last_weight == best_sector.weight and best_sector.name != "adSector": self.consecutive_count += 1 else: self.consecutive_count = 1 self.last_weight = best_sector.weight self.history.append(best_sector.name) return best_sector.name return None def generate_sequence(self, count: int) -> List[str]: """生成指定长度的序列""" result = [] for i in range(count): next_sector = self.select_next() if next_sector: result.append(next_sector) else: break # 没有可选的sector了 return result