洗牌限制 Python

shuffling a list with restrictions in Python

我在使用 Python (3) 中的限制随机化列表时遇到问题。我已经看到了其他一些与此相关的问题,但其中 none 似乎确实解决了我的问题。我是初学者,非常感谢任何帮助!

我正在设计一个实验,使用两种类型的刺激:形状和颜色(各四种)。我需要生成所有 16 种组合的排列,这是我用 random.shuffle-function:

完成的
import random

# letters are shapes, numbers are colors
x=["a1","a2","a3","a4","b1","b2","b3","b4","c1","c2","c3","c4","d1","d2","d3","d4"]

random.shuffle(x)

到目前为止一切顺利。但是,我想避免形状(字母)或颜色(数字)在我的结果中连续出现两次(例如 "a2" 后跟 "a4",或 "c2" 后跟 "a2").

有没有办法做这样的限制?
提前致谢!

处理这个问题的一种方法可能是有两个列表,一个是形状,一个是颜色。单独洗牌每个列表。现在混合两个列表。由于每个列表都是随机构建的,因此混合列表也是随机的,但您没有任何两个条目在一起。

请注意,使用 zip,您实际上会得到成对的集合,这将使您能够通过从结果中获取每对来处理您的测试。

在这种特殊情况下,每种颜色都是形状列表的成员,而每种颜色都是颜色列表的成员

shapes = ['a', 'b', 'c', 'd']
colors = ['1', '2', '3', '4']
zip(shapes, colors)
[('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]

这给了我们每个单独的洗牌,而不是一次生成所有 16 种可能性然后洗牌。这可能使您能够更好地生成测试。

如果你想确保两组列表在相同位置的颜色或形状与前一组四个不相同,那么你可以在随机播放后与之前的设置进行测试。

testing = True
while testing:
    newcolors = colors
    random.shuffle(newcolors)
    # perform the test that you want to make get testresult True or False
    if testresult:
        colors = newcolors
        testing = False

这将继续洗牌,直到测试结果变为真并丢弃来自 random.shuffle()

的所有无效结果

虽然从技术上讲您可以使用 itertools.permutations(我先试过了),但这会花费太长时间。

使用它来生成随机序列,不包含彼此共享 属性 的项目:

from random import shuffle

x=["a1","a2","a3","a4","b1","b2","b3","b4","c1","c2","c3","c4","d1","d2","d3","d4"]

def pairwise(some_list):
    one = iter(some_list)
    two = iter(some_list)
    next(two)
    for first, second in zip(one, two):
        yield first, second

while True:
    shuffle(x)
    for first, second in pairwise(x):
        if first[0] == second[0] or first[1] == second[1]:
            break
    else: # nobreak:
        print(x)

您可以通过将随机选项与最后一个值进行比较来分段构建列表。

import random

options = ["a1", "a2", "a3", "a4", "b1", "b2", "b3", "b4",
           "c1", "c2", "c3", "c4", "d1", "d2", "d3", "d4"]

j = random.choice(range(len(options)))
result = [options.pop(j)]
last = result[-1]
while options:
    j = random.choice(range(len(options)))
    candidate = options[j]
    if all([x != y for x, y in zip(last, candidate)]):
        result.append(options.pop(j))
        last = result[-1]

我怀疑这是最好的方式,但这是一种做到这一点的方式。如果您将输入视为这样的矩阵

a1, b1, c1, d1
a2, b2, c2, d2
a3, b3, c3, d3
a4, b4, c4, d4

那么你的目标就是在每次迭代中选择一个随机索引,这样新索引既不在矩阵的同一行也不与前一个索引在同一列,这样就不会选择新元素前。天真地把它放到代码中,它变成了

import random
shapes_and_colors=["a1","a2","a3","a4","b1","b2","b3","b4","c1","c2","c3","c4","d1","d2","d3","d4"]
nRows = 4
nCols = 4
inds = [(x,y) for x in range(nRows) for y in range(nCols)]
def make_random(someArr):
    toRet = []
    n = len(someArr)
    for i in range(n):
        possible = [thing for thing in someArr if thing not in toRet]
        prev = poss = None
        while poss is None:
            next_val = random.choice(possible)
            if next_val == prev:
                #failed so try again
                return make_random(someArr)
            if not toRet or (next_val[0] != toRet[-1][0] and next_val[1] != toRet[-1][1]):
                poss = next_val 
            prev = next_val
        toRet += poss,
    return toRet



ans= [thing for thing in make_random(shapes_and_colors)]
print ans

一对跑步后的输出

['c3', 'd4', 'c1', 'd3', 'b1', 'a4', 'b3', 'c4', 'a3', 'b2', 'a1', 'c2', 'd1', 'a2', 'b4', 'd2']
['d4', 'b3', 'c1', 'a4', 'b2', 'c4', 'd3', 'a1', 'c3', 'a2', 'b4', 'd2', 'a3', 'b1', 'c2', 'd1']

免责声明

由于这是一种完全幼稚的方法,因此有时会卡住!因此,假设剩下的最后两个索引是 [(2, 2), (3, 2)]。那么,算法就不可能在不打破限制的情况下继续进行。现在,我正在通过递归调用处理它,这并不理想。

这样的事情应该在合理的时间内给出合理的答案

import random
while 1:
    choices = ["a1", "a2","a3","b1","b2","b3","c1","c2","c3"]

    shuffle = []

    last = ""

    while choices:
        l = choices
        if last:
            l = [x for x in l if x[0] != last[0] and x[1] != last[1]]
        if not l:
            #no valid solution
            break 
        newEl = random.choice(l)
        last = newEl
        shuffle.append(newEl)
        choices.remove(newEl)
    if not choices:
        print(shuffle)
        break