Содержание

ГЕНЕРАЦИЯ СЛУЧАЙНЫХ ЧИСЕЛ И МОДЕЛИРОВАНИЕ

Последовательности случайных чисел используются в программировании в самых разнообразных случаях, начиная с моделирования (это наиболее частое применение) и кончая играми и другим развлекательным программным обеспечением. Турбо Паскаль содержит встроенную функцию, называемую Random, которая генерирует случайные числа. Как вы увидите в данной главе, Random - это превосходный генератор случайных чисел, но для некоторых применений вам может потребоваться два или более различных генераторов для обеспечения различных наборов случайных чисел для различных задач. Кроме того, при моделировании требуется ассиметричный или дисбалансный генератор случайных чисел, который порождает последовательность, смещенную к одному из концов. Первая часть данной главы посвящена построению генераторов случайных чисел и оценке их качества.
Во второй части данной главы показывается, как вы можете использовать случайные числа при моделировании реальных ситуаций на двух примерах. В примерах иллюстрируются фундаментальные основы программ моделирования.

ГЕНЕРАТОРЫ СЛУЧАЙНЫХ ЧИСЕЛ

Технически термин «генератор случайных чисел» - это абсурд; числа само по себе не являются случайными. Например, 100 - это случайное число? А 25? Что в действительности означает этот термин, так это то, что создается последовательность чисел, появляющихся случайным образом. Это порождает более сложный вопрос: что такое последовательность случайных чисел? Единственно правильный ответ: последовательность случайных чисел _ это последовательность, в которой все элементы являются несвязанными. Это определение приводит к такому парадоксу, что любая последовательность может быть как случайной, так и неслучайной в зависимости от того, как эта последовательность получена. Например, следующая строка чисел

1 2 3 4 5 6 7 8 9 0

была получена печатанием верхней строки клавиатуры по порядку, таким образом последовательность конечно не может рассматриваться как сгенерированная случайным образом. Но как быть, если вы получите ту же самую последовательность, вынимая пронумерованный теннисные шары из боченка. В данном случае это уже случайным образом сгенерированная последовательность. Данный пример показывает, что случайность последовательности зависит от того, как она была получена, а не от нее самой. Помните, что последовательность чисел, сгенерированная компьютером, является детерминированной: каждое число, кроме первого, зависит от предшествующих чисел. Технически это означает, что компьютером может быть сгенерирована только квазислучайная последовательность чисел. Однако, этого достаточно для большинства задач и в данной книге такие последовательности для простоты будут называться случайными.
В общем случае считается хорошо, когда числа в последовательности случайных чисел распределены равномерно (не путайте это с нормальным распределением или колоколообразной кривой). При равномерном распределении все события равновероятны так, что диаграмма равномерного распределения стремится к прямой горизонтальной линии, а не к кривой.
До широкого распространения компьютеров всякий раз, когда необходимы были случайные числа, они получались либо бросанием игральных костей, либо выниманием пронумерованных шаров из ящика. В 1955 году фирма RAND опубликовала таблицу из 1 миллиона случайных чисел, полученных с помощью вычислительной машины. На ранней стадии развития вычислительной техники было разработано много методов генерации случайных чисел, но большинство из них не нашло применения.
Один очень интересный метод был разработан Джоном фон Нейманом; его часто называют среднеквадратичным. В данном методе предыдущее случайное число возводится в квадрат, а затем из результата выделяются средние цифры. Например, если вы создаете числа из трех цифр, а предыдущее число было 121, то возведение в квадрат дает результат 14641. Выделение трех средних цифр дает следующее случайное число 464. Недостатком данного метода является то, что он имеет очень короткий период повторения, называемый циклом. По данной причине данный метод сегодня не используется.
В настоящий момент наиболее часто применяется метод генерации случайных чисел, основывающийся на использовании уравнения

R = (aR +c) mod m
n+1 n

при выполнении следующих условий

R>0 a>0 c>0 m>R , a и c

Отметим, что R - это предыдущее число, а R - следующее. Данный метод иногда называют линейным сравнительным методом. Формула так проста, что вы можете подумать, что генерировать случайные числа просто. Однако, это ловушка: насколько хорошо работает данная формула, очень сильно зависит от значения а, с и m. Выбор значений иногда в большей степени искусство, нежели наука. Существуют сложные правила, которые могут помочь вам выбрать значения; однако, мы рассмотрим лишь несколько простых правил и примеров.
Модуль (m) должен быть довольно большим, так как он определяет область случайных чисел. Операция взятия по модулю порождает остаток от деления числа на модуль. Следовательно, 10 по модулю 4 равно 2. Таким образом, если модуль равен 12, то формулой порождаются числа от 0 до 11, а если модуль равен 21425, то порождаются числа от 0 до 21424. Выбор множителя а и приращения с является очень сложной задачей. В общем случае множитель может быть довольно большим, а приращение - маленьким. Множество попыток и проверок необходимо, чтобы создать хороший генератор.
В качестве первого примера здесь приведен один из наиболее часто используемых генераторов случайных чисел. Уравнение, показанное в Rаn1 используется как основа для генератора случайных чисел в ряде популярных языков.

