/** * 解析整数范围字符串 * @param label 标签字符串,可能是单个整数(如 "1")或整数范围(如 "1-3") * @returns 如果成功解析返回 [min, max],否则返回 null */ export function parseIntegerRange(label: string): [number, number] | null { const trimmed = label.trim(); // 尝试匹配整数范围格式 (如 "1-3") const rangeMatch = trimmed.match(/^(\d+)\s*-\s*(\d+)$/); if (rangeMatch) { const min = parseInt(rangeMatch[1]!, 10); const max = parseInt(rangeMatch[2]!, 10); if (min <= max) { return [min, max]; } } // 尝试匹配单个整数格式 (如 "5") const intMatch = trimmed.match(/^(\d+)$/); if (intMatch) { const num = parseInt(intMatch[1]!, 10); return [num, num]; } return null; } /** * 计算加权随机的总权重 * @param labels 标签数组 * @returns 总权重值 */ export function calculateTotalWeight(labels: string[]): number { let total = 0; for (const label of labels) { const range = parseIntegerRange(label); if (range) { const [min, max] = range; total += max - min + 1; } } return total; } /** * 根据权重随机选择一个索引 * @param labels 标签数组 * @param totalWeight 总权重(可选,会自行计算) * @returns 选中的索引,如果没有有效权重则返回 -1 */ export function weightedRandomIndex(labels: string[], totalWeight?: number): number { const weight = totalWeight ?? calculateTotalWeight(labels); if (weight === 0) { return -1; } // 生成一个随机权重值 (1 到 totalWeight) const roll = Math.floor(Math.random() * weight) + 1; let cumulative = 0; for (let i = 0; i < labels.length; i++) { const range = parseIntegerRange(labels[i]!); if (range) { const [min, max] = range; const labelWeight = max - min + 1; cumulative += labelWeight; if (roll <= cumulative) { return i; } } } // 理论上不会到这里,但为了安全返回最后一个 return labels.length - 1; } /** * 检查所有 label 是否都是整数或整数范围格式 * @param labels 标签数组 * @returns 如果所有 label 都是整数/整数范围格式返回 true */ export function areAllLabelsNumeric(labels: string[]): boolean { if (labels.length === 0) return false; return labels.every(label => parseIntegerRange(label) !== null); }