templateFun.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import math
  2. import random
  3. from typing import List, Dict, Optional
  4. #首页数据
  5. class indexData:
  6. def __init__(self):
  7. #提交的模板数据
  8. self.sectors_webSiteData = {
  9. "base": {
  10. "websiteId": ""
  11. },
  12. "style": {
  13. "styleId": ""
  14. },
  15. "template": {
  16. "index": [],
  17. "class": [],
  18. "list": [],
  19. "article": [],
  20. "search": [],
  21. "aloneList": [],
  22. "aloneArticle": []
  23. },
  24. "ad": {
  25. "top":{
  26. "width": 830,
  27. "height": 110,
  28. "name": "",
  29. "price": 0,
  30. "introduce":"",
  31. "website_id": "",
  32. "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250527/1748332370111555.png",
  33. "typeid": 2,
  34. "ad_tag": ""
  35. },
  36. "index": [],
  37. "class": [],
  38. "list": [],
  39. "article": [],
  40. "search": [],
  41. "aloneList": [],
  42. "aloneArticle": []
  43. },
  44. "isSearch": True
  45. }
  46. #提交的画布数据
  47. self.canvas_data = {
  48. "topAd":{
  49. "width": 830,
  50. "height": 110,
  51. "name": "",
  52. "price": "",
  53. "introduce": "",
  54. "website_id": "",
  55. "thumb": "http://192.168.1.234:19000/pre/image/jpeg/20251226/1766715237727691.jpg",
  56. "typeid": 2,
  57. "ad_tag": "",
  58. "ad_url": ""
  59. },
  60. "template":{
  61. "index":[],
  62. "class": [],
  63. "list": [],
  64. "article": [],
  65. "search": [],
  66. "aloneList": [],
  67. "aloneArticle": []
  68. }
  69. }
  70. #创建一个templateData实例
  71. templateData = indexData()
  72. # 动态权重计算类
  73. class Sector:
  74. def __init__(self, name: str, total: int, weight: int, CNname: str):
  75. self.name = name # 名称
  76. self.total = total # 总数
  77. self.weight = weight # 权重
  78. self.remaining = total # 剩余数量 后面我们要对比 剩余数据/总数 来计算权重
  79. self.used_count = 0 # 记录已使用次数
  80. self.CNname = CNname # 中文名称
  81. """
  82. 思路:
  83. 1.按照权限的高低选择通栏
  84. 2.如果权限一样,随机选择
  85. 3.每选择两次新闻通栏后,adSector权重提升
  86. """
  87. class SectorScheduler:
  88. def __init__(self, sectors_config: List[Dict]):
  89. # 过滤掉total为0的sector(即不存在的sector)
  90. self.sectors = [
  91. Sector(sector['name'], sector['total'], sector['weight'], sector['CNname'])
  92. for sector in sectors_config
  93. if sector['total'] > 0
  94. ]
  95. self.history = [] # 记录选择历史
  96. self.consecutive_count = 0 # 连续选择相同权重sector的次数
  97. self.last_weight = None # 上一次选择的权重
  98. # 定义需要随机选择的sector组
  99. self.random_sector_group = {sector['name'] for sector in sectors_config if sector.get('random', False)}
  100. def calculate_dynamic_weight(self, sector: Sector) -> float:
  101. """计算动态权重 - 更复杂的权重计算"""
  102. if sector.remaining <= 0:
  103. return 0
  104. base_weight = sector.weight
  105. # 1. 基于剩余数量的权重调整
  106. usage_ratio = sector.remaining / sector.total
  107. quantity_factor = math.pow(usage_ratio, 0.4) # 剩余数量越多,权重相对越高
  108. # 2. 基于使用频率的权重调整(避免过度使用)
  109. if sector.used_count > 0:
  110. usage_frequency = 1.0 / (1 + math.log(1 + sector.used_count))
  111. else:
  112. usage_frequency = 1.0
  113. # 3. 对于adSector的特殊处理
  114. if sector.name == "adSector":
  115. # 检查是否需要插入adSector(每两个相同权重后)
  116. if self.consecutive_count >= 2 and self.last_weight == 7:
  117. ad_boost = 2.0 # 大幅提升adSector权重
  118. else:
  119. # 根据历史中adSector的出现频率调整
  120. recent_ad_count = self.history[-3:].count("adSector") if len(self.history) >= 3 else 0
  121. if recent_ad_count == 0:
  122. ad_boost = 1.5 # 一段时间没出现,适当提升
  123. else:
  124. ad_boost = 0.8 # 最近出现较多,适当降低
  125. else:
  126. ad_boost = 1.0
  127. # 4. 对于高权重sector的保护(确保它们优先被选择)
  128. if base_weight >= 8:
  129. priority_boost = 1.5
  130. else:
  131. priority_boost = 1.0
  132. dynamic_weight = base_weight * quantity_factor * usage_frequency * ad_boost * priority_boost
  133. return dynamic_weight
  134. def get_random_sector_from_group(self, target_weight: int) -> Optional[Sector]:
  135. """从随机组中随机选择一个指定权重的sector"""
  136. available_sectors = [
  137. sector for sector in self.sectors
  138. if sector.name in self.random_sector_group
  139. and sector.weight == target_weight
  140. and sector.remaining > 0
  141. ]
  142. if available_sectors:
  143. return random.choice(available_sectors)
  144. return None
  145. def select_next(self) -> Optional[str]:
  146. """选择下一个sector"""
  147. best_sector = None
  148. best_weight = -1
  149. current_weight = 7 # 随机选择组的权重
  150. # 首先检查是否需要强制插入adSector
  151. if (self.consecutive_count >= 2 and self.last_weight == current_weight and
  152. any(s.name == "adSector" and s.remaining > 0 for s in self.sectors)):
  153. ad_sector = next(s for s in self.sectors if s.name == "adSector" and s.remaining > 0)
  154. ad_sector.remaining -= 1
  155. ad_sector.used_count += 1
  156. # 更新历史记录
  157. self.history.append("adSector")
  158. self.consecutive_count = 0
  159. self.last_weight = ad_sector.weight
  160. return "adSector"
  161. # 正常选择逻辑
  162. for sector in self.sectors:
  163. if sector.remaining > 0:
  164. # 对于随机选择组的sector,使用统一的权重计算
  165. if sector.name in self.random_sector_group and sector.weight == current_weight:
  166. # 使用组内统一的权重值进行比较
  167. dynamic_weight = current_weight
  168. else:
  169. dynamic_weight = self.calculate_dynamic_weight(sector)
  170. if dynamic_weight > best_weight:
  171. best_weight = dynamic_weight
  172. best_sector = sector
  173. if best_sector:
  174. # 如果最佳sector属于随机选择组,则随机选择该组中的一个
  175. if best_sector.name in self.random_sector_group and best_sector.weight == current_weight:
  176. random_sector = self.get_random_sector_from_group(current_weight)
  177. if random_sector:
  178. best_sector = random_sector
  179. best_sector.remaining -= 1
  180. best_sector.used_count += 1
  181. # 更新连续选择计数
  182. if self.last_weight == best_sector.weight and best_sector.name != "adSector":
  183. self.consecutive_count += 1
  184. else:
  185. self.consecutive_count = 1
  186. self.last_weight = best_sector.weight
  187. self.history.append(best_sector.name)
  188. return best_sector.name
  189. return None
  190. def generate_sequence(self, count: int) -> List[str]:
  191. """生成指定长度的序列"""
  192. result = []
  193. for i in range(count):
  194. next_sector = self.select_next()
  195. if next_sector:
  196. result.append(next_sector)
  197. else:
  198. break # 没有可选的sector了
  199. return result