Var 
  a1: integer; { установка до первого вызова Ran1  }
 
Function Ran1: real;
 
Var 
  t: real;
Begin
  t := (a1*32749+3) Mod 32749;
  a1 := Trunc(t);
  Ran1 := Abs(t/32749);
End;

Данная функция имеет три главные особенности. Во-первых, случайные числа в действительности являются целыми, хотя функция возвращает действительные числа. Данный метод работает с целыми числами, но генераторы случайных чисел, как это принято, должны возвращать числа в пределах от 0 до 1, что означает, что это должны быть числа с плавающей запятой.
Во-вторых, начальное значение задается через глобальную переменную а1. До первого вызова Ran1 переменная а1 должна быть установлена в 1.
В-третьих, в Ran1 случайные числа делятся на модуль прежде, чем они будут возвращены функцией, для того, чтобы числа лежали в области от 0 до 1. Если вы поинтересуетесь значением глобальной переменной а1 до возврата строки, то оно должно лежать в области от 0 до 32748. Следовательно, когда а1 делится на 32749, полученное число будет больше или равно 0 и меньше 1.
Многие генераторы случайных чисел не применимы, так как они порождают не равномерное распределение или имеют короткий цикл повторения. Даже когда эти недостатки не очень бросаются в глаза, они могут породить смешанный результат, если такой генератор используется снова и снова. Решение заключается в том, чтобы создать различные генераторы и применять их индивидуально или совместно для получения более качественных последовательностей случайных чисел. Применение нескольких генераторов может сгладить распределение последовательности за счет уменьшения малых смешений отдельных генераторов. Далее приведена функция генерирования случайных чисел, называемая Ran2, которая порождает хорошее распределение:

Var 
  a2: integer; { установить в значение 203 до первого вызова }
 
Function Ran2: real;
 
Var 
  t: real;
Begin
  t := (a2*10001+3) Mod 17417;
  a2 := Trunc(t);
  Ran2 := Abc(t/17417);
End;

Оба этих генератора случайных чисел порождают хорошие последовательности случайных чисел. Тем не менее остаются вопросы: достаточно ли «случайной» является последовательность? Хороши ли данные генераторы?

Определение качества генераторов

Вы можете применить различные тексты для определения случайности последовательности чисел. Ни один из тестов не скажет, что последовательность является случайной, однако, он скажет, если она не является таковой. Тесты могут выявить не случайные последовательности, но, если тест не нашел недостатков, это не означает, что данная последовательность действительно случайная. Тесты, однако, повышают уверенность в генераторе случайных чисел, который породил последовательность. Теперь мы кратко рассмотрим несколько простых способов тестирования последовательностей. Для начала рассмотрим способ определения того, насколько близко распределение чисел в последовательности соответствует ожидаемому. Например, вы пытаетесь генерировать последовательность случайных чисел от 0 до 9. Вероятность появления каждой цифры равна 1/10. Пусть была сгенерирована следующая последовательность

9 1 8 2 4 6 3 7 5 2 9 0 4 2 4 7 8 6 2

Если вы подсчитаете число появлений каждой цифры, то получите результат Цифры Число появлений

0 1
1 1
2 4
3 1
4 3
5 1
6 2
7 2
8 3
9 2

Далее вам следует ответить самому себе на вопрос, достаточно ли похоже данное распределение на ожидаемое вами.
Помните: если генератор случайных чисел хороший, он генерирует последовательности случайно. В истинно случайном варианте все последовательности возможны. Действительно, как какая-то последовательность может быть не случайной, если любая последовательность возможна? Просто некоторые последовательности менее похожи на то, какой должна быть случайная последовательность, чем другие. Вы можете определить вероятность того, что данная последовательность является случайной, используя критерий хи-квадрат.
В критерии хи-квадрат ожидаемое количество вычитается из Работа с Турбо Паскалем #2/2 = 45 = наблюдаемого количества встреч числа в сгенерированной последовательности. Этот результат называется V. Вы можете использовать V для нахождения процента в таблице значений хи-квадрат. Этот процент определяет вероятность того, что была порождена случайная последовательность. Малая таблица хи-квадрат приведена на рис.7-1; вы можете найти полные таблицы в большинстве книг по статистике

n
n
n
n
n p=99%
5 0.5543 10 2.558
15 5.229 20 8.260 30 14.95 p=95%
1.1455
3.940
7.261
10.85
18.49 p=75%
2.675
6.737
11.04
15.45
24.48 p=50%
4.351
9.342
14.34
19.34
29.34 p=25%
6.626
12.55
18.25
23.83
34.80 p=5%
11.07
18.31
25.00
31.41
43.77

Рис.7-1. Выбранные значения хи-квадрат.

