random——模块

random——产生伪随机数

模块用途

random 模块是为各种分布实现伪随机数发生器。

几乎模块中所有函数都依赖于基本函数random(),它在半开放范围[0.0,1.0)内产生一个统一的随机浮点数。Python使用Mersenne Twister作为核心生成器。它生成53位精度浮点数,周期为2**19937-1.C中的底层实现即快速又线程安全。

注:Mersenne Twister是存在的最广泛测试的随机数发生器之一。但是,这是完全确定性的,并不适用于所有目的,并且完全不适合加密的目的

警告:该模块的伪随机生成器不应用于安全目的。为了安全或加密使用

_randbelow()的部分源码

BPF = 53        
# 返回一个小于n的随机数(计算随机数的实例)
    def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
                   Method=_MethodType, BuiltinMethod=_BuiltinMethodType):

        "Return a random int in the range [0,n).  Raises ValueError if n==0."

        random = self.random            # 将父类的random方法赋值给random
        getrandbits = self.getrandbits  # 将父类的getrandbits方法赋值给getrandbits

        # 如果原始的random()内置方法没有被覆盖 或者 提供了一个新的getrandbits() 才可以调用self.getrandbits
        if type(random) is BuiltinMethod or type(getrandbits) is Method:
             # 如果 random 是内置函数 或 getrandbits 是 方法

            # k 是 开始位置转换成二进制的长度
            k = n.bit_length()  # don't use (n-1) here because n can be 1
            # 计算 一个随机数
            r = getrandbits(k)          # 0 <= r < 2**k
            while r >= n:
                r = getrandbits(k)
            # 返回一个小于n的随机数
            return r

        # 如果复写了random()方法但是没有复写getrandbits()方法 我们只能用自己的random()方法
        if n >= maxsize:
            # 如果n >= maxsize (maxsize = 9007199254740992) 就调用random()方法
            _warn("Underlying random() generator does not supply \n"
                "enough bits to choose from a population range this large.\n"
                "To remove the range limitation, add a getrandbits() method.")
            return int(random() * n) # random() 方法 是 随机[0,1) 的一个数
        rem = maxsize % n
        limit = (maxsize - rem) / maxsize   # int(limit * maxsize) % n == 0
        r = random()    # r < liimit
        while r >= limit:
            r = random()
        return int(r*maxsize) % n  

随机整数

random.randrange(self, start, stop=None, step=1, _int=int)

  • start: 起始数值
    • stop=None:结束数值 默认为 None
    • step=1步长 默认为1
random.randrange(10)

​ ——产生一个[0,10)的随机整数

import random
print(random.randrange(10))

5

以下为randrange部分源码

 def randrange(self, start, stop=None, step=1, _int=int):
    istart = _int(start)
        if istart != start:
            raise ValueError("non-integer arg 1 for randrange()")
        # 如果stop是空 即 我们只传了一个参数即 random.randrange(10)
        if stop is None:
            if istart > 0:
                # 返回一个小于 start 的 随机数
                return self._randbelow(istart)
            raise ValueError("empty range for randrange()")

random.randrange(10)

如果我们传入的参数是一个大于0的数字,就会通过_randbelow(10)进行运算得到返回值

random.randrange(5,20)

​ ——产生一个[5,20)的随机整数

import random
print(random.randrange(5, 20))

7

以下是randrange部分源码

 def randrange(self, start, stop=None, step=1, _int=int):
        # stop argument supplied.
        istop = _int(stop)
        if istop != stop:
            raise ValueError("non-integer stop for randrange()")
        width = istop - istart # 范围 是一个整数
        if step == 1 and width > 0: # 如果步长等于1 且 范围大于零
            # 返回 一个 start + 小于范围的随机数
            return istart + self._randbelow(width) # _randbelow()方法见上面
        if step == 1:
            # 如果范围 <= 0 则抛出以下异常 具体示例 见 Lrandrange_E1
            raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))

random.randrange(5,20)

如果我们传入的参数为x, y 且(y - x )> 0 就会通过x + _randbelow(y - x) 进行运算得到返回值

Lrandrange_E1

