Книга: Программирование на языке Ruby
18.2.1. Получение истинно случайных чисел из Web
Разделы на этой странице:
18.2.1. Получение истинно случайных чисел из Web
Всякий, кто пытается сгенерировать случайное число, пользуясь детерминированными средствами, безусловно, живет во грехе.
В модуле Kernel
есть функция rand
, которая возвращает случайное число, но вот беда — число-то не является истинно случайным. Если вы математик, криптограф или еще какой-нибудь педант, то назовете эту функцию генератором псевдослучайных чисел, поскольку она пользуется алгебраическими методами для детерминированного порождения последовательности чисел. Стороннему наблюдателю эти числа представляются случайными и даже обладают необходимыми статистическими свойствами, но рано или поздно последовательность начнет повторяться. Мы можем даже намеренно (или случайно) повторить ее, задав ту же самую затравку.
Но природные процессы считаются истинно случайными. Поэтому при розыгрыше призов в лотерее счастливчики определяются лототроном, который хаотично выбрасывает шары. Другие источники случайности — радиоактивный распад или атмосферный шум.
Есть источники случайных чисел и в Web. Один из них — сайт www.random.org, который мы задействуем в следующем примере.
Программа в листинге 18.4 имитирует подбрасывание пяти обычных (шестигранных) костей. Конечно, игровые фанаты могли бы увеличить число граней до 10 или 20, но тогда стало бы сложно рисовать ASCII-картинки.
Листинг 18.4. Случайное бросание костей
require 'net/http'
HOST = "www.random.org"
RAND_URL = "/cgi-bin/randnum?col=5&"
def get_random_numbers(count=1, min=0, max=99)
path = RAND_URL + "num=#{count}&min=#{min}&max=#{max}"
connection = Net::HTTP.new(HOST)
response, data = connection.get(path)
if response.code == "200"
data.split.collect { |num| num.to_i }
else
[]
end
end
DICE_LINES = [
"+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ",
"| | | * | | * | | * * | | * * | | * * | ",
"| * | | | | * | | | | * | | * * | ",
"| | | * | | * | | * * | | * * | | * * | ",
"+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ "
DIE_WIDTH = DICE_LINES[0].length/6
def draw_dice(values)
DICE_LINES.each do | line |
for v in values
print line[(v-1)*DIE_WIDTH, DIE_WIDTH]
print " "
end
puts
end
end
draw_dice(get_random_numbers(5, 1, 6))
Здесь мы воспользовались классом Net::НТТР
для прямого взаимодействия с Web-сервером. Считайте, что эта программа — узкоспециализированный браузер. Мы формируем URL и пытаемся установить соединение; когда оно будет установлено, мы получаем ответ, возможно, содержащий некие данные. Если код ответа показывает, что ошибок не было, то можно разобрать полученные данные. Предполагается, что исключения будут обработаны вызывающей программой.
Посмотрим на вариацию этой идеи. Что если вы захотели бы применить случайные числа в каком-нибудь приложении? Поскольку обслуживающая программа на стороне сервера позволяет указать количество возвращаемых чисел, то было бы логично сохранить их в буфере. Учитывая, что при обращении к удаленному серверу задержки неизбежны, следует сразу заполнить буфер во избежание лишних запросов по сети.
В листинге 18.5 эта мысль реализована. Буфер заполняется отдельным потоком и совместно используется всеми экземплярами класса. Размер буфера и «нижняя отметка» (@slack
) настраиваются; какие значения задать в реальной программе, зависит от величины задержки при обращении к серверу и от того, как часто приложение выбирает случайное число из буфера.
Листинг 18.5. Генератор случайных чисел с буферизацией
require "net/http"
require "thread"
class TrueRandom
def initialize(min=nil,max=nil,buff=nil,slack=nil)
@buffer = []
@site = "www.random.org"
if ! defined? @init_flag
# Принять умолчания, если они не были заданы явно И
# это первый созданный экземпляр класса...
@min = min || 0
@max = max || 1
@bufsize = buff || 1000
@slacksize = slack || 300
@mutex = Mutex.new
@thread = Thread.new { fillbuffer }
@init_flag = TRUE # Значение может быть любым.
else
@min = min || @min
@max = max || @max
@bufsize = buff || @bufsize
@slacksize = slack || @slacksize
end
@url = "/cgi-bin/randnum" +
"?num=#@bufsize&min=#@min&max=#@max&col=1"
end
def fillbuffer
h = Net::HTTP.new(@site, 80)
resp, data = h.get(@url, nil)
@buffer += data.split
end
def rand
num = nil
@mutex.synchronize { num = @buffer.shift }
if @buffer.size < @slacksize
if ! @thread.alive?
@thread = Thread.new { fillbuffer }
end
end
if num == nil
if @thread.alive?
@thread.join
else
@thread = Thread.new { fillbuffer }
@thread.join
end
@mutex.synchronize { num = @buffer.shift }
end
num.to_i
end
end
t = TrueRandom.new(1,6,1000,300)
count = {1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0}
10000.times do |n|
x = t.rand
count[x] += 1
end
p count
# При одном прогоне:
# {4=>1692, 5=>1677, 1=>1678, 6=>1635, 2=>1626, 3=>1692}
- 18.2.1. Получение истинно случайных чисел из Web
- 18.2.2. Запрос к официальному серверу времени
- 18.2.3. Взаимодействие с РОР-сервером
- 18.2.4. Отправка почты по протоколу SMTP
- 18.2.5. Взаимодействие с IMAP-сервером
- 18.2.6. Кодирование и декодирование вложений
- 18.2.7. Пример: шлюз между почтой и конференциями
- 18.2.8. Получение Web-страницы с известным URL
- 18.2.9. Библиотека Open-URI
- Получение статистики
- Тестирование Web-сервиса XML с помощью WebDev.WebServer.exe
- Листинг 15.11. Код для загрузки файла с Web-сервера
- Получение помощи
- Получение помощи по работе с книгой и компакт-диском
- Получение помощи по Windows SharePoint Services 3.0
- 4.3. Логические функции и таблицы истинности
- Получение доменного имени
- Формы Web ASP.NET
- Пример: базовый клиент Web
- Глава 7 Web-сервер
- 4.8. Получение прав root