Для определения вероятности того, что последовательность не случайная, найдите строку в таблице, показанной на рис.7-1, с числом элементов последовательности; в данном случае это 20. Затем ищите число по строке, которое больше V. В данном случае это колонка 1. Это означает, что существует вероятность 99% того, что пример из 20 элементов будет иметь V больше 8,260. С другой стороны это означает, что существует вероятность только в 1% того, что проверяемая последовательность была сгенерирована случайным образом. Чтобы пройти через критерий хи-квадрат, вероятность V должна снизиться до уровня от 25% до 75%.
Однако, вы можете противопоставить этому выводу вопрос: Так как все последовательности возможны, как может данная последовательность иметь только однопроцентный шанс быть законной? Ответ такой: это всего лишь вероятность. Фактически, если вы применяете критерий хи-квадрат, вам следует получить несколько различных последовательностей и усредненный результат, чтобы избежать отвержения хорошего генератора случайных чисел. Любая единичная последовательность может быть отвергнута, но ряд различных последовательностей после усреднения должен давать хороший результат.
С другой стороны, последовательность может пройти через критерий хи-квадрат и не быть случайной. Например, последовательность

1 3 5 7 9 1 3 5 7 9

пройдет критерий хи-квадрат, но она выглядит не очень случайной. В данном случае сгенерирован пробег по диапазону значений. Пробег - это просто возрастающая или убывающая последовательность чисел с произвольным интервалом. В данном случае каждая группа из пяти цифр представляет собой возрастающую последовательность и как таковая не может считаться случайной последовательностью. Можно создать тест для обнаружения такой ситуации, но это выходит за рамки данной книги.
Другой характеристикой, подлежащей оценке, является длина периода: то есть, как много чисел может быть сгенерировано до начала повторения последовательности. Все машинные генераторы случайных чисел всегда генерировали повторяющуюся последовательность. Чем длинее период, тем лучше генератор. Даже если частота чисел внутри периода распределена равномерно, числа не образуют случайную серию, так как действительно случайная серия не может достаточно повторяться. В общем случае период в несколько тысяч чисел удовлетворяет большинству применений. Тест для выяснения периода может быть разработан.
Различные другие тесты могут быть применены для определения качества генератора случайных чисел. Наверное можно написать больше программ для проверки генераторов случайных чисел, чем создать самих генераторов. Рассмотрим еще один тест, который позволит вам проверить генератор случайных чисел «визуально», используя диаграмму для демонстрации характеристик сгенерированной последовательности.
В идеале диаграмма должна основываться на частоте каждого числа. Однако, так как генератор может порождать тысячи различных чисел, это не выполнимо. Вместо этого будут создаваться диаграммы, сгруппированные до десяти цифрам. Например, так как порождаемые числа лежат в области от 0 до 1, число 0.9365783 будет включено в группу 9, а число 0.34523445 будет включено в группу 3. Это означает, что диаграмма вывода случайных чисел имеет 10 линий, каждая из которых представляет число попаданий в группу. Программа также выводит среднее значение последовательности, которое может быть использовано для обнаружения смешения. Как и все другие программы данной главы следующая программа выполняется только на персональном компьютере IBM PC, который имеет адаптер цветного графического дисплея. Разработанные ранее функции Ran1 и Ran2, а также встроенная функция Турбо Паскаля Random, продемонстрированы рядом для сравнения. программа, которая сравнивает три генератора случайных чисел

Program RanGenerator;
uses
Graph;
 
Const 
  COUNT = 1000;
 
Var 
  freg1, freg2, freg3: array[0..9] Of integer;
  a2, a1: integer;
  f, f2, f3: real;
  r, r2, r3: real;
  y, x: integer;
  GraphDriver, GraphMode: integer;
{ отображение графического вывода }
 
Procedure Display;
 
Var 
  t : integer;
Begin
  For t := 0 To 9 Do
    Begin
      Line(t*10, 180, t*10, 180-freg1[t]);
      Line(t*10+110, 180, t*10+110, 180-freg2[t]);
      Line(t*10+220, 180, t*10+220, 180-freg3[t]);
    End;
End;
 
Function Ran1: real;
 
Var 
  t: real;
Begin
  t := (a1*32749+3) Mod 32749;
  a1 := Trunc(t);
  Ran1 := Abs(t/32749);
End;
 
Function Ran2: real;
 
Var 
  t: real;
Begin
  t := (a2*10001+3) Mod 17417;
  a2 := Trunc(t);
  Ran2 := Abs(t/17417);
