在Python中重新排列列表

时间:2018-03-14 22:55:45

标签: python

假设我们有一个列表cid = [1,1,1,2,2]。目标是循环遍历列表,并且每次用候选池中的每一个替换光标位置处的元素。该池是所有其他元素不等于焦点中的元素加上一个未包含在cid中的元素。例如cid = [1,1,1,2,2]我希望最终得到:

[[2 1 1 2 2]
 [3 1 1 2 2]
 [1 2 1 2 2]
 [1 3 1 2 2]
 [1 1 2 2 2]
 [1 1 3 2 2]
 [1 1 1 1 2]
 [1 1 1 3 2]
 [1 1 1 2 1]
 [1 1 1 2 3]]

我写的是:

arr = np.empty((0, len(cid)), int)
m = max(cid)

# Loop over the list
for i, j in enumerate(cid):
    # make a pool of candidates
    pool = set([x for x in cid if x != j])

    # add a new element
    pool.add(m+1)

    # construct the new list
    for idx in pool:
        a = cid[:i] + [idx] + cid[i+1 :]
        arr = np.append(arr, np.array([a]), axis=0)

print(arr)

请问有更好的方法吗?一个真实案例场景涉及一个长度约为3000的列表,其中包含近50个不同的整数(很可能不是连续的)

2 个答案:

答案 0 :(得分:2)

不是每次都创建一组新的候选者,而是在进入循环之前创建一个主集。每次迭代,只需删除不需要的元素即可获得该索引位置的集合。

另外,我认为你可以清理替换逻辑 little 。我尝试使用一种能保持头部和尾部不变并且遍历所需索引位置的产品,但这不是很易读。我最终决定基本上你自己的处理,但实际上每次更换所需的元素,然后附加一份副本:

cid = [1, 1, 1, 2, 2]
master = set(cid + [max(cid)+1])

solution = []

for idx, val in enumerate(cid):
    print(idx, val)
    cand = master - {val}
    for elem in cand:
        cid[idx] = elem
        solution.append(cid[:])
    print(solution)
    cid[idx] = val

输出(手动插入换行符):

[[2, 1, 1, 2, 2],
 [3, 1, 1, 2, 2],
 [1, 2, 1, 2, 2],
 [1, 3, 1, 2, 2],
 [1, 1, 2, 2, 2],
 [1, 1, 3, 2, 2],
 [1, 1, 1, 1, 2],
 [1, 1, 1, 3, 2],
 [1, 1, 1, 2, 1],
 [1, 1, 1, 2, 3]]

答案 1 :(得分:1)

这将获得与其他答案相同的输出,并且作为OP,但是numpy替换和记忆先前看到的结果的组合可以在大输入上获得更快的结果。当我尝试原始给定的代码时,当给定cidrange(50)时花费约0.25秒,下面的代码中不同输入cid的时序结果如下所示。给定代码比最初给定的代码快得多,但多少取决于给定的cid。输入的分布将很重要,您看到特定输入的次数越多,备忘录将对您有所帮助:)

在代码中已经注意到了,但是当你获得大量输入时,你应该开始分解正在计算的数组并以某种方式将它们写出来。

此外,从粗略的时序分析来看,大多数情况下都会使用array创建np.ones变量。使用numpy的实际替换非常快。 Hooray numpy:)

import numpy as np
cid = [1, 1, 1, 2, 2]
# cid = range(600)       # ~0.23 seconds
# cid = range(100) * 6   # ~0.08 seconds
# cid = range(100) * 30  # ~1.1 seconds
# cid = range(300) * 10  # MemoryError

# Or whatever extra element you want
extra_element = max(cid) + 1
# By putting the set into an array format the pool[pool != j] step below
# becomes very fast
pool = np.array(list(set(cid)) + [extra_element])
# This is the number of available candidates to replace any individual element
num_available = len(pool) - 1
# Memoize results we've seen before
seen_elements = {}

# Create an array to hold the output. It's possible that this can get too
# large to hold in memory all at once, so you might want to consider breaking
# this up (maybe create a new array each for loop and then write it somewhere?)
array = np.ones((len(cid) * num_available, len(cid)), dtype=int) * np.array(cid)

# Replace the operational range each loop with the saved value, or populate
# the saved value as necessary
for i, j in enumerate(cid):
    lower_bound = i * num_available
    upper_bound = i * num_available + num_available
    try:
        array[lower_bound:upper_bound, i] = seen_elements[j]
    except KeyError:
        seen_elements[j] = pool[pool != j]
        array[lower_bound:upper_bound, i] = seen_elements[j]

print array

[[2 1 1 2 2]
 [3 1 1 2 2]
 [1 2 1 2 2]
 [1 3 1 2 2]
 [1 1 2 2 2]
 [1 1 3 2 2]
 [1 1 1 1 2]
 [1 1 1 3 2]
 [1 1 1 2 1]
 [1 1 1 2 3]]