shuffling a list with restrictions in Python

久未见 提交于 2019-12-05 08:18:38

Something like this should give a reasonable answer in a reasonable time

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

One way to handle this might be to have two lists one of shapes and one of colors. Shuffle each list individually. Now intermix the two lists. Since each list was built randomly, the mixed list is random as well, but you do not have any two entries together.

Note that using zip, you will actually get sets of pairs which will enable you to handle your test by getting each pair from the result.

In this particular case each color is a member of the list shapes while each color is a member of the list colors

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

This gives us each individual shuffle rather than generating all 16 possibilities at once and then shuffling them. This might enable you to generate your test better.

If you want to ensure that two sets of lists do not have the same color or shape in the same position as the previous group of four, then you can test for that after the shuffle against the previous setting.

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

This will keep shuffling until testresult becomes True and discard all invalid results from random.shuffle()

I doubt this is the best way, but it is a way of doing this. If you think about your input as a matrix like this

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

Then you're goal becomes choosing an random index at each iteration such that the new index is not in the same row nor the same column of the matrix as the previous index and such that the new element has not been chosen before. Putting that into code naively, it becomes

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

Outputs After A Couple Runs

['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']

Disclaimer

Since this is a totally naive approach, it sometimes gets stuck! So suppose the last two indices remaining are [(2, 2), (3, 2)]. Then, there is no possible way for the algorithm to proceed without breaking the restrictions. Right now, I'm handling it with a recursive call, which isn't ideal.

While you could technically use itertools.permutations (I tried that first), that would take far too long.

Use this to generate random sequences without items that share a property following eachother:

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)

You can build the list piece wise by comparing a random option to the last value.

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]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!