【问题标题】:Merge N lists by randomly picking elements at each index通过在每个索引处随机选取元素来合并 N 个列表
【发布时间】:2019-04-14 05:18:25
【问题描述】:

我有一个 bajillion 配对列表,每对大小相等。我想通过从每个索引中选择一个随机元素来“合并”每个,但我当前的实现非常慢 - 甚至 在多处理时。 (FWIW,我的代码确实需要线程化)。

def rand_merge(l1, l2):
    newl = []
    for i in range(len(l1)):
        q = random.choice([l1, l2])
        newl.append(q[i])
    return newl

非常基本,但是在 20k 个大小约为 5-25 的列表上运行它需要很长时间 - 我认为它是 random.choice 搞砸了工作。但我也尝试过其他版本的随机数,比如创建一个由 0 和 1 组成的字符串来引用,不行。

编辑: 更清晰:这是一种遗传算法,旨在通过匹配语料库来编写句子。有问题的列表是按单词拆分的句子。 GA 正在将获胜的健身“父母”“合并”成孩子,每个孩子都是两个父母句子“基因”的合并。 这意味着“列表”确实需要匹配,并且不能从更大的列表列表中提取(我不认为)。

这里有一些代码...

from multiprocessing import Pool as ThreadPool
import random

def offspring(parents):
    child = []
    p1 = parents[0].split(' ')
    p2 = parents[1].split(' ')
    for i in range(min(len(p1), len(p2))):
        q = random.choice([p1, p2])
        child.append(q[i])
    child = ' '.join([g for g in child]).strip()
    return child

def nextgen(l): #l is two lists of previous generation and grammar seed
    oldgen = l[0][:pop] # Population's worth of previous generation
    gramsent = l[1] # this is the grammar seed
    newgen = []
    newgen.append(tuple([oldgen[0][0], oldgen[0][0]]))  # Keep the winner!
    for i in range(len(oldgen) - len(oldgen)//4):
        ind1 = oldgen[0][0] # paired off against the winner - for larger pools, this is a random.sample/"tournament"
        ind2 = oldgen[i][0]
        newgen.append(tuple([ind1, ind2]))
    pool = ThreadPool(processes=8)
    newgen = pool.map(offspring, newgen)
    pool.close()
    pool.join()

人口和世代可以一起进入高数,并且每个句子都贯穿。自从最初发布这个问题以来,我担心每一代人都需要很长时间才能过去,我发现(对我来说很头疼)长时间的处理时间实际上(几乎)与“人口”大小或数量无关的列表。每一代变异大约需要 15 秒。我将人口从 50 增加到 50000,世代从 15 秒增加到 17 秒左右。所以缓慢显然隐藏在其他地方。

【问题讨论】:

  • 您说“非常慢 - 即使在多处理时”您在多处理/多线程方面到底做了什么?您是否尝试过异步/多线程的组合?您对 multiXYZ 不满意的代码示例将非常有帮助,因此其他人不会提供您已经尝试过的示例。
  • 你能举个小例子吗?
  • 我发现速度变慢了 - 不是功能;就是每次我调用 pool.join() 时,main 之外的所有东西都会被调用,并且有很多文件加载设置。不幸的是,我无法在 main 内部进行设置,因此我必须删除多处理模块(据我所知)。

标签: python python-2.7 list random


【解决方案1】:

尝试一次合并所有 20,000 个列表,而不是一次合并两个。

from itertools import zip_longest
from functools import partial
import random

lists = [l1, l2, ...]

idxvals = map(partial(filter, None), itertools.zip_longest(*lists))
newl = [random.choice([*i]) for i in idxvals]

由于您想在每个索引处选择一个随机元素,因此一次从所有 20k 个列表中选择而不是一次选择 2 个是有意义的。


>>> lists = [[1, 2, 3], [10], [20, 30, 40, 5]]

zip_longest 将压缩到最长的列表,用None 填充缺失值。

>>> list(itertools.zip_longest(*lists))
[(1, 10, 20), (2, None, 30), (3, None, 40), (None, None, 5)]

在选择步骤之前需要过滤掉这些无。 filter 将对此有所帮助。

>>> f = partial(filter, None)
>>> list(map(list, map(f, itertools.zip_longest(*lists))))
[[1, 10, 20], [2, 30], [3, 40], [5]]

我想做什么应该很清楚。对于lists 中的每个l,输出的第i 个索引包含l[i] 中存在的那些元素。

现在,遍历 idxvals 并选择:

>>> idxvals = map(f, itertools.zip_longest(*lists))
>>> [random.choice([*i]) for i in idxvals]
[10, 30, 3, 5]

【讨论】:

  • 我应该更明确一点,列表确实需要“配对”——它们是共享底层语法的句子。他们可以配对的原因是因为他们将共享一个结构,例如“名词”、“动词”、“名词”——然后他们交换名词和动词,通过一个评分标准,等等。然而,这一切都在通过一个庞大的语料库进行迭代,所以它必须很快。然而,我想我发现我的速度来自另一个问题。
  • @Xodarap777 叹息...,我明白了。
  • 我确实找到了一种适应这种情况的方法(如果我先设置等效长度的列表,它就可以工作)。
猜你喜欢
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
相关资源
最近更新 更多