最近在看代码库rlkit时,发现一句有意思的代码和注释(如下所示),大意是从列表中随机选择一个元素时使用np.random.randint比np.random.choice更加高效,相关的解释是np.random.choice会进行一些不必要的复制操作,使得效率相较于randint低一些。

possible_future_obs_idxs = self._idx_to_future_obs_idx[i]

# This is generally faster than random.choice.

# Makes you wonder what random.choice is doing

num_options = len(possible_future_obs_idxs)

next_obs_i = int(np.random.randint(0, num_options))

future_obs_idxs.append(possible_future_obs_idxs[next_obs_i])

我做了相关的实验进行对比,发现使用np.random.random的实现比np.random.randint更快,并且是否使用参数size=1对结果的影响也很大,文末进行了总结。

1. random, randint, choice

生成长度为1e6的随机array,从中随机选择1个数和1000个数,比较两种情况下运行速度,测试代码如下:

import numpy as np

import time

N = 1000000

array = np.random.random(N)

def random(N=N): # 使用random随机选择1个数

return array[int(np.random.random() * N)]

def random_size1(N=N):# 使用random(size=1)随机选择1个数

return array[int(np.random.random(size=1) * N)]

def random_size_n(n, N=N): # 使用random随机选择n个数

return array[(np.random.random(n) * N).astype(np.int)]

def randint(N=N):# 使用randint随机选择1个数

return array[np.random.randint(N)]

def randint_size1(N=N): # 使用randint(size=1)随机选择1个数

return array[np.random.randint(N, size=1)]

def randint_size_n(n, N=N): # 使用randint随机选择n个数

return array[np.random.randint(N,size=n)]

def choice(array=array): # 使用choice随机选择1个数

return np.random.choice(array)

def choice_size1(array=array): # 使用choice(size=1)随机选择1个数

return np.random.choice(array, size=1)

def choice_size_n(n, array=array): # 使用choice随机选择n个数

return np.random.choice(array, size=n)

test_funs = [random, random_size1, randint, randint_size1, choice, choice_size1]

test_randn_funs = [random_size_n, randint_size_n, choice_size_n]

def test_main(mode, times=1000000):

test_fun = test_funs[mode]

start = time.time()

for _ in range(times):

test_fun()

end = time.time()

print('test {} {} times using time {} s'.format(test_fun, times, end - start))

def test_randn_main(mode,n=1000, times=1000000):

test_fun = test_randn_funs[mode]

start = time.time()

for _ in range(times):

test_fun(n)

end = time.time()

print('test {} {} times using time {} s'.format(test_fun, times, end - start))

if __name__ == "__main__":

for i in range(6):

test_main(i)

for i in range(3):

test_randn_main(i)

进行1e6次的实验结果(单位:s):

更直观的使用ipython的%timeit的结果:

import numpy as np

N = 1000

%timeit int(np.random.random() * N)

446 ns ± 3.21 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.random.randint(N)

908 ns ± 4.11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.random.randint(N, size=1)

1.29 µs ± 5.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

以上实验结果在不同环境中跑会有些许误差,但是应该能得到以下结论:随机选择一个数时,random最快,速度是randint的2倍;

随机选择一个数时,不指定size=1的参数更快,以random为例,速度差异能到5倍左右;

随机选择n个数时,指定size=n,randint最快。

以下是我的猜测和解释,如有问题欢迎指出:choice会有内存申请和复制array的操作,通常是最慢的;

指定size会有内存申请的操作,并且会转化为ndarray类型,因此产生一个随机数时不指定size=1更快;