End;
Begin
{ переключение на графический режим, используя режим 4 CGA/EGA  }
  GraphDriver := CGA;
  GraphMode := CGAC1;
  InitGraph(GraphDriver, GraphMode, '');
  SetColor(2);
  SetLineStyle(SolidLn, 0, NormWidth);
  OutTextXy(80, 10, 'Comparison of Random');
  OutTextXy(96, 20, 'Number Generators');
{ прорисовать базовые линии  }
  Line(0, 180, 90, 180);
  Line(110, 180, 200, 180);
  Line(220, 180, 310, 180);
  OutTextXy(30, 190, 'Random Ran1 Ran2');
{ инициализация переменных генераторов случайных чисел }
  a1 := 1;
  a2 := 203;
  f := 0;
  f2 := 0;
  f3 := 0;
  For x := 0 To 9 Do
    Begin { инициализация матриц частот  }
      freg1[x] := 0;
      freg2[x] := 0;
      freg3[x] := 0;
    End;
  For x := 1 To COUNT Do
    Begin
      r := Random; { взять случайное число  }
      f := f+r; { прибавить для вычисления среднего }
      y := Trunc(r*10); { преобразовать в целое число от 0 до 9  }
      freg1[y] := freg1[y]+1; { увеличить счетчик частоты  }
      r2 := Ran1; { взять случайное число  }
      f2 := f2+r2; { прибавить для вычисления среднего  }
      y := Trunc(r2*10); { преобразовать в целое число от 0 до 9 }
      freg2[y] := freg2[y]+1; { увеличить счетчик частоты  }
      r3 := Ran2; { взять случайное число  }
      f3 := f3+r3; { прибавить для вычисления среднего  }
      y := Trunc(r3*10); { преобразовать в целое число от 0 до 9 }
      freg3[y] := freg3[y]+1;{ увеличить счетчик частоты  }
      Display; { отобразить счетчики частот }
    End;
  ReadLn;
  RestoreCrtMode;
  WriteLn('mean of Random is: ', f/COUNT);
  WriteLn('mean of Ran1 is: ', f2/COUNT);
  WriteLn('mean of Ran2 is: ', f3/COUNT);
End.

В данной программе каждая функция генерирует 1000 чисел и на основе этого создаются таблицы частот. Процедура Display рисует все три матрицы частот на экране после генерации каждого случайного числа так, что вы можете наблюдать рост частот. На рис.7-2 показан вывод каждого генератора случайных чисел после генерации 1000 чисел. Средние значения равны 0,489932 у Ran1, 0,4858311 y Ran2 и 0,494014 у Random. Это приемлемо.

  1. ———————————————————–

Сравнение генераторов случайных чисел

| | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
------------------- ------------------- -------------------

Random Ran1 Ran2 Рис.7-2. Вывод из программы отображения работы генераторов случайных чисел. Для эффективного использования программы вы должны наблюдать как за формой диаграммы, так и за динамикой роста, чтобы выявить короткие повторяющиеся циклы. Например, Ran2 генерирует значительно меньше чисел в области от 0,9 до 0,999999, чем Random и Ran1. Конечно данный текст не является всеобъемлющим, но он поможет вам понять способ, которым генератор порождает числа, и ускорит процесс анализа генераторов.

Использование нескольких генераторов

Один простой метод, который улучшает свойства случайных последовательностей, порождаемых тремя генераторами, заключается в комбинировании их под управлением одной главной функции. Данная функция выбирает между двумя из них, основываясь на результате третьей. С помощью этого метода вы можете получить очень длинный период и уменьшить влияние циклов и смещений. Функция, называемая CombRandom, показанная здесь, осуществляет комбинирование выводов генераторов Ran1, Ran2, Random: данная функция использует три генератора для возврата одного случайного числа

Function CombRandom: real;
 
Var 
  f: real;
Begin
  f := Ran2;
  If f>0.5 thenCombRandom := Random
     Else CombRandom := Ran1; { случайный выбор генератора }
End;

Результат Ran2 используется для того, чтобы решить, Ran1 или Random выдаст значение главной функции CombRandom. При таком методе период главной функции равен или больше суммы периодов Random и Ran1. Таким образом, данный метод делает возможным порождение последовательности с очень длинным периодом. Можно легко изменять смесь Random и Ran1 изменением константы в операторе if, чтобы получить желаемое вами распределение между этими двумя генераторами. Кроме того, вы можете добавить дополнительные генераторы и осуществлять выбор между ними для получения еще более длинного периода. Далее следует программа для отображения диаграммы CompRandom и ее среднего значения. На рис.7-3 показана финальная диаграмма после генерации 1000 чисел. Среднее значение CombRandom равно 0,493361. данная программа демонстрирует комбинированный вывод трех генераторов случайных чисел

Program MultiRandom;
uses
Graph;
 
Const 
  COUNT = 1000;
 
Var 
  freg: array[0..9] Of integer;
  a2,a1: integer;
  f, r: real;
  y, x: integer;
  GraphDriver, GraphMode: integer;
 
{ отображение графического
представления работы генераторов  }
 
Procedure Display;
 
Var 
  t: integer;
Begin
  For t := 0 To 9 Do
    Line(t*10+110, 180, t*10+110, 180-freg[t]);
End;
 
Function Ran1: real;
 
Var 
  t: real;
Begin
  t := (a1*32749+3) Mod 32749;
  a1 := Trunc(t);
  Ran1 := Abs(t/32749);
End;
 
Function Ran2: real;
 
Var 
  t: real;
