Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用列表理解和/或映射避免嵌套for循环_Python_Python 3.x_List_Numpy_Optimization - Fatal编程技术网

Python 使用列表理解和/或映射避免嵌套for循环

Python 使用列表理解和/或映射避免嵌套for循环,python,python-3.x,list,numpy,optimization,Python,Python 3.x,List,Numpy,Optimization,几天来,我一直在努力研究如何优化(不仅仅是让它看起来更好)3个嵌套循环,其中包含一个条件调用和一个函数调用。我现在的情况如下: def build_prolongation_operator(p,qs): ''' p: dimension of the coarse basis q: dimension of the fine basis The prolongation operator describes the relationship between

几天来,我一直在努力研究如何优化(不仅仅是让它看起来更好)3个嵌套循环,其中包含一个条件调用和一个函数调用。我现在的情况如下:

def build_prolongation_operator(p,qs):
    '''
    p: dimension of the coarse basis
    q: dimension of the fine basis

    The prolongation operator describes the relationship between
    the coarse and fine bases:    
    V_coarse = np.dot(V_fine, I)
    '''

    q = sum(qs)

    I = np.zeros([q, p])

    for i in range(0, q):
        for j in range(0, p):
            for k in range(0, qs[j]):
                # if BV i is a child of j, we set I[i, j] = 1
                if i == f_map(j, k, qs):
                    I[i, j] = 1
                    break

    return I
其中
f_map
为:

def f_map(i, j, q):
    '''
    Mapping which returns the index k of the fine basis vector which
    corresponds to the jth child of the ith coarse basis vector.    
    '''

    if j < 0 or j > q[i]:
        print('ERROR in f_map')
        return None

    result = j

    for k in range(0, i):
        result += q[k]

    return result

我不知道基和延拓算子,但你应该关注算法本身。在优化方面,这几乎总是一个合理的建议

这里可能是问题的症结所在——如果不是的话,那就让你开始吧:
f_图
的计算并不依赖于
i
,但你要为
i
的每个值重新计算它。由于
i
的范围从零到
qs
中的值之和,因此通过缓存结果可以节省大量的重新计算;谷歌“pythonmomeize”,它实际上会自己编写。解决了这个问题,你可能就完成了,没有任何微观优化


您需要足够的空间来存储
max(p)*max(qs[j])
值,但从您报告的呼叫数来看,这应该不是一个太大的障碍

试着检查一下这是否有效

for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
        val = f_map(j,k,qs)
        if I[val, j] == 0:
            I[val, j] = 1

首先,您确实不需要将
p
作为函数的参数:
len(qs)
只需调用一次,而且非常便宜。如果您的输入总是一个numpy数组(在这种情况下,没有理由不这样做),
qs.size
也可以

让我们先重写
f_图
。那里的循环只是
qs
的累积和(但从零开始),您可以预先计算一次(或者每次调用外部函数至少一次)

其中,
cumsum\u q
将在
build\u extendence\u操作符中定义为

cumsum_q = np.roll(np.cumsum(qs), 1)
cumsum_q[0] = 0
我相信您会欣赏在
f\u map
中使用与
build\u extendement\u操作符
中相同的变量名集的有用性。为了更简单,我们可以完全删除
f_map
,并在您的情况下使用它所表示的表达式:

if i == k + cumsum_q[j]:
    I[i, j] = 1
k
上的循环表示“如果
i
k+cumsum[j]
对于任何
k
”,将元素设置为1。如果我们将条件重写为
i-cumsum_q[j]==k
,您可以看到我们根本不需要循环
k
i-cumsum_q[j]
将等于范围
[0,qs[j])
内的一些
k
,如果它是非负的并且严格小于
qs[j]
。您可以检查一下

if i >= cumsum_q[j] and i - cumsum_q[j] < qs[j]:
    I[i, j] = 1
如您所见,最快的选项是完全矢量化的选项,但和选项的差距非常小

注意


我的矢量化解决方案创建了一个布尔数组。如果您不想使用
I.astype(…)
转换为所需的数据类型,或者将其视为
np.uint8
数组:
I.view(dtype=np.uint8)

这里是中建议的优化循环

这里是
f_map
函数,在这里我编辑了参数的名称,以反映调用者使用的名称

def f_map(j,k,qs):
    if k < 0 or k > qs[j]:
        print('ERROR in f_map')
        return None
    result = k
    for i in range(0, j):
        result += qs[i]
    return result
现在,这是
sum
内置的文档字符串

签名:sum(iterable,start=0,/)
文档字符串:
返回一个“开始”值(默认值:0)加上一组数字的总和

当iterable为空时,返回起始值。
此函数专门用于数值,可能会拒绝非数值类型。
类型:内置函数或方法

很明显,我们可以写作

def f_map(j,k,qs):
    return sum(qs[:j], k)
显然,我们也可以不调用函数

for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
            val = sum(qs[:j], k)
            if I[val, j] == 0:
                I[val, j] = 1
调用内置函数应该比函数调用和循环更有效,不是吗


回应疯狂物理学家的评论

我们可以预先计算
qs
的部分和,以获得进一步的加速

sqs = [sum(qs[:i]) for i in range(len(qs))] # there are faster ways...
...
for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
            val = k+sqs[j]
            if I[val, j] == 0:
                I[val, j] = 1

列表理解或
map
仍将执行850万次操作。对于像您这样的非平凡代码,这些构造之间的性能差异可以忽略不计。如果您提供更多上下文(例如
qs
是什么)有可能设计一个更好的算法。考虑一下NUMPY的构建向量化特征,或者可能的Cython进行蛮力优化。你可以用向量乘法替换三个循环。什么是<代码> P<代码> >代码> QS >你期望代码< > <代码>?我想是你的一个循环。s完全不是必需的。外部的是特别的。请提供一个示例输入,以便我们可以使用它。@Mad,这将比我建议的备忘录要好得多:-)@MisterMiyagi谢谢你的时间。为了测试这个,我制作了{qs=np.random.randint(3,size(p))}p只是一个整数。我认为你甚至不需要记忆。你可能只需要完全去掉外循环,并正确地排列其余两个循环。伙计!!这帮了大忙!大大减少了函数调用!谢谢!!我将尝试其他方法,但这是一个很好的解决方案!我刚刚做了一个更有效的是,也要检查它。一旦你设置了1,你就应该中断两个循环,而不是继续检查。@Mad,从他的函数中我可以理解,每当f_map返回一个等于
I
的值时,他就设置了1,而且太贪婪了(即,他第一次发现它就中断)因此,如果f_map返回2,他需要设置
I[2,j]=1
。如果我的逻辑似乎有误,请纠正我。@RaunaqJain这样做。时间上的差异与以前的解决方案相比相对较小,并且更好地消除了1个函数调用,如我的答案所示,您可以通过不多次计算和来进一步优化。看起来您的close parens键卡住了:)我希望你不介意我修正这个以及你的总和限制。这个更新带来了巨大的变化。我添加了一个
In [1]: p = 10
In [2]: q = np.random.randint(3, size=p)

In [3]: ops = (
...     build_prolongation_operator(p, qs),
...     build_prolongation_operator_optimized(qs),
...     build_prolongation_operator_numpy(qs),
...     build_prolongation_operator_RaunaqJain(p, qs),
...     build_prolongation_operator_gboffi(p, qs),
... )

In [4]: np.array([[(op1 == op2).all() for op1 in ops] for op2 in ops])
Out[4]: 
array([[ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True]])

In [5]: %timeit build_prolongation_operator(p, qs)
321 µs ± 890 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [6]: %timeit build_prolongation_operator_optimized(qs)
75.1 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [7]: %timeit build_prolongation_operator_numpy(qs)
24.8 µs ± 77.7 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [8]: %timeit build_prolongation_operator_RaunaqJain(p, qs)
28.5 µs ± 1.55 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [9]: %timeit build_prolongation_operator_gboffi(p, qs)
31.8 µs ± 772 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [10]: %timeit build_prolongation_operator_gboffi2(p, qs)
26.6 µs ± 768 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
            val = f_map(j,k,qs)
            if I[val, j] == 0:
                I[val, j] = 1
def f_map(j,k,qs):
    if k < 0 or k > qs[j]:
        print('ERROR in f_map')
        return None
    result = k
    for i in range(0, j):
        result += qs[i]
    return result
def f_map(j,k,qs):
    result = k
    for i in range(0, j):
        result += q[i]
    return result
def f_map(j,k,qs):
    return sum(qs[:j], k)
for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
            val = sum(qs[:j], k)
            if I[val, j] == 0:
                I[val, j] = 1
sqs = [sum(qs[:i]) for i in range(len(qs))] # there are faster ways...
...
for j in range(0,p):
    for k in range(0, qs[j]):
        # if BV i is a child of j, we set I[i,j] = 1
            val = k+sqs[j]
            if I[val, j] == 0:
                I[val, j] = 1