根据另一个列表高效地调整Python列表

3

很抱歉我没有想到更好的问题标题。

我有两个等长的列表,例如:

a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]
           i      j                  i'     j'

我希望调整列表a,使得当从索引ij的列表a中存在一个或多个连续的False时,按照以下条件进行调整:

if b[i-1]==b[j+1]:
    a[i:j+1]=[True]*(j-i+1)

在上面的示例中,有两个这样的块:i,j=1,2i',j'=5,6。 结果应该是:
a = [True, True, True, True, True, False, False, True]

我用for循环和if语句写了一个解决方案,但是由于我想在非常大的列表上使用它,所以速度太慢。

a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]

#Edit: the next two lines were originally and wrongly inside the for loop
moving=True
istart=1
for i,trp in enumerate((a)):
    if trp==False:
        if moving==False:
            # if this condition holds, the particle just started a new move
            istart = i
            moving = True
    else:
        if moving==True:
            # is this condition holds, the particle has stopped its move
            moving = False
            if b[i]==b[istart-1]:
                # if this holds, a needs to be adjusted
                a[istart:i]=[True]*(i-istart)

非常感谢您的帮助。 (由于它是用于分析物理模拟,因此注释和变量名称就像那样)

1
你的代码没有表现出“低效”的特点。如果说“非常大”的列表,你是指百万或十亿个项目吗?如果是这样,你最好使用一个库(内置或第三方)能够帮助你在C代码中执行一些操作,这样会更快。itertools 可以起到作用。另外一种方法是将它改写成编译型语言,例如C、C++或Java。纯Python相比这些语言本质上更慢,写一小段代码可能就能解决你的问题。 - dantiston
谢谢评论。我所说的“非常大”指的是10的8次方数量级,但我想它真的不应该这么慢... 我一定会去看看itertools的。 - Johann
1
你是对的,dantiston。这显然是jupyter笔记本的问题。我重新启动了内核,再次运行函数,现在只需要几秒钟。我会保留这个问题,因为Ajax1234已经回答了它。 - Johann
1个回答

1
你可以尝试这个:

import itertools
a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]
new_a = [(a, list(b)) for a, b in itertools.groupby(zip(a, b), key=lambda x:x[0])]
final_list = list(itertools.chain(*[[True]*len(b) if not a and new_a[i-1][-1][-1] == new_a[i+1][-1][-1] and i > 0 else [c for c, d in b] for i, [a, b] in enumerate(new_a)]))

输出:

[True, True, True, True, True, False, False, True]

编辑:测试新输入:
a = [True, False, True]
b = [1, 3, 1]
new_a = [(a, list(b)) for a, b in itertools.groupby(zip(a, b), key=lambda x:x[0])]
final_list = list(itertools.chain(*[[True]*len(b) if not a and new_a[i-1][-1][-1] == new_a[i+1][-1][-1] and i > 0 else [c for c, d in b] for i, [a, b] in enumerate(new_a)]))

输出:

[True, True, True]

代码解释:

itertools.groupby 将连续的 True/False 值形成单个列表。然后,final_list 存储迭代存储在 new_a 中的列表的结果,如果子列表完全由 False 值组成,则创建一个新的子列表 True 值,仅当前后值相同时才这样做。这是通过使用 enumerate 来获取每次迭代的当前索引来确定的。该索引可以用于通过 i-1i+1 访问前面和后面的值。


谢谢回复。我测试了您的代码,但它没有得出正确的结果。似乎缺少了模式“[True,False,True]”,而应该变为“[True,True,True]”。顺便说一下,我在我的代码中发现了一个错误,我已经把它包含在问题里了。 - Johann
1
@Johann 这个回答假设当你提到“有一块False”时,你指的是多个False的情况。在这种情况下,你是指只能有一个False实例吗? - Ajax1234
是的,我猜那个措辞不太好,抱歉。如果符合模式,则应替换单个实例的“False”。 - Johann
@Johann 没问题。请查看我的最新编辑,因为我已经将您的新输入添加到我的新解决方案中。 - Ajax1234
你的版本比我的版本快大约三倍。现在我只需要想办法理解你的代码 :) - Johann
1
@ Johann,抱歉我没有表达清楚。请查看我最近的编辑。 - Ajax1234

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接