I am trying to create a function to mix two lists in python, or I rather say put the element of list2 into list1. It is required that in the output list, no more than two elements next two each other have the same value
For example:
list1 = [1,1,1,1,1]
list2 = [2,2,2,2,2]
output = [1,1,2,1,2,2,1,2,1,2]
Wrong output example:
# There are more than two '1' standing next two each other
output = [1,1,1,2,2,1,2,1,2,2]
Here is my solution:
def guyGenerator(toughGuy,softGuy):
    i = 0
    while len(softGuy) > 0:
        temp = softGuy[:1]
        while i < len(toughGuy) - 1:
            if toughGuy[i] == toughGuy[i + 1] == 2:
                toughGuy.insert(random.randint(i, i + 1), temp[0])
            i = i + 1
        softGuy = softGuy[1:]
    return toughGuy
The issue is getting the output with more than two of the same elements standing next two each other or the length of the output list is longer than combine length of two lists
For example one of my output
[2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2]
What am I doing wrong here ?
Edit 1
Clodion asked me so I put this up here
The results can be random, as long as they satisfy the requirement in the question. The order of the elements does not matter as long as no more than two elements standing next to each other have the same value
Edit 2
I am trying to mess around by create a iterable class, using Clodion solution. Here is the new code:
import random
class GuyGenerator:
    def __init__(self,toughGuyList,softGuyList):
        self.toughGuyList = toughGuyList
        self.softGuyList = softGuyList
    def __iter__(self):
        return self
    def __next__(self):
        listSum = self.toughGuyList + self.softGuyList
        while True:
            res = random.sample(listSum,len(listSum))
            if not any([res[i-2]==res[i-1]==res[i] for i in range(len(listSum))]):
                break
        return res
toughGuy = ['tough','tough','tough','tough','tough','tough','tough','tough']
softGuy = ['soft','soft','soft','soft','soft','soft','soft','soft']
for guy in GuyGenerator(toughGuy,softGuy):
    print(guy)
The result turned out quite well, except for the code executing unlimited and I have to use KeyboardInterrupt to stop the code. What am I doing wrong in this case ? I would be grateful with a thorough explain since I am a newbie in Python and Iterator
Edit 3
Solved the iterator problem, easier than I think. It turned out that what returned in the iter is what the class return when calling in the for-loop
Solution:
def __iter__(self):    
    listSum = self.listRandom1 + self.listRandom2
    while True:
        self.res = random.sample(listSum,len(listSum))
        if not any([self.res[i-2]==self.res[i-1]==self.res[i] for i in range(len(listSum))]):
            break
    return iter(self.res)
                You could implement a greedy algorithm which tries to yield the most common item as frequently as possible (i.e. up to twice), and then yield the next most common item when necessary.
This has two advantages over search by random shuffle:
The greedy algorithm is much faster as the length of items increases:
In [223]: %timeit list(intermix([1]*10+[2]*5))
10000 loops, best of 3: 39.8 µs per loop
In [222]: %timeit intermix_random([1]*10+[2]*5)
100 loops, best of 3: 6.85 ms per loop
It can identify when there is no solution, whereas a random shuffle search loops forever if visited shuffles are not cached.
import collections
def intermix(items, nconsecutive=2):
    counter = collections.Counter(items)
    # sort from most common to least common
    items = sorted(items, key=counter.get, reverse=True)
    N = len(items)
    count = 0
    # remember the last two values
    last = []
    for i in range(N):
        val = items[i]
        if len(last) < nconsecutive:
            if last and val == last[-1]:
                last.append(val)
            else:
                last = [val]
            counter[val] -= 1
            yield val
        else:
            # last is full; find a different value
            for j in range(i, N):
                if items[j] != last[-1]:
                    items[i], items[j] = items[j], items[i]
                    val = items[i]
                    last = [val]
                    counter[val] -= 1
                    # as items are yielded, the meaning of "most common" can change.
                    items[i+1:] = sorted(items[i+1:], key=counter.get, reverse=True)
                    yield val
                    break
            else:
                raise ValueError('No solution possible')
In [184]: list(intermix([1,1,1,1,1,2,2,2,2,2]))
Out[184]: [1, 1, 2, 2, 1, 2, 2, 1, 2, 1]
In [185]: list(intermix([1,0,1,1,2,1,0,1,1,1,2]))
Out[185]: [1, 1, 0, 1, 1, 2, 1, 1, 2, 1, 0]
In [186]: list(intermix([1,0,1,1,2,1,0,1,1,1,1,1,1,2]))
Out[186]: [1, 1, 0, 1, 1, 2, 1, 1, 2, 1, 1, 0, 1, 1]
In [187]: list(intermix([1,0,1,1,2,1,0,1,1,1,1,1,1,1,2]))
ValueError: No solution possible
In [188]: list(intermix([1,0,1,1,2,1,0,1,1,1,1,1,1,1,2], nconsecutive=3))
Out[188]: [1, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0]
                        You can use shuffle from random:
list1=[1,1,1,1,1]
list2=[2,2,2,2,2]
import random
lst = list1+list2
print(lst)
while True:
    random.shuffle(lst)
    if not any([lst[i-2]==lst[i-1]==lst[i]for i in range(len(lst))]):
        break
print(lst)
Result:
[1, 2, 1, 1, 2, 1, 2, 2, 1, 2]
Explanation: I add the two list and suffle it. Then I repeat shuffle until there's not 3 same consecutive numbers. As comment point out, it may take a long time but it's easy to make a limit of iterations.
Trying to explain why your code don't work:
def guyGenerator(toughGuy,softGuy):
    import random
    i = 0
    while len(softGuy) > 0:
        temp = softGuy[:1]
        while i < len(toughGuy)- 1:
            if toughGuy[i] == toughGuy[i + 1] == 2:
                # you insert temp[0]: it can be inserted before i
                # for lst = [0, 1, 2, 3]
                # lst.insert(0, "X") result in ["X", 0, 1, 2, 3]
                # But then, the next time, you'll test exactly the
                # same thing: because you increment i and have move all
                # the items of toughGuy one char to the right
                # and that, without reducing softGuy len.
                toughGuy.insert(random.randint(i, i + 1), temp[0])
            i = i + 1
        softGuy = softGuy[1:]
    return toughGuy
print(guyGenerator([2,2,2,2,2], [1,1,1,1,1]))
Well? I'm clear?
lst = [1, 2, 3, 4, 5]
>>> lst.insert(0, "X")
>>> lst
['X', 1, 2, 3, 4, 5]
>>> lst = [1, 2, 3, 4, 5]
>>> lst.insert(1, "X")
>>> lst
[1, 'X', 2, 3, 4, 5]
>>> 
If randint give i, then "X" is inserted before the items.
You increment i and you so i locate exactly the same element:
>>> lst = [1, 2, 3, 4, 5]
>>> lst.insert(0, "X")
>>> lst
['X', 1, 2, 3, 4, 5]
>>> lst.insert(1, "X")
>>> lst
['X', 'X', 1, 2, 3, 4, 5]
>>> 
That's the problem, because you're still in the same loop!!!
So you can insert more than one temp[0] before reducing softGuy by one element. Yes?
Any suggestion would be welcome!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With