在数组中重新编号元素的有效方法

时间:2015-04-21 11:20:50

标签: python arrays

我是python的新手,我正在尝试实现遗传算法,但需要一些操作的代码帮助。

我用这种方式制定了问题:

  • 每个人I由一串M整数
  • 表示
  • e中的每个元素I的值均为0到N
  • 0中的每个数字 - N必须至少出现一次I
  • e的值并不重要,只要每个唯一值元素都采用相同的唯一值(将它们视为类标签)
  • e小于或等于N
  • N对于每个I
  • 可以有所不同

应用交叉操作后,我可能会生成违反这些约束中的一个或多个的子项,因此我需要找到一种方法来重新编号元素,以便它们保留其属性,但符合约束条件。

例如:

parent_1 (N=5): [1 3 5 4 2 1|0 0 5 2]
parent_2 (N=3): [2 0 1 3 0 1|0 2 1 3]

*** crossover applied at "|" ***

child_1: [1 3 5 4 2 1 0 2 1 3]
child_2: [2 0 1 3 0 1 0 0 5 2]

child_1显然仍满足所有约束条件,因为N = 5且所有值0-5在数组中至少出现一次。

问题在于孩子2 - 如果我们使用max(child_2)计算N的方式得到值5,但如果我们计算唯一值的数量那么N = 4,这是什么值N应该是。我所要求的(以非常漫长的方式,被授予)是一种良好的,pythonic的方式:

child_2: [2 0 1 3 0 1 0 0 5 2]
*** some python magic ***
child_2':  [2 0 1 3 0 1 0 0 4 2]
*or*
child_2'': [0 1 2 3 1 2 1 1 4 0]

child_2''用于说明值本身并不重要,只要唯一值的每个元素映射到相同的值,就会满足约束。

这是我到目前为止所尝试的内容:

value_map = []
for el in child:
    if el not in value_map:
        value_map.append(el)

for ii in range(0,len(child)):
    child[ii] = value_map.index(child[ii])

这种方法可以工作并返回类似于child_2''的结果,但我无法想象它在字符串上迭代两次的方式非常有效,所以我想知道是否有人对如何有任何建议使它变得更好。

谢谢,对于这么简单的问题这么长的帖子感到抱歉!

5 个答案:

答案 0 :(得分:2)

您需要多次迭代列表,我不认为有任何解决方法。毕竟,在开始更改元素(第二遍)之前,首先必须确定不同元素的数量(第一遍)。但是请注意,由于重复调用indexnot in,在列表中有O(n),因此根据不同元素的数量,最多可能有O(n ^ 2)

或者,您可以为dict使用list代替value_map。字典比列表具有更快的查找速度,因此,复杂性应该确实在O(n)的数量级上。您可以使用(1)字典理解来确定旧值到新值的映射,以及(2)用于创建更新子项的列表理解。

value_map = {el: i for i, el in enumerate(set(child))}
child2 = [value_map[el] for el in child]

使用for循环就地更改孩子。

for i, el in enumerate(child):
    child[i] = value_map[el]

答案 1 :(得分:1)

您可以使用以下单循环执行此操作:

value_map = []
result = []
for el in child:
    if el not in value_map:
        value_map.append(el)
    result.append(value_map.index(el))

答案 2 :(得分:1)

我能想到的一个解决方案是:

  1. 确定N的值并确定未使用的整数。 (这迫使你迭代一次数组)
  2. 浏览数组,每次遇到优于N的数字时,将其映射到未使用的整数。
  3. 这会强制你通过数组两次,但它应该比你的例子更快(这会强迫你在每次迭代时遍历数组的每个元素的value_map

    child = [2, 0, 1, 3, 0, 1, 0, 0, 5, 2]
    
    used = set(child)
    N = len(used) - 1
    unused = set(xrange(N+1)) - used
    
    value_map = dict()
    for i, e in enumerate(child):
        if e <= N:
            continue
        if e not in value_map:
            value_map[e] = unused.pop()
        child[i] = value_map[e]
    print child # [2, 0, 1, 3, 0, 1, 0, 0, 4, 2]
    

答案 3 :(得分:0)

我喜欢@Selçuk吉汗的回答。它也可以在适当的位置完成。

>>> child = [2, 0, 1, 3, 0, 1, 0, 0, 5, 2]
>>>
>>> value_map = []
>>> for i in range(len(child)):
...     el = child[i]
...     if el not in value_map:
...         value_map.append(el)
...     child[i] = value_map.index(el)
...
>>> child
[0, 1, 2, 3, 1, 2, 1, 1, 4, 0]

答案 4 :(得分:0)

我相信这是有效的,虽然我没有测试它超过问题中给出的单个案例。

唯一困扰我的是value_map在代码中出现三次......

def renumber(individual):
    """
    >>> renumber([2, 0, 1, 3, 0, 1, 0, 0, 4, 2])
    [0, 1, 2, 3, 1, 2, 1, 1, 4, 0]
    """
    value_map = {}
    return [value_map.setdefault(e, len(value_map)) for e in individual]