假设我有两个数组:
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
我想将这两个数组交织到变量'c'(注意'a'和'b'不一定长度相等)但我不希望它们以确定的方式交错。简而言之,仅仅压缩这两个数组是不够的。我不想要:
c = [1, 5, 2, 6, 3, 7, 4, 8, 9]
相反,我想要一些随机的东西:
c = [5, 6, 1, 7, 2, 3, 8, 4, 9]
另请注意,'a'和'b'的顺序保留在结果数组'c'中。
我目前的解决方案需要一个for循环和一些随机数生成。我不喜欢它,我希望有人能指出我更好的解决方案。
# resulting array
c = []
# this tells us the ratio of elements to place in c. if there are more elements
# in 'a' this ratio will be larger and as we iterate over elements, we will place
# more elements from 'a' into 'c'.
ratio = float(len(a)) / float(len(a) + len(b))
while a and b:
which_list = random.random()
if which_list < ratio:
c.append(a.pop(0))
else:
c.append(b.pop(0))
# tack on any extra elements to the end
if a:
c += a
elif b:
c += b
答案 0 :(得分:15)
编辑我认为最近这个是最好的:
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = [x.pop(0) for x in random.sample([a]*len(a) + [b]*len(b), len(a)+len(b))]
或更有效率:
c = map(next, random.sample([iter(a)]*len(a) + [iter(b)]*len(b), len(a)+len(b)))
请注意,上面的第一种方法会修改原始列表(如代码所做的那样),而第二种方法则不会。在Python 3.x上,您需要执行list(map(...))
,因为map
返回迭代器。
以下原始答案:
这是一个保存几行的选项:
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = []
tmp = [a]*len(a) + [b]*len(b)
while a and b:
c.append(random.choice(tmp).pop(0))
c += a + b
这是另一个选项,但只有当您知道所有元素都不是假的时候才会生效(不是0
,''
,None
,False
,或空序列):
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
ratio = float(len(a)) / float(len(a) + len(b))
c = [(not a and b.pop(0)) or (not b and a.pop(0)) or
(random.random() < ratio and b.pop(0)) or a.pop(0)
for _ in range(len(a) + len(b))]
答案 1 :(得分:9)
编辑删除多余的混乱:这是一个适用于任意数量输入列表的解决方案,不会丢弃输入列表,也不会复制它们:
import random
def interleave(*args):
iters = [i for i, b in ((iter(a), a) for a in args) for _ in xrange(len(b))]
random.shuffle(iters)
return map(next, iters)
Stackoverflow用户EOL已经提供了我的解决方案的增强版本:
def interleave(*args):
iters = sum(([iter(arg)]*len(arg) for arg in args), [])
random.shuffle(iters)
return map(next, iters)
使用
运行此功能a = [1,2,3,4]
b = [5,6,7,8,9]
print interleave(a, b)
产生以下结果作为许多可能的结果之一:
[5, 6, 7, 1, 8, 2, 3, 9, 4]
编辑:根据EOL的要求,我更新了时间码。不幸的是,由于接受的解决方案修改了它的输入,我需要在每次迭代时制作一个新的副本。我已经为F.J和我自己的解决方案做了这个,以使结果具有可比性。这是F.Js解决方案的时机:
$ python -m timeit -v -s "from srgerg import accepted" -s "a = list(xrange(40000))" -s "b = list(xrange(60000))" "accepted(list(a), list(b))"
10 loops -> 10.5 secs
raw times: 10.3 10.1 9.94
10 loops, best of 3: 994 msec per loop
这是我的函数版本的时间
$ python -m timeit -v -s "from srgerg import original" -s "a = list(xrange(40000))" -s "b = list(xrange(60000))" "original(list(a), list(b))"
10 loops -> 0.616 secs
raw times: 0.647 0.614 0.641
10 loops, best of 3: 61.4 msec per loop
以下是EOL增强版的时间:
$ python -m timeit -v -s "from srgerg import eol_enhanced" -s "a = list(xrange(40000))" -s "b = list(xrange(60000))" "eol_enhanced(list(a), list(b))"
10 loops -> 0.572 secs
raw times: 0.576 0.572 0.588
10 loops, best of 3: 57.2 msec per loop
如果我从EOL的增强版本的循环中删除列表复制,我得到这个:
$ python -m timeit -v -s "from srgerg import eol_enhanced" -s "a = list(xrange(40000))" -s "b = list(xrange(60000))" "eol_enhanced(a, b)"
10 loops -> 0.573 secs
raw times: 0.572 0.575 0.565
10 loops, best of 3: 56.5 msec per loop
另一个编辑: F.J有一个更新的解决方案,并要求我添加时间:
$ python -m timeit -v -s "from srgerg import fj_updated" -s "a = list(xrange(40000))" -s "b = list(xrange(60000))" "fj_updated(list(a), list(b))"
10 loops -> 0.647 secs
raw times: 0.652 0.653 0.649
10 loops, best of 3: 64.9 msec per loop
答案 2 :(得分:7)
PS :请考虑阅读@srgerg's answer :我认为这是最好的解决方案(尽管F.J相对接近)。与下面的解决方案相比,它更通用,甚至更直接,而且只需要大约两倍的内存。
以下是简单和高效:
[(a if random.randrange(0, len(a)+len(b)) < len(a) else b).pop(0) for _ in range(len(a)+len(b))]
此解决方案避免明确针对a
或b
为空的特定情况进行测试。
此解决方案使用了几个关键点:
randrange()
可以简单地处理整数(无需计算比率)。< len(a)
测试),而无需其他测试,例如a or b
,[… a and b]+a+b
...... 此解决方案很好地处理不同大小的列表:较短列表的元素在结果中非常均匀地分布。此方法还具有“不变性”:可能结果列表的概率分布仅取决于a
和b
列表的当前内容。
使用速度更快.pop()
代替.pop(0)
可以提高效率(因为列表可以快pop()
而不是pop(0)
):
a.reverse(); b.reverse()
[(a if random.randrange(0, len(a)+len(b)) < len(a) else b).pop() for _ in range(len(a)+len(b))]
答案 3 :(得分:6)
编辑TryPyPy的建议:
from random import choice
l = [a, b]
c = [choice(l).pop(0) for i in range(len(a) + len(b)) if (a and b)] + a + b
答案 4 :(得分:6)
这是一个适用于任意数量的迭代的解决方案:
import random
def interleave(*args):
iters = map(iter, args)
while iters:
it = random.choice(iters)
try:
yield next(it)
except StopIteration:
iters.remove(it)
print list(interleave(xrange(1, 5), xrange(5, 10), xrange(10, 15)))
答案 5 :(得分:2)
如何连接,然后改组一组标志,然后用它来挑选一个数组来取出每个项目?
import random
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = list('a' * len(a) + 'b' * len(b)) # Flags for taking items from each array
random.shuffle(c) # Randomize from where we take items
aa, bb = a[:], b[:] # Copy the arrays for popping
d = [aa.pop(0) if source == 'a' else bb.pop(0) for source in c]
# Take items in order, randomly from each array
FogleBird的更有效方式:
c = [a[:]] * len(a) + [b[:]] * len(b)
random.shuffle(c) # Randomize from where we take items
d = [x.pop(0) for x in c] # Take items in order, randomly from each array
答案 6 :(得分:1)
此解决方案为您提供了一个生成器,并通过随机交换尚未发出的列表(a)和(b)的部分来工作。
import random
a = [1,2,3,4]
b = [5,6,7,8,9]
def interleave(a,b):
while a or b:
(a,b)=(a,b) if len(a) and (random.random()<0.5 or not len(b)) else (b,a)
yield a.pop(0)
print list(interleave(a,b))
答案 7 :(得分:1)
这是使用未记录的Python(特别是列表迭代器对象的__length_hint__
方法,它告诉您迭代器中剩余多少项)将其塞入列表推导的东西。我想,更多的是为了好玩,而不是实际的实用性。
itera, iterb = iter(a), iter(b)
morea, moreb = itera.__length_hint__, iterb.__length_hint__
c = [next(itera) if not moreb() or morea() and random.random() < ratio
else next(iterb) for c in xrange(len(a) + len(b))]
答案 8 :(得分:1)
我会像这样解决这个问题:
import random
LResult = []
LLists = [[1, 2, 3, 4], [5, 6, 7, 8, 9]]
while LLists[0] or LLists[1]:
LResult.append(LLists[random.choice([int(len(LLists[0])==0), int(len(LLists[1])!=0)])].pop(0))
LLists是一个多维列表,存储两个列表(示例中的a和b)。该陈述将等同于:LLists = [a [:],b [:]]但是为了简单和清晰起见,我明确地在列表中编码。
LResult来自您的示例,最终存储结果数组。
while循环将循环,直到LLists sub 0和LLists sub 1都完全为空。在循环中,LResult附加一个来自LLists sub 0或LLists sub 1的值。关于选择哪个子列表值的决定由random.choice()语句确定,该语句接受两个(在本例中)参数,然后随机返回其中一个。
提供给random.choice()的选项由LLists中每个子列表的长度决定。如果LLists sub 0的长度大于零,则语句int(len(LLists [0])== 0)将选项号1作为零返回。对于random.choice()的第二个选项,如果LLists sub 1的长度大于零,则语句int(len(LLists [1])!= 0)将返回1.在这两种情况下,如果一个子列表的长度为零,那么该适当的语句将返回相反的数字。也就是说,如果LLists [0]的长度为零,并且LLists [1]的长度大于零,那么结果语句将是random.choice(1,1)。在这种情况下,random.choice()将返回1和1之间的选择(当然是1)。
一旦决定从哪个子列表中提取值,第一项就是该子列表是poped,.pop(0),进入LResult。
答案 9 :(得分:0)
你可以这样做:
(L, l) = (a, b) if len(a) > len(b) else( b, a)
positions = random.sample(range(len(L)), len(l))
for i in range(len(positions)):
L.insert(positions[i], l[i])
但是以我的拙见,你所拥有的一切都很好..它有效,很简单
答案 10 :(得分:0)
这个想法怎么样:
import random as rn
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
n = 100 #Here i am picking an arbitrary number, it should probably be a function of
# lengths of a and b
a_ind = sorted(rn.sample(range(n),len(a))) #sorting the indexes insures that order of
b_ind = sorted(rn.sample(range(n),len(b))) # a and b is preserved
big_list = zip(a,a_ind) + zip(b,b_ind)
big_list.sort(key = lambda k: k[1])
result = list(zip(*big_list)[0])
结果:
>>> result
[1, 5, 2, 6, 3, 7, 8, 9, 4]
答案 11 :(得分:0)
说明中的“交错”一词可能令人困惑。如果您只是添加输入列表然后随机抽取结果,您将获得相同的结果。如果要保留交错的结果,则只需要交织。
一些代码:
>>> import random
>>>
>>> a, b = [1,2,3,4], [5,6,7,8]
>>> c = sum([a,b], [])
>>> random.shuffle(c)
>>> c
[6, 5, 8, 2, 7, 4, 1, 3]
答案 12 :(得分:0)
可能非常低效,但另一种方法有效:
import random
def interleave(*args):
indices=[(i,j) for i in range(len(args)) for j in range(len(args[i]))]
random.shuffle(indices)
indices.sort(key=lambda x:x[1])
return [args[i][j] for i,j in indices]
答案 13 :(得分:0)
我已经改编@NPE's solution所以它会在常量而不是线性时间*中删除空迭代器。它接受任意数量的输入列表,并返回一个随机交错的迭代器,保留输入列表给出的顺序。
def interleave(*args):
iters = [iter(x) for x in args]
while iters:
i = random.randrange(len(iters))
try:
yield next(iters[i])
except StopIteration:
# swap empty iterator to end and remove
iters[i],iters[-1] = iters[-1],iters[i]
iters.pop()
print list(interleave(xrange(1, 5), xrange(5, 10), xrange(10, 15)))
*总运行时间为O(N)
而不是O(N+M^2)
,其中N
是项目总数,M
是列表数。
答案 14 :(得分:0)
如果列表1和列表2之间的比率保持恒定,则可以创建如下函数:
def selectFromTwoList(ratioFromList1):
final_list = []
for i in range(len(list1)):
rand = random.randint(1, 100)
if rand <= ratioFromList1*100:
final_list.append(list1.pop(0))
else:
final_list.append(list2.pop(0))
return final_list