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