Begin
  t := (a2*10001+3) Mod 17417;
  a2 := Trunc(t);
  Ran2 := Abs(t/17417);
End;
{ данная функция использует три генератора
для возврата одного случайного числа }
 
Function CombRandom: real;
 
Var 
  t: real;
Begin
  f := Ran2;
  If f>0.5 Then CombRandom := Random
  Else CombRandom := Ran1; { случайный выбор генератора  }
End;
Begin
{ переключение на графический режим, используя режим 4 CGA/EGA  }
  GraphDriver := CGA;
  GraphMode := CGAC1;
  InitGraph(GraphDriver, GraphMode, '');
  SetColor(2);
  SetLineStyle(SolidLn, 0, NormWidth);
  OutTextXy(48, 10, 'вывод, полученный комбинированием ');
  OutTextXy(40,20, 'три генератора случайных чисел ');
  Line(110, 180, 200, 180);
  a1 := 1;
  a2 := 203; { инициализация переменных для генераторов случайных чисел  }
  f := 0;
  For x:=0 To 9 Do
    freg[x] := 0; { инициализация матрицы
частот }
  For x := 1 To COUNT Do
    Begin
      r := CombRandom; { взять случайное число  }
      f := f+1; { прибавить для вычисления среднего  }
      y := Trunc(r*10); { преобразовать в целое число от 0 до 9 }
      freg[y] := freg[y]+1; { увеличить счетчик частоты  }
      Display;
    End;
  ReadLn;
  RestoreCrtMode;
  WriteLn('Среднее случайное число равно : ', f/COUNT);
End.
  1. —————————————————-

Вывод, полученный комбинированием трех генераторов случайных чисел.

   | |
   | | | |
   ||||||||||
   ||||||||||
   ||||||||||
   ||||||||||
   ||||||||||
   ---------- 

Рис.7-3. Финальное отображение функции CombRandom:

МОДЕЛИРОВАНИЕ

Оставшаяся часть данной главы посвящена применениям генераторов случайных чисел для моделирования на компьютерах. Промоделировать можно все; успех моделирования зависит главным образом от того, насколько хорошо программист понял ситуацию, которую надо моделировать. Так как реальные ситуации часто имеют тысячи переменных, многие вещи с трудом поддаются моделированию. Однако, существуют ситуации, которые очень хорошо подходят для моделирования. Моделирование важно по двум причинам. Во-первых, оно позволяет вам изменять параметры моделирования для проверки и наблюдения возможных результатов в то время, как в реальности такие эксперименты могут быть и дорогими и опасными. Например, моделирование атомной электростанции может быть использовано для проверки влияния различных типов отказов без какой-либо опасности. Во-вторых, моделирование позволяет нам создавать ситуации, которые не могут произойти в реальной жизни. Например, психологи могут захотеть изучить влияние непрерывного роста интеллекта мыши до человеческого, чтобы определить, когда мышь будет проходить лабиринт наиболее быстро. Хотя это не может быть сделано в реальной жизни, моделирование может помочь проникновению в природу соотношения интеллекта и инстинкта. Далее разберем первый из двух примеров, в которых используются генераторы случайных чисел..

Моделирование очередей обслуживания

В первом примере моделируется обслуживание в бакалейной лавке. Предположим, что лавка открыта 10 часов в день с пиковыми часами с 12 до 13 и с 17 до 18 часов. Период с 12 до 13 часов имеет нагрузку в два раза, а с 17 до 18 - в три раза больше обычной. При моделировании один генератор «порождает» покупателей, второй генератор определяет время обслуживания покупателя, а третий решает, в какую очередь пойдет покупатель. Цель моделирования состоит в том, чтобы помочь управляющему найти оптимальное число очередей, которые должны работать в обычный день при условии, что число людей в очереди в любое время не превышало бы 10 и кассиры не ожидали бы покупателей.
Ключ к данному типу моделирования состоит в создании многих процессов. Хотя Турбо Паскаль непосредственно не поддерживает параллельности, вы можете моделировать с помощью множества процессов или с помощью главной программы с циклами. Далее показана программа с ее глобальными данными для моделирования очередей без поддержки процедур и функций:

Var 
  gueues, count: array[0..9] Of integer;
  open: array[0..9] Of boolean;
  cust, time: integer;
  a1, a2: integer;
  y, x: integer;
  change: boolean;
  GraphDriver, GraphMode: integer;
