You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带约束的随机洗牌问题求解:相邻球颜色不得相同

问题

现有20个带唯一编号的球:编号1-5为黄色,6-10为绿色,11-15为红色,16-20为蓝色。需要设计一种随机洗牌方法,满足约束:相邻两个球颜色不能相同。

尝试过两种方法,但均存在缺陷:

  • 方法1:随机抽取第一个球,之后每次从剩余的不同颜色球中随机抽取。
    问题:可能出现无法完成抽取的情况,例如剩余球均为同一颜色时,无法继续满足相邻颜色不同的约束。
  • 方法2:将球按颜色分为4组(每组5个),从每组随机取一个球组成5个新组,再重组这些组以满足约束(调整最后一组的首个球直至符合要求)。
    问题:某些合法的序列永远无法生成,例如「红、黄、红、黄……」「绿、蓝、绿、蓝……」这类交替序列。
解决方案

编辑补充:以下是Unlikus提供的解决方案实现代码,核心思路是通过计算合法排列数加权随机选择,确保所有合法序列都能被等概率生成,同时避免中途卡壳的问题。

import enum
import random

# 定义颜色枚举
Color = enum.Enum('Color', ['RED', 'GREEN', 'BLUE', "YELLOW"])

# 缓存已计算的合法排列数,避免重复计算
NB_PERMUTATION = {
    # 基础情况:只剩1个某颜色球时,仅1种排列方式
    (Color.RED, 1, 0, 0, 0): 1,
    (Color.GREEN, 0, 1, 0, 0): 1,
    (Color.BLUE, 0, 0, 1, 0): 1,
    (Color.YELLOW, 0, 0, 0, 1): 1,
    # 无剩余球时,排列数为0
    (Color.RED, 0, 0, 0, 0): 0,
    (Color.GREEN, 0, 0, 0, 0): 0,
    (Color.BLUE, 0, 0, 0, 0): 0,
    (Color.YELLOW, 0, 0, 0, 0): 0,
}

def compute_nb_permutations(last_color, red_count, green_count, blue_count, yellow_count):
    """计算以last_color结尾、剩余各颜色球数量为指定值时的合法排列数"""
    try:
        # 优先查询缓存,减少计算量
        return NB_PERMUTATION[(last_color, red_count, green_count, blue_count, yellow_count)]
    except KeyError:
        # 可选颜色为上一个颜色之外的所有颜色
        available_color_values = {1, 2, 3, 4} - {last_color.value}
        # 复制当前剩余球数量,模拟选择后的状态
        remaining = [red_count, green_count, blue_count, yellow_count]
        # 减去已使用的最后一个颜色的球
        remaining[last_color.value - 1] -= 1
        
        # 递归求和所有可选颜色对应的合法排列数
        total = sum(
            compute_nb_permutations(Color(c), *remaining) 
            for c in available_color_values 
            if remaining[c - 1] > 0
        )
        # 将计算结果存入缓存
        NB_PERMUTATION[(last_color, red_count, green_count, blue_count, yellow_count)] = total
        return total

def arrange_color():
    """生成符合相邻颜色不同约束的随机颜色序列"""
    all_colors = set(Color)
    # 初始各颜色球数量均为5
    remaining_counts = [5, 5, 5, 5]
    
    # 第一个颜色随机均匀选择
    color_sequence = [random.choice(list(all_colors))]
    remaining_counts[color_sequence[-1].value - 1] -= 1
    
    # 循环直到所有球都被选中
    while remaining_counts != [0, 0, 0, 0]:
        # 可选颜色排除上一个颜色
        candidates = list(all_colors - {color_sequence[-1]})
        # 计算每个候选颜色对应的合法后续排列数,作为随机选择的权重
        weights = [compute_nb_permutations(c, *remaining_counts) for c in candidates]
        # 按权重随机选择下一个颜色
        next_color = random.choices(candidates, weights=weights)[0]
        color_sequence.append(next_color)
        # 更新剩余球数量
        remaining_counts[next_color.value - 1] -= 1
    
    return color_sequence

def draw():
    """生成带编号的最终洗牌结果"""
    # 初始化各颜色对应的编号列表
    number_pool = {
        Color.RED: list(range(1, 6)),
        Color.GREEN: list(range(6, 11)),
        Color.BLUE: list(range(11, 16)),
        Color.YELLOW: list(range(16, 21)),
    }
    # 打乱每个颜色的编号顺序
    for nums in number_pool.values():
        random.shuffle(nums)
    
    # 根据颜色序列生成编号-颜色映射
    return {number_pool[c].pop(): c.name for c in arrange_color()}

内容的提问来源于stack exchange,提问作者René paul Debroize

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:02:14