Python 如何洗牌带有约束的列表(1和2、3和4、5和6不相邻)?

Python 如何洗牌带有约束的列表(1和2、3和4、5和6不相邻)?,python,algorithm,random,Python,Algorithm,Random,我有6个测试问题,我想随机化,连同他们的正确答案。问题1和2、3和4、5和6属于同一类型。为了不让测试太容易,我不想连续显示1和2,也不想显示3和4,或者5和6 出于这个目的,我想我应该用这个约束条件洗牌一个列表[1,2,3,4,5,6]:1和2,3和4,5和6不是相邻的。例如,[1,2,4,6,3,5]是不可接受的,因为1和2相邻。然后,我想对问题列表和答案列表应用新的顺序 作为一名编程新手,我只知道如何无约束地洗牌列表,如下所示: question = [1, 3, 5, 2, 4, 6]

我有6个测试问题,我想随机化,连同他们的正确答案。问题1和2、3和4、5和6属于同一类型。为了不让测试太容易,我不想连续显示1和2,也不想显示3和4,或者5和6

出于这个目的,我想我应该用这个约束条件洗牌一个列表[1,2,3,4,5,6]:1和2,3和4,5和6不是相邻的。例如,[1,2,4,6,3,5]是不可接受的,因为1和2相邻。然后,我想对问题列表和答案列表应用新的顺序

作为一名编程新手,我只知道如何无约束地洗牌列表,如下所示:

question = [1, 3, 5, 2, 4, 6]
answer = ['G', 'Y', 'G', 'R', 'Y', 'R']
order = list(zip(question, answer))
random.shuffle(order)
question, answer = zip(*order)
任何帮助都将不胜感激

我看到两种简单的方法:

洗牌列表,如果它满足约束,则接受洗牌,否则重复

迭代地采样数字,并使用约束限制可能的数字。例如,如果第一次绘制1,则第二次绘制可以是3..6。这也可能导致解决方案不可行,因此您必须对此进行说明


这里有一个蛮力的方法。它只是反复洗牌列表,直到找到有效的顺序:

import random

def is_valid(sequence):
    similar_pairs = [(1, 2), (3, 4), (5, 6)]
    return all(
        abs(sequence.index(a) - sequence.index(b)) != 1
        for a, b in similar_pairs
    )

sequence = list(range(1, 7))
while not is_valid(sequence):
    random.shuffle(sequence)

print(sequence)

# One output: [6, 2, 4, 5, 3, 1]

对于这么小的输入,这是很好的。计算机很快。对于较长的输入,您可能希望考虑做一些更有效的事情,但听起来您想要的是一种简单实用的方法,而不是理论上最优的方法。

绘制一个以列表元素为顶点的图形。如果元素u和v在输出列表中可以相邻,则在它们之间绘制一条边u,v,否则不绘制

现在你必须在这个图上找到一条哈密顿路径。这个问题通常是难以解决的NP完全问题,但如果图几乎是完全的,则几乎没有约束,即缺少边,则可以通过DFS有效地解决该问题


对于像您的示例中这样的小输入集,只需生成所有置换,然后过滤掉违反其中一个约束的置换,可能会更容易。

您可以尝试这样做。这对于小列表应该很好。正如您在下面看到的,我使用了一个python集列表作为约束。该代码逐元素构建所需的排列

如果列表中的其余元素在某一点上都受到约束的限制,则逐个元素构建可能会导致无效排列

示例:如果代码生成4,1,3,2,6,则强制尝试使用5作为最后一个元素,但这是无效的,因此函数尝试进行另一个置换

在生成随机洗牌并检查其是否有效(smarx给出的答案)的性能方面,它比蛮力方法要好

注意:如果没有满足约束条件的排列,函数将导致无限循环

import random

def shuffler(dataList, constraints):
    my_data_list = list(dataList)
    shuffledList = [random.choice(dataList)]
    my_data_list.remove(shuffledList[0])
    for i in range(1, list_size):
        prev_ele = shuffledList[i - 1]
        prev_constraint = set()
        for sublist in constraints:
            if prev_ele in sublist:
                prev_constraint = set.union(prev_constraint, sublist)
        choices = [choice for choice in my_data_list if choice not in prev_constraint]
        if len(choices) == 0:
            print('Trying once more...')
            return shuffler(dataList,constraints)
        curr_ele = random.choice(choices)
        my_data_list.remove(curr_ele)
        shuffledList.append(curr_ele)
    return shuffledList

if __name__ == '__main__':
    dataList = [1, 2, 3, 4, 5, 6]
    list_size = len(dataList)
    constraints = [{1,2},{3,4},{5,6}]
    print(shuffler(dataList,constraints))

您可以尝试以下方法:

shuffle the list
while (list is not good)
  find first invalid question
  swap first invalid question with a different random question
endwhile

我没有做任何计时,但它可能比重新排列整个列表要快。它在第一个无效问题之前部分保留了有效部分,因此应该更快地达到良好排序。

不相邻是什么意思?一个或多个示例?您能提供您迄今为止尝试过的内容和问题中的示例输出吗?@nikpod所说的不相邻,我的意思是像[1,2,3,5,4,6],[3,1,5,6,2,4]等的内容是不可接受的,因为前者中的1和2是相邻的,后者中的5和6是相邻的。这可能会帮助您获得解决方案。如果您不测试随机排列并自己创建随机索引,那么您可以以更优化的方式进行。这是最残忍的方式。