import random
print(random.randrange(1,1,1))

Traceback (most recent call last):
  File "D:/random_demo.py", line 20, in <module>
    print(random.randrange(1,1,1))
  File "D:/random_demo.py", line 198, in randrange
    raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (1,1, 0)
random.randrange(5,-1,-2)

​ ——产生(5,-1,-2)的随机数

import random
print(random.randrange(5,-1,-2))

3
random.randrange(5,20,5)

​ ——产生(5,20,5)的随机数

import random
print(random.randrange(5, 20, 5))


10

以下是randrange部分源码

 def randrange(self, start, stop=None, step=1, _int=int):
    istep = _int(step)
        if istep != step:
            raise ValueError("non-integer step for randrange()")

        # 要么 start < stop and istep > 0
        # 要么 start > stop and istep < 0
        # 其余情况报错
        if istep > 0:
            # width = istop - istart
            n = (width + istep - 1) // istep 
        elif istep < 0:
            n = (width + istep + 1) // istep 
        else:
            raise ValueError("zero step for randrange()")

        if n <= 0:
            raise ValueError("empty range for randrange()")
        # 返回 start + 步长 * 小于n的随机整数
        return istart + istep*self._randbelow(n)

如果我们传入的参数为为x, y, z

  • ( z < 0 )且(y - x )< 0 就会通过x + z * _randbelow((y - x + 1) // z) 进行运算得到返回值
  • ( z > 0 )且(y - x )> 0 就会通过x + z * _randbelow((y - x - 1) // z) 进行运算得到返回值
    • ((y - x + 1) // z) 是计算了 可取的范围(其实就是计算了[x, y)这个范围中有最多可取到多少个z)
    • 根据这个数取一个小于它的随机数然后乘步长 + x

random.randint(a,b)

返回一个随机整数N使得。别名 。a <= N <= b``randrange(a, b+1)

def randint(self, a, b):
    """Return random integer in range [a, b], including both end points.
    """

    return self.randrange(a, b+1)

so~ randint() 其实就是调用了 randrange()方法 也就是一个简化版的randrange()

序列函数

random.choice(self, seq)

​ ——在一个非空的序列中选择一个元素

import random
print(random.choice([1,2,3]))

1

源码如下

def choice(self, seq):
    try:
        # 选择一个随机索引
        i = self._randbelow(len(seq))
    except ValueError:
        # 如果序列为空 则抛出异常
        raise IndexError('Cannot choose from an empty sequence')
    # 通过索引找到随机的元素
    return seq[i]

random.choice([1,2,3])

在这个list列表中 根据列表的长度随机算出一个小于列表长度的数作为索引,返回该索引的值

random.shuffle(self, x, random=None)

将序列x随机混合(将x随机打乱)

import random
l = [12,11,10]
random.shuffle(l)
print(l)


[12, 10, 11]

源码如下

def shuffle(self, x, random=None):
        ''' x 列表(可迭代对象)
            random 是一个 计算随机数的方法
        '''
    # random 为空 即 我们没有手动传入一个计算随机数的方法
    if random is None
        # 赋予self._randbelow方法 一个别名 randbelow
        randbelow = self._randbelow

        # 循环 len 次每次将索引为i的 值 和 索引为 一个小于i的随机整数的值进行替换
        for i in reversed(range(1, len(x))):
            # 通过self._randbelow方法进行计算随机数得到j的值
            j = randbelow(i+1)
            # 交换 索引为i 和索引为j的值
            x[i], x[j] = x[j], x[i]

    # 如果我们手动传入了一个随机数方法
    else:
        _int = int
        # 循环 len 次每次将索引为i的 值 和 索引为 一个小于i的随机整数 的值进行替换
        for i in reversed(range(1, len(x))):
            # 通过我们手动传入的随机数方法的结果 * (i+1) 得到j的值
            j = _int(random() * (i+1))
            # 交换 索引为i 和索引为j的值
            x[i], x[j] = x[j], x[i]

shuffle方法其实是不断进行两两索引位置的值进行交换 达到打乱顺序的

并且它是在原本的列表上进行打乱的,而没有新的列表生成

random.sample(self, population, k)

​ ——返回从总体序列或集合中选择的唯一元素的k长度列表。用于无需更换的随机抽样。

import random
l = [1,2,3,4,5]
l2 = random.sample(l,2)
print(l2)


[4, 3]

源码如下

    def sample(self, population, k):
        # 判断population是否是可使用sample的类型(如果是set转成tuple 如果是tuple、list就继续运行 如果是dict直接报错)
        if isinstance(population, _Set):
            population = tuple(population)
        if not isinstance(population, _Sequence):
            raise TypeError("Population must be a sequence or set.  For dicts, use list(d).")
        # self._randbelow 命名一个别名randbelow
        randbelow = self._randbelow
        # 计算population的长度
        n = len(population)
        # 如果k >=n 或者<=0 就报错, k是选择元素的长度
        if not 0 <= k <= n:
            raise ValueError("Sample larger than population or is negative")
        # 设置默认值
        result = [None] * k
        setsize = 21        # 默认为小集合
        # 如果k大于5则认为 是一个大集合 否则是小集合
        if k > 5:
            setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets

        # 如果是小集合(An n-length list is smaller than a k-length set)
        if n <= setsize:
            # 将population强转成list
            pool = list(population)
            # 这里!!! 其实就是为了每次都可以选出列表中的一个数(选出一个之前没有选择过的数) 然后依次赋给result 这个之前设置的默认值为None 的列表
            for i in range(k):         # invariant:  non-selected at [0,n-i)
                # 随机一个 n-i 的整数当作 下标赋给j
                j = randbelow(n-i)
                # 这下面两句就是在 把选中的数替换成倒置位的数 (因为倒置位的数一直无法取到所以将其替换到前面 )
                result[i] = pool[j]     # 将值赋给  result列表
                pool[j] = pool[n-i-1]   # move non-selected item into vacancy

        # 猜测是因为大集合如果转换成list太过于占空间 所以单独 拿了出来
        else:
            # 创建了一个set 对象 
            selected = set()
            selected_add = selected.add # 给set对象的add方法 赋值一个别名

            for i in range(k):
                j = randbelow(n) # 一个小于n的随机数 并赋给j 当作下标

                # 如果j 不在selected中,即 我们没有选择过 下标为j的数,就跳过while循环
                # 如果在selected中,即 我们曾经选择到 下标为j的数, 则重新取随机数 直到,选择一个从来没有使用过的j
                while j in selected:
                    j = randbelow(n)

                selected_add(j)     # 将j添加到集合中
                result[i] = population[j] # 将值赋给  result列表
        return result

簿记功能

random.seed(self, a=None, version=2)

​ ——seed() 方法改变随机数生成器的种子,可以在调用其他随机模块函数之前调用此函数

改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。

import random
random.seed(0)
print(random.random())
random.seed(0)
print(random.random())
0.8445167133186481
0.8445167133186481

源码如下

def seed(self, a=None, version=2):
    # a -- 改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。
    # 默认调用第二个方式 version = 2 

    # 是方案1 并且 a是str 或 bytes 
    if version == 1 and isinstance(a, (str, bytes)):
        # 如果 a 不为None 则 进行运算 否则x 为0
        x = ord(a[0]) << 7 if a else 0
        for c in a:
            x = ((1000003 * x) ^ ord(c)) & 0xFFFFFFFFFFFFFFFF
        x ^= len(a)
        a = -2 if x == -1 else x
    # 是方案2 并且 a是str 或 bytes 或 bytearray
    if version == 2 and isinstance(a, (str, bytes, bytearray)):
        # 如果a是字符串则 转成bytes类型
        if isinstance(a, str):
            a = a.encode()
        # a 进行加密并进行拼接
        a += _sha512(a).digest()
        a = int.from_bytes(a, 'big')

    # 调用父类的seed(父类中根据当前时间进行生成)
    super().seed(a)
    self.gauss_next = None

seed( ) 用于指定随机数生成时所用算法开始的整数值,如果使用相同的seed( )值,则每次生成的随即数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值