【问题标题】:Time performance of np.random.permutation, np.random.choicenp.random.permutation、np.random.choice 的时间表现
【发布时间】:2016-02-24 01:13:05
【问题描述】:

在我的纯 python 图论库中,我遇到了一个相对于可比较的 MATLAB 代码的时间性能非常差的函数,因此我尝试分析该函数中的一些操作。

我追踪到如下结果

In [27]: timeit.timeit( 'permutation(138)[:4]', setup='from numpy.random import permutation', number=1000000)
Out[27]: 27.659916877746582

将其与 MATLAB 中的性能进行比较

>> tic; for i=1:1000000; randperm(138,4); end; toc
Elapsed time is 4.593305 seconds.

通过将其更改为 np.random.choice 而不是我最初写的 np.random.permutation,我能够显着提高性能。

In [42]: timeit.timeit( 'choice(138, 4)', setup='from numpy.random import choice', number=1000000)
Out[42]: 18.9618501663208

但它仍然没有接近 matlab 的性能。

是否有另一种方法可以在纯 python 中获得这种行为,时间性能接近 MATLAB 时间性能?

【问题讨论】:

  • 别忘了,如果您想要一个无需更换的样品,您需要将replace=False 传递给np.random.choice。 (不是性能改进,但它是正确性的重要修复。)
  • numpy.random.choice 在此类用例中存在长期存在的性能问题,请参阅 github.com/numpy/numpy/issues/2764

标签: python numpy random


【解决方案1】:

基于this solution,它展示了如何使用基于argsort/argpartition 的技巧来模拟np.random.choice(..., replace=False) 的行为,您可以使用np.argpartition 重新创建MATLAB 的randperm(138,4),即NumPy 的np.random.choice(138,4, replace=False)如:

np.random.rand(138).argpartition(range(4))[:4]

或者像这样使用np.argsort -

np.random.rand(138).argsort()[:4]

让我们对这两个版本进行计时,以便与 MATLAB 版本进行性能比较。

在 MATLAB 上 -

>> tic; for i=1:1000000; randperm(138,4); end; toc
Elapsed time is 1.058177 seconds.

在 NumPy 上使用 np.argpartition -

In [361]: timeit.timeit( 'np.random.rand(138).argpartition(range(4))[:4]', setup='import numpy as np', number=1000000)
Out[361]: 9.063489798831142

在 NumPy 上使用 np.argsort -

In [362]: timeit.timeit( 'np.random.rand(138).argsort()[:4]', setup='import numpy as np', number=1000000)
Out[362]: 5.74625801707225

最初建议使用 NumPy -

In [363]: timeit.timeit( 'choice(138, 4)', setup='from numpy.random import choice', number=1000000)
Out[363]: 6.793723535243771

似乎可以使用np.argsort 来稍微提高性能。

【讨论】:

  • 似乎有很大缺陷。我用 5 和 3 而不是 138 和 4 尝试了一百万次。在 60 个可能的结果中,27 个从未发生,3 个发生了大约 100000 次,12 个发生了大约 33333 次,18 个发生了大约16667 次。
  • @StefanPochmann 你能分享你用于调查的代码吗?谢谢!
  • @Divakar collections.Counter(tuple(np.random.rand(5).argpartition(3)[:3]) for _ in range(1000000)).most_common()
  • @StefanPochmann 非常感谢您测试所有这些!因此,事实证明 argpartiton 与 kth 选项作为标量会产生一些偏差。因此,将其替换为一次排序的范围,从而创建一个无偏分布,如其docs 中所述。所以,你担心不是一个统一的分布。应该在编辑中修复。再次感谢!
  • @Divakar 尝试在设置期间将np.arange(4) 存储在变量中,并将其用作kth 参数而不是range(4)。对我来说速度提高了一倍以上。 (不过,我自己的答案对我来说仍然快五倍多:-)
【解决方案2】:

这对您来说需要多长时间?我估计 1-2 秒。

def four():
    k = np.random.randint(138**4)
    a = k % 138
    b = k // 138 % 138
    c = k // 138**2 % 138
    d = k // 138**3 % 138
    return (a, b, c, d) if a != b and a != c and a != d and b != c and b != d and c != d else four()

更新 1: 起初我使用 random.randrange,但 np.random.randint 使整个过程快了两倍。

更新 2:由于 NumPy 的随机函数似乎要快得多,所以我尝试了这个,它是另一个因素,速度快了 ~1.33:

>>> def four():
        a = randint(138)
        b = randint(138)
        c = randint(138)
        d = randint(138)
        return (a, b, c, d) if a != b and a != c and a != d and b != c and b != d and c != d else four()

>>> import timeit
>>> from numpy.random import randint
>>> timeit.timeit(lambda: four(), number=1000000)
2.3742770821572776

这比原来快了大约 22 倍:

>>> timeit.timeit('permutation(138)[:4]', setup='from numpy.random import permutation', number=1000000)
51.80568455893672

(字符串与lambda 没有明显区别)

【讨论】:

  • 这个实现并没有超过 MATLAB 的速度,但在 10% 以内。
  • @aestrivex 试试更新版本,我现在用的是 NumPy 的随机函数,对我来说速度是原来的两倍。
  • 我自己做了一些测试,发现np.random.randint 比random.randrange 函数快。我的分析并没有表明重复调用randint(n) 四次比randint(n**4) 好,实际上后者稍微好一点。
  • @aestrivex 嗯,很有趣。您是否完全按原样比较了我的上述解决方案,或者您是否进行了修改?例如,使用numpy.random.randint 或randint 会有很大的不同。
  • 我进行了修改并尝试了几种不同的实现,但您的代码目前使用np.random.randint(n**4) 列在您的答案顶部,提供了最佳的时间性能,比调用np.random.randint(n) 四次快约75% .
猜你喜欢
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
相关资源
最近更新 更多