15

2 つの配列があるとします。

a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]

これらの 2 つの配列を変数 'c​​' にインターリーブしたい ('a' と 'b' は必ずしも同じ長さではないことに注意してください) が、決定論的な方法でインターリーブしたくありません。つまり、これら 2 つの配列を圧縮するだけでは十分ではありません。私はほしくない:

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
4

15 に答える 15

16

編集:この最近のものは最高だと思います:

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)))

上記の最初の方法は元のリストを (コードが行ったように) 変更しますが、2 番目の方法は変更しないことに注意してください。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別のオプションがありますが、すべての要素が偽ではない ( 、''NoneFalseまたは空のシーケンスがない)ことがわかっている場合にのみ機能します。

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))]
于 2012-05-17T23:57:36.123 に答える
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)

多くの考えられる結果の 1 つとして、次の結果が得られます。

[5, 6, 7, 1, 8, 2, 3, 9, 4]

編集: EOL の要求で、タイミング コードを更新しました。残念ながら、受け入れられたソリューションはその入力を変更するため、反復ごとに新しいコピーを作成する必要があります。結果を同等にするために、FJと私自身のソリューションの両方でこれを行いました。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

別の編集: FJ には更新されたソリューションがあり、タイミングを追加するように依頼されました。

$ 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
于 2012-05-18T00:27:30.887 に答える
7

PS@srgergの回答を読むことを検討してください:私の意見では、これが最良の解決策です(FJは比較的近いですが)。以下のソリューションと比較すると、より一般的で、より単純であり、約 2 倍のメモリしか必要としません。

シンプル効率的なものを次に示します。

[(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

このソリューションは、さまざまなサイズのリストを適切に処理します。短いリストの要素は、結果に非常に均等に分散されます。このアプローチには「不変」という特徴もあります。可能な結果リストの確率分布は、 andリストの現在の内容にのみ依存します。ab

.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))]
于 2012-05-18T08:28:45.930 に答える
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
于 2012-05-18T00:41:05.117 に答える
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)))
于 2012-05-18T07:46:51.377 に答える
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
于 2012-05-18T00:03:38.750 に答える
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))]
于 2012-05-18T00:18:37.953 に答える
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 は、2 つのリスト (例の a と b) を格納する多次元リストです。このステートメントは次のようになります。

LResult は例の c であり、最終的に結果の配列を格納します。

while ループは、LList サブ 0 と LList サブ 1 の両方が完全に空になるまでループします。ループ内で、LResult に LLists サブ 0 または LLists サブ 1 のいずれかからの値が追加されます。どのサブ リストの値が選択されるかについての決定は、2 つの (この場合) 引数を取る random.choice() ステートメントによって決定されます。それらの 1 つをランダムに返します。

random.choice() に提供されるオプションは、LList 内の各サブリストの長さによって決まります。LLists サブ 0 の長さがゼロより大きい場合、選択番号 1 は、ステートメント int(len(LLists[0])==0) によってゼロとして返されます。random.choice() の 2 番目のオプションでは、LLists サブ 1 の長さがゼロより大きい場合、ステートメント int(len(LLists[1])!=0) は 1 を返します。サブリストの長さがゼロの場合、その適切なステートメントは反対の数を返します。つまり、LLists[0] の長さがゼロで、LLists[1] の長さがゼロより大きい場合、結果のステートメントは random.choice(1, 1) になります。この場合、 random.choice() は 1 と 1 の間の選択肢を返します (もちろん 1 です)。

どのサブ リストから値を取得するかが決定されると、最初の項目は、サブ リスト .pop(0) が LResult にポップされることです。

于 2012-05-26T17:45:17.993 に答える
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))
于 2012-05-18T00:11:52.680 に答える
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] 
于 2012-05-21T15:17:42.887 に答える
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]
于 2012-05-18T00:35:50.887 に答える
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]
于 2013-10-23T13:43:37.503 に答える
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])

しかし、私の謙虚な意見では、あなたが持っているものは完全に問題ありません..それは機能します、それは簡単です

于 2012-05-18T00:06:24.060 に答える