List shuffling algorithm to minimize equal neighbors

I want to shuffle a list like this: -

to_shuffle = [ a, b, b, b, b, a, c, b, a, b ]

to minimize the number of repeating elements. Initially, I thought about popping items from the top to_shuffleand either clicking them on a different list shuffledif the item is different from the previously put forward item, or the bottom to_shuffleand try another item. This will result in: -

shuffled = [ a, b, a, c, b, a, b, b, b, b ]

which in this example is not better - there are still 4 b in a row (although this method sometimes reduces duplicate elements).

What I thought then was to begin by creating a bucket for each class element: -

buckets = [ (a, [a, a, a]), (b, [b, b, b, b, b, b]), (c, [c]) ]

sort buckets by size, descending

buckets = [ (b, [b, b, b, b, b, b]), (a, [a, a, a]), (c, [c]) ]

tracking the last item moved

last = None

, , last, : -

sorted = [ b ]

buckets = [ (b, [b, b, b, b, b]), (a, [a, a, a]), (c, [c]) ] 
last = b

sorted = [ b, a ]

buckets = [ (b, [b, b, b, b, b]), (a, [a, a]), (c, [c]) ] 
last = a

sorted = [ b, a, b ]

buckets = [ (b, [b, b, b, b]), (a, [a, a]), (c, [c]) ] 
last = b

sorted = [ b, a, b, a ]

buckets = [ (b, [b, b, b, b]), (a, [a]), (c, [c]) ] 
.
.
.
sorted = [ b, a, b, a, b, a, b, c, b, b ]

.

, , python (2.7)?

: -

test = [ 'a', 'b', 'b', 'b', 'b', 'a', 'c', 'b', 'a', 'b' ]
expected = [ 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'c', 'b', 'b' ]

def sort_buckets(buckets):
    return sorted(buckets, key=lambda x: len(x[1]), reverse=True)

def make_buckets(to_shuffle):
    h = {}
    buckets = []
    for e in to_shuffle:
        if e not in h:
            h[e] = []
        h[e].append(e)
    for k, elems in h.iteritems():
        buckets.append((k, elems))
    return buckets

def shuffle(to_shuffle):
    buckets = make_buckets(to_shuffle)
    shuffled = []
    last = ''
    while len(buckets) > 1:
        buckets = sort_buckets(buckets)
        for i in range(len(buckets)):
            candidate = buckets[i][0]
            if candidate == last:
                continue
            t = buckets.pop(i)
            last = candidate
            shuffled.append(t[1][-1])
            if len(t[1]) > 1:
                buckets.append((t[0], t[1][:-1]))
            break
    t = buckets.pop()
    shuffled += t[1]
    return shuffled

print expected
print shuffle(test)
+4
2

, , , .

, , , {b,b,b,b,b,b,a,a,a,c}, " " {b,_,b,_,b,_,b,_,b,_}, ( , ): {b,c,b,a,b,a,b,a,b,b}. . , (, , ) , , . , .

, , : {a,a,b,b,c,c} = > {a,_,a,_,b,_}, , , , : {a,b,a,c,b,c}.

+2

, , . Counter Python.

from collections import Counter

def careful_shuffle(lst):
    '''Returns a new list based on a given iterable, with the elements shuffled
    such that the number of duplicate consecutive elements are minimized.'''

    c = Counter(lst)
    if len(c) < 2:
        # Return early if it trivial.
        return lst

    output = []
    last = None
    while True:
        # All we need are the current 2 most commonly occurring items. This
        # works even if there only 1 or even 0 items left, because the
        # Counter object will still return the requested number of results,
        # with count == 0.
        common_items = c.most_common(2)
        avail_items = [key for key, count in common_items if count]

        if not avail_items:
            # No more items to process.
            break

        # Just reverse the list if we just saw the first item. This simplies
        # the logic in case we actually only have 1 type of item left (in which
        # case we have no choice but to choose it).
        if avail_items[0] == last:
            avail_items.reverse()

        # We've found the next item. Add it to the output and update the
        # counter.
        next_item = avail_items[0]
        output.append(next_item)
        c[next_item] -= 1
        last = next_item

    return output
+1

All Articles