Проект для демографического моделирования населения с использованием вероятностных методов.
Программа имитирует изменение населения с течением времени, учитывая:
- Вероятности смерти по возрастам и полу
- Рождаемость (женщины 18-45 лет)
- Начальное распределение населения по возрастам
- Статистику по годам
- Каждый
Personподписан на событиеYearTickдвижка - Движок подписан на событие
ChildBirthкаждого Person - Прямая обработка через
ProcessYear()для производительности
- 1 объект
Person= 1000 человек в реальном населении - Автоматическое масштабирование при инициализации
- Статистика в реальных числах (миллионы людей)
Demographic/ # Решение
├── Demographic/ # Основной проект (.NET Standard)
│ ├── Classes/
│ │ ├── Engine.cs # Движок симуляции
│ │ ├── Person.cs # Класс человека
│ │ └── ProbabilityCalculator.cs # Калькулятор вероятностей
│ ├── Interfaces/
│ │ └── IEngine.cs # Интерфейс движка
│ └── Models/
| ├── Constants.cs # Дата-класс для хранения констант
│ ├── DeathRules.cs # Правила смертности
│ ├── DemographicStats.cs # Статистика за год
│ ├── Gender.cs # Перечисление полов
│ ├── InitialAgeData.cs # Начальное распределение по возрастам
│ └── SimulationResult.cs # Результаты симуляции
├── Demographic.Exec/ # Console App (.NET)
│ ├── Files/
│ │ ├── DeathRules.csv # Правила смертности
│ │ ├── InitialAge.csv # Начальное распределение возрастов
│ │ ├── PeopleData.csv # Выходные данные отдельных людей на последний год моделирования
│ │ └── PopulationData.csv # Общие выходные данные населения за весь период (снимки популяции)
│ └── Program.cs # Точка входа
└── Demographic.FileOperations/ # Class Library (.NET Standard)
├── Classes/
│ ├── CsvDeathRulesReader.cs # Чтение правил смертности
│ ├── CsvInitialAgeReader.cs # Чтение начального распределения
│ └── CsvResultWriter.cs # Запись результатов
└── Interfaces/
├── IDeathRulesReader.cs # Интерфейс чтения правил смертности
├── IInitialAgeReader.cs # Интерфейс чтения возраста
└── IResultWriter.cs # Интерфейс записи результатов
- .NET 6.0 или выше
- CSV файлы с данными в папке
Demographic.Exec/Files/
cd Demographic.Exec
dotnet run [initialAgeFile] [deathRulesFile] [startYear] [endYear] [totalPopulation] [outputPopulationData] [outputPeopledata]- Назначение: Центральный координатор симуляции, управляет жизненным циклом моделирования
- Архитектурная роль: Реализует паттерн "Событийная шина" между компонентами
- Основные методы:
Initialize()- инициализация с автоматическим масштабированием (1 объект = 1000 человек)RunSimulation()- запуск симуляции с гибридной обработкой (прямые вызовы + события)ProcessPeopleYear()- высокопроизводительная прямая обработка людей
- События:
YearTick- уведомление внешних наблюдателей о завершении года
- Зависимости: Работает через интерфейсы, инкапсулирует бизнес-логику симуляции
- Назначение: Представляет группу из 1000 человек с агрегированными демографическими характеристиками
- Архитектурная роль: Активный участник событийной модели, подписчик на
YearTick - Свойства:
Age,Gender,IsAlive,DeathYear- агрегированные характеристики группы
- Методы:
ProcessYear()- основная логика обработки года (высокая производительность)OnYearTick()- обработчик события для формального соответствия требованиям
- События:
ChildBirth- уведомление движка о рождении новой группы людей - Принцип: Соответствует требованию "каждый Person подписан на
YearTick"
- Назначение: Фасад для управления вероятностными правилами смертности
- Методы:
GetDeathProbability()- интеллектуальный поиск вероятности с обработкой граничных случаевAddRule()- добавление правила с валидацией данных
- Структура: Коллекция объектов
DeathRuleс гарантированной целостностью данных
- Назначение: Calculator pattern для преобразования сырых данных в готовое распределение
- Методы:
CalculatePercentages()- нормализация данных в вероятностное распределениеGetPercentageForAge()- быстрый доступ к данным через словарь
- Данные: Read-only словарь
AgePercentagesдля иммутабельного доступа
- Назначение: Потокобезопасный сервис для вероятностных вычислений
- Архитектурная роль: Реализует паттерн "ThreadStatic Random" для многопоточной безопасности
- Методы:
IsEventHappened()- детерминированная проверка событий по вероятности
- Особенности: Гарантирует консистентность при параллельных вызовах
- Назначение: Адаптер для преобразования CSV данных в доменные модели
- Реализует: Интерфейс
IInitialAgeReader(Dependency Inversion) - Методы:
ReadAgeDistribution()- чтение с автоматическим масштабированием и валидацией - Обработка ошибок: Детализированные исключения с контекстом ошибки
- Назначение: Сопоставитель данных для трансформации CSV строк в объекты DeathRule
- Реализует: Интерфейс
IDeathRulesReader - Методы:
ReadDeathRules()- парсинг с культурно-независимым форматированием чисел - Валидация: Проверка целостности возрастных диапазонов
- Назначение: Стратегия для различных форматов вывода результатов
- Реализует: Интерфейс
IResultWriter - Методы:
WritePopulationData()- сериализация агрегированной статистикиWritePeopleData()- потоковая запись больших объемов данных
- Производительность: Пакетная обработка для минимизации IO операций
- Назначение: Класс с правилами смертности
- Свойства:
StartAge,EndAge- иммутабельный возрастной диапазонDeathProbabilityMale,DeathProbabilityFemale- валидируемые вероятности
- Инварианты: Гарантирует корректность данных через конструктор
- Назначение: Класс для статистики года
- Свойства:
Year,TotalPopulation,MalePopulation,FemalePopulation
- Использование: Сериализуемый объект для передачи между слоями
- Назначение: Аргументы для передачи контекста рождения
- Свойства:
ChildGender,BirthYear- вся необходимая информация для создания новогоPerson
- Назначение: Итоговый класс для результатов симуляции
- Свойства:
YearlyStatistics- полная история демографических изменений(Снимки)
initialAgeFile: Files/InitialAge.csvdeathRulesFile: Files/DeathRules.csvstartYear: 1970endYear: 2021totalPopulation: 130,000,000outputFile1: Files/PopulationData.csvoutputFile2: Files/PeopleData.csv
age,count_per_1000
0,15.2
1,14.8
...
start_age,end_age,male_probability,female_probability
0,1,0.0316,0.0266
1,4,0.0021,0.0017
...
year,total_population,male_population,female_population
1970,130000000,65000000,65000000
...
Age,Gender,IsAlive,DeathYear
25,Male,True,
30,Female,False,2005
...
- Проверка смерти - вычисление вероятности смерти по возрасту и полу из
DeathRules - Увеличение возраста - только если человек выжил
- Рождение детей - для женщин 18-45 лет с вероятностью 15.1%
- Определение пола ребенка - 55% девочки, 45% мальчики
Инициализация начального населения
- Создание объектов
Personс масштабированием 1:1000 - Подписка каждого
Personна событиеYearTick - Подписка
Engineна событияChildBirthвсех Person
Для каждого года (1970-2021):
- Обработка всех живых людей через прямой вызов
ProcessYear() - Учет рождений и смертей (автоматически через события
ChildBirthи флаги смерти) - Сохранение статистики в реальных числах (умножение на 1000)
- Вызов события
YearTickдля внешних наблюдателей
Demographic- бизнес-логика и моделиDemographic.Exec- точка входа и конфигурацияDemographic.FileOperations- работа с файлами
- Событийная модель для связи между компонентами
- Инверсия зависимостей через интерфейсы
Для анализа результатов создан Python скрипт с построением графиков:
import pandas as pd
import matplotlib.pyplot as plt
import subprocess
import os
%matplotlib inline
plt.style.use('seaborn-v0_8-whitegrid')
# Настройка путей
exe_path = r"путь\к\Demographic.Exec.exe"
init_age_path = r"путь\к\InitialAge.csv"
death_rules_path = r"путь\к\DeathRules.csv"
# Ввод параметров
start_year = input('Введите год начала моделирования: ')
end_year = input('Введите год окончания моделирования: ')
start_people = input('Введите начальный состав населения: ')
# Запуск C# приложения
process = subprocess.run([
exe_path, init_age_path, death_rules_path,
start_year, end_year, start_people,
'Population.csv', 'People.csv' # Убедитесь, что у вас есть эти файлы, в противном случаем задайте пути к своим
])