Begin
{ переключение на графику, используя режим 4 CGA/EGA }
  GraphDriver := CGA;
  GraphMode := CGAC1;
  InitGraph(GraphDriver, GraphMode, '');
  SetColor(2);
  SetLineStyle(SolidLn, 0, NormWidth);
  a1 := 1;
  a2 := 203; { инициализация переменных генератора
случайных чисел }
  change := FALSE;
  cust := 0;
  time := 0;
  For x:=0 To 9 Do
    Begin
      gueues[x] := 0; { инициализация очереди  }
      open[x] := FALSE; { нет покупателей или очередей в
начале дня  }
      count[x] := 0; { счетчик очереди  }
    End;
  OutTextXy(155, 190, '1 10');
  OutTextXy(8,190,'Check-out lines: ');
{ теперь начинается день открытием первой очереди  }
  open[0] := TRUE;
  Repeat
    AddCust;
    AddQueue;
    Display;
    CheckOut;
    Display;
    If (time>30) And (time<50) Then AddCust;
    If (time>70) And (nime<80) Then
      Begin
        AddCust;
        AddCust;
      End;
    time := time+1;
  Until time>100; { конец дня  }
  ReadLn;
  RestoreCrtMode;
End.
 
{ Элемент Graph.P включен, чтобы программа могла использовать графические функции.
Главный цикл управляет всем моделированием: }
Repeat
  AddCust;
  AddQueue;
  Display;
  CheckOut;
  Display;
  If (time>30) And (time<50) Then AddCust;
  If (time>70) And (time<80) Then
    Begin
      AddCust;
      AddCust;
    End;
  time := time+1;
Until time>100; { конец дня  }

Функция AddCust использует либо Ran1, либо Ran2 для генерации числа покупателей, встающих в очереди при каждом запросе.
Функция AddQuece используется для помещения покупателей в очереди в соответствии с результатом Ran2, а также открывает новые очереди, если все существующие переполнены. Функция Display отображает программу моделирования. Checkout использует Ran2 для назначения каждого покупателя в очередь с соответствующим увеличением счетчика очереди; каждый вызов уменьшает счетчик на 1. Когда счетчик покупателей равен 0, очередь становится пустой.
Переменная time (время) изменяет интенсивность, с которой генерируются покупатели, для того, чтобы отследить часовые пики. Каждый проход по циклу представляет одну десятую часа. На рис.7-4, 7-5 и 7-6 показаны состояния очередей, когда time=28, time=60 и time=88, что соответствует нормальному времени, концу первого пика и концу второго пика, соответственно. Отметим, что в конце второго пика требуется максимум пять очередей, Если моделирующая программа написана правильно, то в бакалейной лавке оставшиеся пять очередей не нужны.

queue 1: 10 time: 28
queue 2: 8
queue 3: 0
queue 4: 0
queue 5: 0
queue 6: 0
queue 7: 0
queue 8: 0
queue 9: 0
queue 10: 0
Очередь |
|
| |
| |
| |
| |
| |
| |
1 10

Рис.7-4. Состояние очередей, когда time=28:

queue 1: 6 time: 60
queue 2: 8
queue 3: 8
queue 4: 1
queue 5: 0
queue 6: 0
queue 7: 0
queue 8: 0
queue 9: 0
queue 10: 0
Очередь | |
| |
| | |
| | |
| | |
| | | |
1 10

Рис.7-5. Состояние очередей, когда time=60:

queue 1: 8 time: 80
queue 2: 9
queue 3: 6
queue 4: 6
queue 5: 7
queue 6: 0
queue 7: 0
queue 8: 0
queue 9: 0
queue 10: 0
Очередь |
| |
| | |
| | | | |
| | | | |
| | | | |
| | | | |
1 10

Рис.7-6. Состояние очередей, когда time=88: Вы можете непосредственно управлять различными переменными в программе. Во-первых, вы можете изменить путь и число прибывающих покупателей. Вы также можете изменить функцию AddCost, чтобы она возвращала число покупателей в пиковые часы с большим или меньшим постепенным увеличением или уменьшением. Программа предполагает, что покупатели случайным образом выбирают, в какую очередь им встать. Такой подход справедлив для одних покупателей, а другие будут выбирать самую короткую очередь. Вы можете учесть это, изменив функцию AddQueue так, чтобы она в некоторых случаях помещала покупателей в самую короткую очередь, а в некоторых - случайным образом. При моделировании не учитываются такие случайности, как упавшая булка или буйный покупатель в очереди, которые вызывают временные остановки очереди. Целиком программа выглядит следующим образом:

Program simulation; { моделирование очередей в бакалейной лавке  }
uses
Graph;
 
Var 
  gueues, count: array[0..9] Of integer;
  open: array[0..9] Of boolean;
  cust, time: integer;
  a1, a2: integer;
  y,x: integer;
  change: boolean;
  GraphDriver, GraphMode: integer;
 
Function Ran1: real;
 
Var 
  t: real;
Begin
  t := (a1*32749+3) Mod 32749;
  a1 := Trunc(t);
  Ran1 := Abs(t/32749);
End;
 
Function Ran2: real;
 
Var 
  t: real;
Begin
  t := (a2*10001+3) Mod 17417;
  a2 := Trunc(t);
  Ran2 := Abs(t/17417);
End;
 
Function CombRandom: real;
{ random selection of generators 2 }
 
Var 
  f: real;
Begin
  f := Ran2;
  If f>0.5 Then CombRandom := Random
  Else CombRandom := Ran1;{ случайный выбор генераторов }