random的输出是python的float类型,指定size后是ndarray类型,乘以N时,float类型和N都直接进行python的float类型运算,而ndarray乘以N多进行了数据转化和传递操作(转化为numpy的类型并传入底层进行运算,可以参考这个问题https://www.zhihu.com/question/24789359/answer/55643155),randint内部实现其实是一样的,但是乘以N的操作本身是在numpy的类型中进行的,减少了两次数据转化和传递,速度更快。

>>> x=np.random.random()

>>> type(x)

>>> y=np.random.random(size=1)

>>> type(y)

为了让大家体会到数据格式转化的耗时,补充一个实验,注意上面是np.random.random()乘以的是python的常数,均为python内置数据类型的运算,而如果乘以的是numpy数据类型,结果可能不一样:

import numpy as np

N = 1000

L = np.random.randint(1, N, size=N)

%timeit [np.random.randint(x) for x in L]

1.12 ms ± 87.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit [int(np.random.random() * x) for x in L]

2.68 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

## 可以看到这里使用np.random.random 反而比randint慢了,这是因为x其实是numpy.int64的格式

# np.random.random()是python数据类型,又涉及到numpy的数据格式转换和数据传递。

>>>type(L[0])

numpy.int64

>>>type(np.random.random())

float

# 而当我们将x转换为int型后,再次得到了我们之前的结论

%timeit [int(np.random.random() * int(x)) for x in L]

638 µs ± 12.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

因为涉及到数据格式转化和向底层代码的数据传递,简单的python内置运算可能比numpy更快,当运算较多时,numpy并行的优势才能显示出来。

2. 案例优化

我们以rlkit的这段代码为例进行优化,该段代码的目的是从不等长的列表组里对每个列表随机选择1个数据。这个案例特殊的地方在于列表组里有N个不等长的子列表,还要考虑循环处理子列表的时间,使用numpy并行进行向量对应位相乘速度更快,选择random_idx的过程能够加速80倍左右。

import numpy as np

N = 1000

L = np.random.randint(1, N, size=N) # 子列表的长度

%timeit random_idx = (np.random.random(N) * L).astype(np.int)

13 µs ± 419 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit random_idx = [np.random.randint(x) for x in array_len]

1e+03 µs ± 22.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3. Take Awaynumpy的运算会涉及到数据格式转化和向底层代码的数据传递,当运算比较少时python内置运算可能比numpy更快,所以要注意运算量的类型,numpy的优势在于大规模的并行计算;

随机选择一个数时,尽量避免设置size=1,不设置size的运行速度从快到慢为:random > randint > choice;

随机选择n个数时,由于randint(size=n)内置了random(size=n) * N的操作,比外部实现的random(size=n) * N少了两次数据转化而更快,速度从快到慢为:randint > random > choice。

python从数组中随机选择一些元素_numpy.random随机选择数组元素如何更高效相关推荐

  1. 34. 在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标

    34. 在排序数组中查找元素的第一个和最后一个位置 难度中等2012 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target.请你找出给定目标值在数组中的开始位置和结束位置. 如果 ...

  2. python使用numpy中的np.linalg.det函数计算2D numpy数组的行列式的值、使用numpy中的np.linalg.inv函数计算2D numpy数组的逆矩阵

    python使用numpy中的np.linalg.det函数计算2D numpy数组的行列式的值(determinant).使用numpy中的np.linalg.inv函数计算2D numpy数组的逆 ...

  3. 未排序数组中累加和为给定值的最长子数组系列问题

    题目:给定一个无序数组arr,其中元素可正.可负.可 0,给定一个整数k.求arr所有子数组中累加和为k的最长子数组长度. 补充问题: 1.给定一个无序数组arr,其中元素可正.可负.可 0.求arr ...

  4. 左神算法:未排序正数数组中累加和为给定值的最长子数组长度(Java版)

    本题来自左神<程序员代码面试指南>"未排序正数数组中累加和为给定值的最长子数组长度"题目. 题目 牛客OJ:未排序数组中累加和为给定值的最长子数组长度 题解 本文提供的 ...

  5. 牛客题霸 [ 未排序数组中累加和为给定值的最长子数组长度] C++题解/答案

    牛客题霸 [ 未排序数组中累加和为给定值的最长子数组长度] C++题解/答案 题目描述 给定一个无序数组arr, 其中元素可正.可负.可0.给定一个整数k,求arr所有子数组中累加和为k的最长子数组长 ...

  6. 算法013:二维数组中的查找-在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断中是否存在

    题目:在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.示例: 现有 ...

  7. c语言二维数组中的周边,【C语言】二维数组中的查找,杨氏矩阵

    //二维数组中的查找,杨氏矩阵 //在一个二维数组中,每行都依照从左到右的递增的顺序排序.每列都依照从上到下递增的顺序排序. //请完毕一个函数.输入这种一个数组和一个数,推断数组中是否包括这个数. ...

  8. 每天一道LeetCode-----摩尔投票法寻找给定数组中出现个数大于n/2或n/3的元素

    Majority Element 原题链接Majority Element 给定一个数组,元素个数为n,找出出现次数大于n/2的那个元素 摩尔投票法思想 每次从数组中选择两个不相等的元素进行相互抵消( ...

  9. python中打乱顺序的函数_numpy.random.shuffle打乱顺序函数的实现

    python有打乱列表内元素排列顺序的函数吗 import random random.shuffle(你的列表) 举个例子: L1 = [1, 3, 5, 7] random.shuffle(L1) ...

最新文章

  1. 正则表达式学习实例1
  2. leetcode算法题--石子游戏 II★★
  3. ASP.NET Eval如何进行数据绑定
  4. MyGeneration学习笔记(5) :在Web Service中使用dOOdad(中)
  5. GPUImage – 亮度平均 GPUImageLuminosity
  6. 【C语言及程序设计】项目1-24-4:个人所得税计算器if语句版
  7. CTO职场解惑指南系列(一)
  8. 再谈Weiphp公众平台开发——1、成语接龙插件
  9. 计算机网络(三)——TCP/IP协议
  10. java三角形创建子类,A派生出子类B,B派生出子类C,并且在Java源代码中有如下声明: 1.A a0=new 2.A a1 =new 3.A a2=new 问以下哪个说法是正确的?()...
  11. android 相对布局例子代码
  12. Learn OpenGL(三)——顶点着色器(Vertext Shader)
  13. 安装maya过程记录
  14. 《异度神剑2》与柏拉图的精神世界略考(上)
  15. FITS基本文件格式
  16. 如何获取手机的屏幕尺寸
  17. 关于主从同步时应该注意的一些问题;
  18. Android自定义控件之拓展TextView(ExpandTextView)
  19. 【23考研】计算机/软件等专业调剂信息集合!【完结版】
  20. 对main 未定义的引用_错误:ID返回1个退出状态(对“ main”的未定义引用)

热门文章

  1. 偏度和峰度存在的意义
  2. 利用tensorflow神经网络进行泰坦尼克的生存预测
  3. 电子硬件工程师要求?
  4. html中iframe显示多个子页面
  5. javascript es6常用语法
  6. 如何在eclipse中安装Jess
  7. intellij idea 创建web 项目
  8. 2021-2027全球与中国冰球护具市场现状及未来发展趋势
  9. JavaCard开发环境搭建
  10. java写花束_Java作业 题目:16版.情人节送玫瑰花