/** * Selector — 精英保留 + 锦标赛选择 * * 选择策略: * 1. 精英保留: 每代 top N 直接晋级 (不被淘汰) * 2. 锦标赛选择: 随机抽 k 个,选最优者存活 * 3. 弱者淘汰: 不满足存活条件的标记 is_alive=false */ const evolutionQueries = require('../queries').evolutionQueries; // ════════════════════════════════════════════════════════ // 选择策略 // ════════════════════════════════════════════════════════ /** * 精英保留 — 按适应度保留 top N * * @param {object[]} genomes - 基因组数组 * @param {number} count - 保留数量 * @returns {string[]} 精英 genome IDs */ function selectElites(genomes, count = 2) { const sorted = [...genomes] .filter(g => g.overall_fitness !== null && g.overall_fitness !== undefined) .sort((a, b) => (b.overall_fitness || 0) - (a.overall_fitness || 0)); return sorted.slice(0, count).map(g => g.id); } /** * 锦标赛选择 — 每轮随机抽 k 个,取最优 * * @param {object[]} genomes - 候选基因组 * @param {number} survivors - 存活者数量 * @param {number} tournamentSize - 每轮参赛者数量 * @returns {string[]} 存活者 genome IDs */ function tournamentSelect(genomes, survivors, tournamentSize = 3) { const selected = new Set(); const pool = [...genomes]; while (selected.size < survivors && pool.length > 0) { // 随机抽 k 个 const k = Math.min(tournamentSize, pool.length); const candidates = []; const indices = new Set(); while (candidates.length < k) { const idx = Math.floor(Math.random() * pool.length); if (!indices.has(idx)) { indices.add(idx); candidates.push(pool[idx]); } } // 选最优 candidates.sort((a, b) => (b.overall_fitness || 0) - (a.overall_fitness || 0)); const winner = candidates[0]; selected.add(winner.id); // 从池中移除赢家(避免重复选中) const winnerIdx = pool.findIndex(g => g.id === winner.id); if (winnerIdx >= 0) pool.splice(winnerIdx, 1); } return [...selected]; } /** * 多样性保留 — 确保不同 Skill 至少各有一个存活 * * @param {object[]} genomes - 候选基因组 * @returns {string[]} 每个 Skill 的最佳 genome ID */ function diversityPreserve(genomes) { const bySkill = {}; for (const g of genomes) { if (!bySkill[g.skill_name]) bySkill[g.skill_name] = []; bySkill[g.skill_name].push(g); } const preserved = []; for (const pool of Object.values(bySkill)) { pool.sort((a, b) => (b.overall_fitness || 0) - (a.overall_fitness || 0)); preserved.push(pool[0].id); } return preserved; } // ════════════════════════════════════════════════════════ // 选择执行器 // ════════════════════════════════════════════════════════ /** * 对一个 run 的当代群体执行选择 * * @param {string} runId * @param {object} options * @param {number} options.eliteCount - 精英保留数量 (默认 2) * @param {number} options.survivalRate - 存活率 0-1 (默认 0.5) * @param {number} options.tournamentSize - 锦标赛大小 (默认 3) */ async function selectGeneration(runId, options = {}) { const { eliteCount = 2, survivalRate = 0.5, tournamentSize = 3, } = options; const run = await evolutionQueries.getRun(runId); if (!run) throw new Error(`Run ${runId} not found`); const genomes = await evolutionQueries.getGenomesByRun(runId, { generation: run.current_generation, aliveOnly: true, }); if (genomes.length <= eliteCount) { console.log(` ⏭️ 种群 ${genomes.length} ≤ 精英数 ${eliteCount},跳过选择`); return { survivors: genomes.length, killed: 0, elites: genomes.length }; } const targetSurvivors = Math.max( eliteCount, Math.ceil(genomes.length * survivalRate) ); // 1. 精英保留 const eliteIds = selectElites(genomes, eliteCount); for (const id of eliteIds) { await evolutionQueries.markElite(id); } // 2. 多样性保留 const diversityIds = diversityPreserve(genomes); // 3. 锦标赛选择(排除已选中的) const alreadySelected = new Set([...eliteIds, ...diversityIds]); const remaining = genomes.filter(g => !alreadySelected.has(g.id)); const slotsLeft = Math.max(0, targetSurvivors - alreadySelected.size); const tournamentWinners = tournamentSelect(remaining, slotsLeft, tournamentSize); // 汇总存活者 const survivorIds = new Set([...eliteIds, ...diversityIds, ...tournamentWinners]); // 4. 淘汰 const killedIds = genomes .filter(g => !survivorIds.has(g.id)) .map(g => g.id); if (killedIds.length > 0) { await evolutionQueries.killGenomes(killedIds); } const result = { survivors: survivorIds.size, killed: killedIds.length, elites: eliteIds.length, diversityPreserved: diversityIds.length, tournamentWinners: tournamentWinners.length, }; console.log(` 🏆 精英: ${result.elites} | 多样性: ${result.diversityPreserved} | 锦标赛: ${result.tournamentWinners} | 淘汰: ${result.killed}`); return result; } module.exports = { selectElites, tournamentSelect, diversityPreserve, selectGeneration, };