"平衡"符号列表

时间:2015-03-09 18:31:10

标签: python algorithm list python-3.x numpy

考虑一个列表,其中包含从一组符号中提取的元素,例如: {A, B, C}

List             --> A, A, B, B, A, A, A, A, A, B,  C,  C,  B, B
Indexing indices --> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

我如何重新排序此列表,以便对于任何符号,我们在列表的前半部分中有大约一半的符号,即列表的[0, [N/2]]和一半在下半场?即[[N/2, N]]

请注意,此问题可能有多种解决方案。我们还想计算排列索引的结果列表,以便我们可以将新排序应用于与原始索引关联的任何列表。

这个问题有名字吗?它的任何有效算法?我能想到的大部分解决方案都是非常强力的。

4 个答案:

答案 0 :(得分:5)

您可以在此处使用字典,这将花费O(N)时间:

from  collections import defaultdict


lst = ['A', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'B', ' C', ' C', ' B', 'B']
d = defaultdict(list)
for i, x in enumerate(lst):
    d[x].append(i)

items = []
indices = []
for k, v in d.items():
    n = len(v)//2
    items.extend([k]*n)
    indices.extend(v[:n])

for k, v in d.items():
    n = len(v)//2
    items.extend([k]*(len(v)-n))
    indices.extend(v[n:])

print items
print indices

<强>输出:

['A', 'A', 'A', ' C', 'B', 'B', 'A', 'A', 'A', 'A', ' C', 'B', 'B', ' B']
[0, 1, 4, 10, 2, 3, 5, 6, 7, 8, 11, 9, 13, 12]

答案 1 :(得分:2)

您可以通过获取符号的排名顺序,然后为输出数组的每一半选择备用排名来执行此操作:

x = np.array(['A', 'A', 'B', 'B', 'A', 'A', 'A',
              'A', 'A', 'B', 'C', 'C', 'B', 'B'])

order = np.argsort(x)
idx = np.r_[order[0::2], order[1::2]]

print(x[idx])
# ['A' 'A' 'A' 'A' 'B' 'B' 'C' 'A' 'A' 'A' 'B' 'B' 'B' 'C']
print(idx)
# [ 0  4  6  8  3 12 10  1  5  7  2  9 13 11]

默认情况下,np.argsort使用快速排序算法,平均时间复杂度 O(N log N)。索引步骤将是 O(1)

答案 2 :(得分:1)

使用索引对列表进行随机播放,然后将其拆分为一半。这种方法每次都不会完美地分割符号,但随着每个符号的重复次数变大,它将接近完美的分割。

import random

symbols = ['A', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'B',  'C', 'C', 'B', 'B']
indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

both = zip(symbols, indices)
random.shuffle(both)

symbols2, indices2 = zip(*both)

print symbols2
print indices2

一些示例输出:

试验#1:

('A', 'C', 'B', 'A', 'A', 'B', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'C')
( 7,   10,  2,   4,   1,   13,  8,   0,   5,   6,   9,   3,   12,  11)
#                                  |

试验#2

('A', 'A', 'B', 'B', 'C', 'A', 'A', 'A', 'B', 'C', 'A', 'A', 'B', 'B')
( 6,   0,   9,   3,   11,  1,   8,   4,   13,  10,  7,   5,   2,   12)
#                                  |

试验#3

('A', 'A', 'C', 'C', 'B', 'B', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'B')
( 4,   5,   11,  10,  2,   3,   0,   13,  12,  6,   7,   8,   1,   9)
#                                  |

答案 3 :(得分:1)

您可以使用collections.Counter甚至比defaultdict更好 - 您可以分别将项目放入上半部分和后半部分。这样,如果你愿意,你可以随意改变前半部分和后半部分(并且只需跟踪改组排列,例如NumPy的argsort)。

import collections

L = ['A', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'B',  'C',  'C',  'B', 'B']

idx_L = list(enumerate(L))
ctr = collections.Counter(L)

fh = []
fh_idx = []
sh = []
sh_idx = []

for k, v in ctr.iteritems():
    idxs = [i for i,e in idx_L if e == k]
    fh = fh + [k for i in range(v//2)]
    fh_idx = fh_idx + idxs[:v//2]  
    sh = sh + [k for i in range(v // 2, v)] 
    sh_idx = sh_idx + idxs[v//2:]

shuffled = fh + sh
idx_to_shuffled = fh_idx + sh_idx
​
print shuffled
print idx_to_shuffled

给出了

['A', 'A', 'A', 'C', 'B', 'B', 'A', 'A', 'A', 'A', 'C', 'B', 'B', 'B']
[0, 1, 4, 10, 2, 3, 5, 6, 7, 8, 11, 9, 12, 13]