End;
{ добавление покупателей в очередь  }
 
Procedure AddCust;
 
Var 
  f, r: real;
Begin
  If change Then f := Random { переключение между двумя  }
  Else f := Ran2; { генераторами  }
  If f>0.5 Then
    If f>0.6 Then cust := cust+1 { добавить одного покупателя }
  Else If f>0.7 Then cust := cust+2 { два покупателя }
  Else If f<0.8 Then cust := cust+3 { три покупателя  }
  Else cust := cust+4; { четыре покупателя  }
End;
 
{ обслуживание покупателя  }
 
Procedure CheckOut;
 
Var 
  t: integer;
Begin
  For t := 0 To 9 Do
    Begin
      If gueues[t]<>0 Then
        Begin
{ получить время обслуживания  }
          while count[t] = 0 Do count[t] := Trunc(Ran1+5);
{ новый покупатель требует времени обслуживания }
          count[t] := count[t]-1;
          If count[t]=0 Then gueues[t] := gueues[t]-1;
{ удаление покупателя }
        End;
      If gueues[t]=0 Then open[t] := FALSE; { закрытие очереди }
    End;
End;
{ возвращается TRUE, если очередь переполнена  }
 
Function AllFull: Boolean;
 
Var 
  t: integer;
Begin
  AllFull := TRUE;
  For t := 0 To 9 Do
    If (gueues[t]<10) And open[t] Then AllFull := FALSE;
End;
 
{ данная процедура вводит новые очереди  }
 
Procedure AddQueue;
 
Var 
  t, line: integer;
  done: Boolean;
Begin
  done := FALSE;
  while cust<>0 Do
  Begin
    If AllFull Then
      Begin
        t := 0;
        Repeat
          If Not open[t] Then
            Begin
              open[t] := TRUE;
              done := TRUE;
            End;
          t := T+1;
        Until done Or (t=9);
      End
    Else
      Begin
        Line := Trunc(Ran2*10);
        If open[line] And (gueues[line]<10) Then
          Begin
            gueues[line] := gueues[line]+1;
            cust := cust-1;
          End;
      End;
    If AllFull And open[9] Then cust := 0; { all full }
  End;
End;
 
{ очистить символы длины, начиная с позиции Х, У  }
 
Procedure ClrVid(x,y,len: integer);
 
Var 
  i: integer;
  s: string[80];
Begin
  For i := 1 To len Do
    s := concat(Chr(219), Chr(219));
  SetColor(0);
  OutTextXy(x, y, s);
  SetColor(2);
End;
 
{ отображение экрана результатов моделирования очереди  }
 
Procedure Display;
 
Var 
  t: integer;
  value: string[80];
Begin
  clrVid(170, 10, 3);
  str(time, value);
  OutTextXy(120, 10, 'Time: ');
  OutTextXy(170, 10, value);
  For t := 0 To 9 Do
    Begin
{ erase old line  }
      SetColor(0);
      Line((t*10)+160, 180, (t*10)+160, 180);
{ нарисовать новое состояние моделирования  }
      SetColor(2);
      Circle((t*10)+160, 180, 3);
      Line((t*10)+160, 180, (t*10)+160, 180-gueues[t]*10);
{ дать также текстовый вывод  }
      OutTextXy(8, t*10, 'gueue');
      str(t+1, value);
      value := concat(value, ':');
      OutTextXy(56, t*10, value);
      ClrVid(80, t*10, 3);
      str(gueues[t], value);
      OutTextXy(80, t*10, value);
    End;
End;
 
Begin
{ переключение на графику, используя режим 4 CGA/EGA  }
  GraphDriver := CGA;
  GraphMode := CGAC1;
  InitGraph(GraphDriver, GraphMode, '');
  SetColor(2);
  SetLineStyle(SolidLn, 0, NormWidth);
  a1 := 1;
  a2 := 203; { инициализация переменных генератора случайных чисел  }
  change := FALSE;
  cust := 0;
  time := 0;
  For x := 0 To 9 Do
    Begin
      gueues[x] := 0; { инициализировать очереди  }
      open[x] := FALSE;{ нет покупателей или очередей в начале дня }
      count[x] := 0; { счетчик очереди  }
    End;
  OutTextXy(155, 190, '1 10');
  OutTextXy(8, 190, 'Check-out lines; ');
{ теперь начинается день
открытием первого пункта обслуживания }
 
  open[0] := TRUE;
  Repeat
    AddCust;
    AddQueue;
    Display;
    CheckOut;
    Display;
    If (time>30) And (time<50) Then AddCust;
    If (time>70) And (time<80) Then
      Begin
        AddCust;
        AddCust;
      End;
    time := time+1;
  Until time>100;{  конец дня  }
  ReadLn;
  RestoreCrtMode;
End.

Управление портфелем акций методом случайного поиска

