random.seed(seed)是否在多个进程中生成相同的序列?

时间:2018-04-25 19:04:48

标签: python multithreading multiprocessing

2017年7月19日更新

在此处找到解决方案:https://stackoverflow.com/a/10021912/5729266

如果你不想读到最后,快速得出结论。

我之前代码中随机数的不一致是由线程不安全引起的,因为随机模块被认为是一个全局变量,即使每个线程处理一个包含随机模块的单个实例。

要解决此问题,您必须使用线程锁定或生成独立的随机实例,如上面的链接所述。请参阅下面的测试代码。

import threading
import random

class do_threads:

    def __init__(self):
        # Using random directly is thread-unsafe
        # self.random = random 

        # instead of using random, create a local random instance
        self.random = random.Random()

    def __call__(self, n):
        self.n = n
        self.run_thread()

    def get_balance(self, e):
        self.random.seed(self.n)
        return self.random.uniform(0, 1)


    def run_thread(self):
        total = []
        for i in range(100000):
           total.append(self.get_balance())
        print(sum(total) / 100000)

a = do_threads()
b = do_threads()

t1 = threading.Thread(target=a, args=(5,))
t2 = threading.Thread(target=b, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()

旧帖子:

在我的Python程序中,我需要使用multiprocessing.pool运行N个子进程。每个子进程产生M个线程,每个线程都需要为数据帧的'ID'列中的ID生成哈希码。

哈希码需要遵循 uniform(0,1)的分布。为此,我使用ID作为种子(random.seed(ID))来设置随机状态,然后从random.uniform(0, 1)生成随机密钥。但是,ID有大约0.01%的可能性具有不同的随机数。例如,ID'200300'在所有这些线程/子进程中出现10000次,但是9999次有一个随机密钥,1次有另一个随机密钥。

所以,我的问题是:random.seed(seed)是否始终在并行程序中生成相同的序列?如果没有,我如何修复随机状态以确保random.uniform(0, 1)在给定相同ID的情况下弹出相同的数字?我也对其他方法开放,这些方法可以将ID散列为具有统一(0,1)分布的随机变量。

请注意,我想在我的工作中使用Process和线程,并且无法在程序中连接这些数据帧以一次性生成随机密钥。

我尝试使用multiprocessing.Manager来共享随机状态或在父进程中导入随机状态,或者将random_generator()作为实例或对象从父进程传递到子环境。但似乎事情没有按预期发挥作用。

以下是我的代码的简单版本:

#mythreads.py
from foo import Foo

class TaskWorker(Thread):
        def __init__(self, queue):
            Thread.__init__(self)
            self.queue = queue
        def run(self):
            while True:
                Foo, task = self.queue.get()
                Foo(task).generate_data(df)

def mythreads():
    queue = Queue()
    for x in range(10):
        worker = TaskWorker(queue)
        worker.daemon = True
        worker.start()
    for task in sub_list:
        queue.put((Foo, task))
    queue.join()

# foo.py
import random
class Foo:
    def __init__(self, task):
        ...

    def random_generator(self, e):
        random.seed(e)
        randomkey = random.uniform(0, 1)

    def generate_data(self, df):
        df['RK'] = df[‘ID’].apply(self.random_generator)
        ...

#main.py
from multiprocessing.pool import Pool
from mythreads import mythreads
with Pool(N) as p:
    p.map(mythreads, list_of_sublists)

注意 :我使用的是Python 3.6

2 个答案:

答案 0 :(得分:3)

摘要

问。 random.seed(seed)是否始终在并行程序中生成相同的序列?

A。是。

保证随机数生成器在给定相同起始种子的情况下重现相同系列的随机值。

另一个想法:使用random.random()代替random.uniform(0, 1)。两者都给出相同范围的随机变量,但前者既快又更惯用。

实施例

演示以相同种子开始运行不同生成器的单独进程:

from multiprocessing.pool import Pool
from pprint import pprint
import random

def make_seq(identifier):
    random.seed(8675309)
    seq = [random.random() for i in range(4)]
    return identifier, seq

p = Pool(10)
pprint(list(p.map(make_seq, range(10))), width=100)

输出:

[(0, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (1, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (2, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (3, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (4, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (5, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (6, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (7, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (8, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507]),
 (9, [0.40224696110279223, 0.5102471779215914, 0.6637431122665531, 0.8607166923395507])]

请注意,所有进程都生成相同的值。

答案 1 :(得分:0)

听起来你真正想要的不是随机数,而是ID的散列。查看Hashing strings with Python

使用散列,您可以获得均匀分布且相同的散列键,但相同的ID将始终转换为相同的散列键。哈希键会随机。从散列键中推断出原始ID是很困难的。如果安全性是一个问题(如果它确实真的难以从键中找出ID),请避免使用MD5,否则MD5应该没问题。

>>> import hashlib
>>> print (hashlib.md5('This is a test').hexdigest())
ce114e4501d2f4e2dcea3e17b546f339