如何为数组长度设置权重并生成可变列数的特征数组
问题描述
我一直搞不定怎么给数组长度设置权重,想让数组里的特定列按概率出现,生成不同长度的结果。
当前输出
# Output 1 ['green eyes', 'black hair', 'girl', 'human', 'happy', 'propeller beanie', 'kitsune mask', 'smurk', 'horns', 'monocle', 'wings', 'halo', [...]] # Output 2 ['brown eyes', 'brown hair', 'boy', 'human', 'tired', 'propeller beanie', 'oni mask', 'frowning', 'cat ears', 'tail', 'bat wings', 'halo', [...]]
目标输出示例
# Output1 ['green eyes', 'black hair', 'girl', 'happy', 'smurk'] # Output2 ['green eyes', 'black hair', 'girl', 'elf', 'sad', 'horns', 'wings'] # Output3 ['green eyes', 'black hair', 'girl', 'kitsune mask']
核心需求
输出元素来自不同加权列表,比如:
feeling = ["tired", "happy", "sad", "grumpy"]
- 前3列(眼睛颜色、头发颜色、性别)必须出现(100%概率)
- 第4列及以后的特征(种族、情绪、头饰等)各自有独立的出现概率:种族22%、情绪18%、头饰12%、面饰9%、表情15%、杂项10%、配饰7%、翅膀6%、神性1%
- 以情绪为例:18%概率出现该列,且出现时"happy"的选中概率是32%
之前尝试把数组截到前2列,再用random.choices给后续列加权合并,但每次只能取单个值,迭代会导致重复元素,没成功生成可变长度的输出。
当前代码
import random import math import os # 假设这些列表已提前定义 modelSex = ["girl", "boy"] race = ["human", "elf", "dwarf", "orc", "halfling", "gnome", "tiefling"] feeling = ["tired", "happy", "sad", "grumpy"] hairColor = ["black", "brown", "blonde", "red", "auburn", "white", "gray", "blue", "green", "purple", "pink", "silver", "gold", "orange"] eyeColor = ["green", "brown", "blue", "hazel", "amber", "gray", "violet", "red", "gold", "silver", "pink", "black", "orange", "yellow"] headWear = ["propeller beanie", "cap", "hat", "helmet", "bandana", "tiara", "crown", "headband", "hood"] faceWear = ["kitsune mask", "oni mask", "sunglasses", "monocle", "eyepatch"] expression = ["smirk", "frowning", "neutral"] misc = ["horns", "cat ears", "tail"] classy = ["monocle", "top hat", "cane", "gloves"] wings = ["wings", "bat wings", "dragon wings"] divine = ["halo"] modelSexOdds = random.choices(modelSex, weights=(50, 50)) # always raceOdds = random.choices(race, weights=(6, 10, 3, 9, 60, 5, 7)) feelingOdds = random.choices(feeling, weights=(15, 32, 29, 24)) hairOdds = random.choices( hairColor, weights=(13, 8, 12, 11, 5, 7, 10, 8, 6, 5, 8, 4, 2, 1) ) eyeOdds = random.choices( eyeColor, weights=(13, 8, 12, 11, 5, 7, 10, 8, 6, 5, 8, 4, 2, 1) ) headWearOdds = random.choices(headWear, weights=(27, 14, 13, 15, 10, 7, 5, 2, 7)) faceWearOdds = random.choices(faceWear, weights=(5, 20, 23, 35, 17)) expressionOdds = random.choices(expression, weights=(15, 47, 38)) miscOdds = random.choices(misc, weights=(30, 40, 10)) classyOdds = random.choices(classy, weights=(25, 8, 36, 31)) wingsOdds = random.choices(wings, weights=(37, 26, 37)) traits = [ "Eye Color", "Hair Color", "Sex", "Race", "Feeling", "Head Wear", "Face Wear", "Expression", "Misc", "Classy", "Wings", "Divine", ] trait_options = { trait_name: [ os.path.splitext(x)[0] for x in os.listdir("scripts/Prompts/assets/" + trait_name) ] for trait_name in traits if trait_name != "modelSex" } trait_options["Eye Color"] = eyeOdds trait_options["Hair Color"] = hairOdds trait_options["Sex"] = modelSexOdds trait_options["Race"] = raceOdds trait_options["Feeling"] = feelingOdds trait_options["Head Wear"] = headWearOdds trait_options["Face Wear"] = faceWearOdds trait_options["Expression"] = expressionOdds trait_options["Misc"] = miscOdds trait_options["Classy"] = classyOdds trait_options["Wings"] = wingsOdds trait_options["Divine"] = divine max_possible_combinations = math.prod([len(trait_options[t]) for t in trait_options]) # eyeColor, 0 | 100% # hairColor, 1 | 100% # modelSex, 2 | 100% # raceOdds, 3 | 22% # feelingOdds, 4 | 18% # headWearOdds, 5 | 12% # faceWearOdds, 6 | 9% # expressionOdds, 7 | 15% # miscOdds, 8 | 10% # classyOdds, 9 | 7% # wingsOdds, 10 | 6% # divineOdds, 11 | 1% traitsOdds = [ raceOdds, feelingOdds, headWearOdds, faceWearOdds, expressionOdds, miscOdds, classyOdds, wingsOdds, divine, ] allOdds = random.choices(traitsOdds, weights=(22, 18, 12, 9, 15, 10, 7, 6, 1)) print(allOdds) dnas = [] for i in range(2): dna = {} for t in traits: dna[t] = trait_options[t][random.randint(0, len(trait_options[t]) - 1)] dnas.append(dna) for dna in dnas: text_config = [] for trait_name, trait_value in dna.items(): if trait_name == "modeSexx": pass else: text_config.append(f"{trait_value}") text_config.append(text_config) print(text_config)
解决方案
核心思路是:先固定必选特征,再对可选特征逐个判断是否保留,同时处理每个特征内部的加权选择。
优化后的代码
import random import os # 定义所有特征列表及权重 # 必选特征(100%出现) mandatory_traits = [ ("Eye Color", eyeColor, (13, 8, 12, 11, 5, 7, 10, 8, 6, 5, 8, 4, 2, 1)), ("Hair Color", hairColor, (13, 8, 12, 11, 5, 7, 10, 8, 6, 5, 8, 4, 2, 1)), ("Sex", modelSex, (50, 50)) ] # 可选特征(带出现概率+内部元素权重) optional_traits = [ ("Race", race, (6, 10, 3, 9, 60, 5, 7), 22), ("Feeling", feeling, (15, 32, 29, 24), 18), ("Head Wear", headWear, (27, 14, 13, 15, 10, 7, 5, 2, 7), 12), ("Face Wear", faceWear, (5, 20, 23, 35, 17), 9), ("Expression", expression, (15, 47, 38), 15), ("Misc", misc, (30, 40, 10), 10), ("Classy", classy, (25, 8, 36, 31), 7), ("Wings", wings, (37, 26, 37), 6), ("Divine", divine, (100,), 1) # 神性只有一个元素,权重设为100% ] def generate_trait_list(): trait_list = [] # 添加必选特征 for name, options, weights in mandatory_traits: # 从选项中加权选择一个元素 selected = random.choices(options, weights=weights)[0] trait_list.append(selected) # 处理可选特征:逐个判断是否保留 for name, options, weights, appearance_prob in optional_traits: # 按概率决定是否添加该特征 if random.randint(1, 100) <= appearance_prob: selected = random.choices(options, weights=weights)[0] trait_list.append(selected) return trait_list # 生成3个示例输出 for i in range(3): result = generate_trait_list() print(f"# Output{i+1}") print(result)
代码说明
- 拆分特征类型:把特征分成必选和可选两类,必选直接全部添加,可选则按设定的概率判断是否加入结果。
- 双层权重处理:
- 第一层:对每个可选特征,用
random.randint(1,100)判断是否满足出现概率(比如18%概率就判断随机数≤18)。 - 第二层:若特征被选中,再用
random.choices按内部权重选取具体元素(比如情绪里"happy"占32%)。
- 第一层:对每个可选特征,用
- 避免重复元素:每个特征只处理一次,不会重复添加,解决了之前迭代导致重复的问题。
- 结构清晰:把特征配置集中管理,后续调整概率或选项更方便。
内容的提问来源于stack exchange,提问作者Norepi
相关产品推荐
相关产品推荐