Искусство управления портфелем акций в общем случае основывается на различных теориях и предположениях о многих факторах, некоторые из которых не могут быть легко поняты. Существуют стратегии купли и продажи, основанные на статистическом анализе стоимости акций, индексе РЕ, цены на золото и даже лунного цикла.
Вы можете подумать, что фондовая биржа слишком сложна для моделирования; она имеет слишком много переменных и слишком много неизвестных; ей присущи широкие колебания по времени и плавные изменения в зависимости от других параметров. Однако, задача сама по себе имеет решение: так как рынок такой сложный, его можно рассматривать как совокупность случайно происходящих событий. Это означает, что вы можете моделировать поведение фондовой биржы, как серию не связанных событий, носящих случайный характер. Такой метод моделирования называется методом случайного поиска управления портфелем акций. Вы можете руководствоваться методом случайного поиска, так как он не хуже других.
Прежде, чем продолжать, предупредим: метод случайного поиска дискредитирован профессиональными бизнесменами. Он представляется здесь исключительно в познавательных целях, а не для того, чтобы помочь вам принять решение о ваших вложениях.
Для реализации метода случайного поиска, во-первых, выберите десять компаний из журнала «Уол Стрит Джоурнел» некоторым случайным методом. После того, как вы выберите десять компаний, введите их имена в программу моделирования случайного поиска так, чтобы она могла сообщить вам, как поступить с ее акциями. Программа может сообщить вам пять вариантов поведения по отношению к акциям данной компании:

Далее показана программа случайного поиска. Встроенная функция KeyPressed проверяет статус клавиатуры и ожидает нажатия клавиши. Это позволяет вам использовать последовательность, порождаемую генератором случайных чисел в случайный момент, в сущности порождая случайно выбранное значение, что предотвращает выдачу программой одинаковых рекомендаций программа управления портфелем акций методом случайного поиска

Program RandomWalk;
uses
Crt;
 
Type 
  str80 = string[80];
  action = (buy, sell, hold, short, margin);
 
Var 
  t: integer;
  stock: array[1..10] Of str80;
  ch: char;
  act: action;
  f: real;
 
{ ввод имен компаний  }
 
Procedure Enter;
 
Var 
  t: integer;
Begin
  For t := 1 To 10 Do
    Begin
      Write('Введите имена компаний : ');
      ReadLn(stock[t]);
    End;
End;
 
{ возврат очередного курса акций  }
 
Function NextAction: action;
 
Var 
  f: real;
Begin
  NextAction := hold;
  Case Trunc(Random*10) Of
    1: NextAction := sell;
    2: NextAction := buy;
    3: NextAction := short;
    4: NextAction := margin
  End;
End;
 
Begin
  Write('Подождать, а затем нажать любую клавишу ');
  Repeat
    f := Random; { Randomize the generator }
  Until KeyPressed;
  ch := ReadKey;
  WriteLn;
  enter;
  Repeat
    For t := 1 To 10 Do
      Begin
        act := NextAction;
        If Length(stock[t])>0 Then
          Begin
            Write(stock[t], ': ');
            Case act Of
              buy: WriteLn('Кyпить');
              sell: WriteLn('Продать');
              short: Write('Продать не на долго');
              margin: WriteLn('Купить на прибыль ');
              hold: WriteLn('Держать ');
            End;
          End;
      End;
    Write('Снова (Y/N) ');
    ch := ReadKey;
    WriteLn;
  Until UpCase(ch)='N';
End.

Программа требует, чтобы вы интерпретировали ее инструкции следующим образом:

Инструкция Интерпретация
Купить Купить столько указанных акций, сколько вы можете себе позволить без занимания
Продать Продать все акции, если они у вас есть.
Далее случайным образом выбрать новую компанию для вложения ваших денег
Продать не на долго Продать 100 акций указанной компании, даже если их у вас нет, в надежде, что вы в будущем сможете купить их по низкой цене
Купить на прибыль Занять деньги для покупки акций указанной компании
Держать Не делать ничего

Например, вы запустили данную программу, используя фиктивные имена компаний Com1-Com10, совет первого дня может выглядеть следующим образом:

Сом1: продать
Сом2: купить
Сом3: купить на прибыль
Сом4: продать не на долго
Сом5: держать
Сом6: держать
Сом7: держать
Сом8: купить
Сом9: держать
Сом10: продать не на долго

Совет на второй день мог бы быть таким:

Сом1: держать
Сом2: держать
Сом3: продать
Сом4: продать не на долго
Сом5: держать
Сом6: держать
Сом7: купить
Сом8: купить на прибыль
Сом9: держать
Сом10: продать

Помните, что, так как программа ждет, когда вы нажмете клавишу, ваш вывод будет отличаться от показанного здесь. Вы можете предпочесть запускать программу каждую неделю или месяц, а не каждый день.
Программу можно свободно менять любыми путями. Например, вы могли бы захотеть иметь программу, которая выдает количество акций для купли или продажи в зависимости от имеющихся у вас свободных средств. Снова напомним, что данная программа является только забавой и не рекомендуется для решения по настоящим вкладам. Однако, интересно создать такую программу и проследить за ее эффективностью.