Модуль random из состава python вполне приличная вещь. Если верить встроенной документации, то период генерации ограничивается числом (2**19937)-1. Число, мягко говоря - фантастическое. Это правоверная реализация ГПСПЧ на Си. Надо полагать, что делали не дураки, но как требует чекисткая мудрость: доверяй, но проверяй.
В итоге родился код, суть которого в двух словах: в одном из процессов идёт отрисовка Canvas размером, который задаётся при создании класса clsRndTester(Tk). Второй процесс ставит в очередь массив точек с координатами и цветом полученными из класса Random. Теоретически, генерацией чисел можно нагрузить столько ядер, сколько есть в системе. Пакет multiprocessing позволяет это сделать не напрягаясь. Да только генерация чисел происходит куда быстрее, чем отрисовка точек на экране ;). Код ниже:
# -*- coding: utf8 -*-
'''
Программулина тестирует модуль random.
На экране выводит массив точек для оценки случайности.
Размер экрана - 900*500=480 тыс. - вполне достаточно для
оценки случайности.
'''
from random import Random
from Tkinter import Tk, Label, Frame, Button, Canvas
from multiprocessing import Process, Lock, cpu_count, Queue
from time import sleep
class clsGenerator:
def __init__(self, queue=None, dx=None, dy=None):
self.queue=queue
self.dx=dx
self.dy=dy
self.rnd=Random()
self.proc=Process(target=self.create_rnd)
self.proc.start()
def create_rnd(self):
while True:
if not self.queue.full():
x=self.rnd.randint(0,self.dx)
y=self.rnd.randint(0,self.dy)
color='#'
for i in range(0,3):
c=self.rnd.randint(0,15)
if c<=9:
c=chr(c+ord('0'))
else:
c-=9
c=chr(c+ord('@'))
color+=c
self.queue.put((x,y, color))
else:
sleep(0.005)
class clsRndTester(Tk):
def __init__(self, dx=500, dy=300):
def create_self():
Tk.__init__(self)
self.running=0
# получить число процессоров
self.cpu_num=cpu_count()
# создать очередь сообщений
self.queue=Queue(maxsize=500)
# создать процессы по числу ядер проца
# первое ядро не трогать - под графику
self.list_proc=[]
for i in xrange(0, self.cpu_num-1):
i0=clsGenerator(queue=self.queue, dx=self.dx, dy=self.dy)
self.list_proc.append(i0)
def create_frmUp():
def create_lblProgress():
self.lblProgress=Label(self.frmUp, relief='sunken', text='0', width=10)
self.lblProgress.pack(side='left')
def create_btnStart():
self.btnStart=Button(self.frmUp,
text='Start',
command=self.start_rnd)
self.btnStart.pack()
self.frmUp=Frame(self)
self.frmUp.pack(side='top', fill='x')
create_lblProgress()
create_btnStart()
def create_frmDown():
def create_cnvRnd():
self.cnvRnd=Canvas(self.frmDown,
bg='#FFFFFF',
relief='groove',
bd=3,
height=self.dy,
width=self.dx)
self.cnvRnd.pack()
self.frmDown=Frame(self)
self.frmDown.pack(side='bottom', fill='both', expand=1)
create_cnvRnd()
self.dx=dx
self.dy=dy
create_self()
create_frmUp()
create_frmDown()
def start_rnd(self, event=''):
if self.running==0:
self.btnStart['text']='Stop'
self.running=1
else:
self.btnStart['text']='Start'
self.running=0
if self.running:
for x0 in xrange(self.dx):
if not self.running:
break
for y0 in xrange(self.dy):
if not self.running:
break
crd=self.queue.get()
x,y,fill=crd
self.cnvRnd.create_line(x,y,x+1,y+1, fill=fill)
self.lblProgress['text']=str(int(x0/500.0*100))+'%'
self.update()
if __name__=='__main__':
app=clsRndTester(dx=900, dy=500)
app.mainloop()
Типичная картинка с размером 200 на 300:
Тут стоит упомянуть о том, что шкодю я на нетбуке с 2-мя ядрами 1,3 ГГц и 1 ГБ RAM. Графика, понятное дело - номинальная. Так что, вывод 450 тыс. точек на экран занял у меня почти час. Картинка на разных разрешениях имела явно разные кластеры по цвету и группировке. Но в целом, можно сказать, что диких косяков нет.Технически, можно устроить seed двум разным генераторам от исходного, но как говорит теория - эти два генератора связаны единым алгоритмом, а значит, можно запросто поломать монотонность генератора и получить отрицательный эффект. Тем более что, (2**19937)-1 в десятичной форме - это 6001 цифра для записи полного числа. Хочу напомнить, что "Гугл" - это всего лишь 10**100.
Комментариев нет:
Отправить комментарий