Платежная матрица (выигрыши)
| Альтернатива | П1 | П2 | П3 | П4 | П5 | П6 |
|---|---|---|---|---|---|---|
| A1 | 5 | -3 | 6 | -8 | 7 | 4 |
| A2 | 7 | 5 | 5 | -4 | 8 | 1 |
| A3 | 1 | 3 | -1 | 10 | 0 | 2 |
| A4 | 9 | -9 | 7 | 1 | 3 | -6 |
Минимальные значения по стратегиям
| Альтернатива | Минимальный выигрыш |
|---|---|
| A1 | -8 |
| A2 | -4 |
| A3 | -1 |
| A4 | -9 |
Максимальное из минимальных значений: -1
Рекомендуемая стратегия: A3
Максимальные риски по стратегиям
| Альтернатива | Максимальный риск |
|---|---|
| A1 | 18 |
| A2 | 14 |
| A3 | 8 |
| A4 | 14 |
Минимум из максимальных рисков: 8
Рекомендуемая стратегия: A3
Расчет критерия Гурвица для рисков
| Альтернатива | Мин. риск | Макс. риск | HR |
|---|---|---|---|
| A1 | 0.0 | 18.0 | 7.200 |
| A2 | 0.0 | 14.0 | 5.600 |
| A3 | 0.0 | 8.0 | 3.200 |
| A4 | 0.0 | 14.0 | 5.600 |
Минимальное значение HR: 3.200
Рекомендуемая стратегия: A3
# lab4_decision_analysis.py
# -*- coding: utf-8 -*-
"""
Анализ принятия решений в условиях неопределенности (Вариант 1)
Критерии: Вальда, Сэвиджа, Гурвица
"""
import os
import numpy as np
import datetime
from typing import List, Tuple, Dict, Any
import sys
# Попытка импортировать python-docx для генерации .docx отчёта
try:
from docx import Document
from docx.shared import Inches, Pt
PYDOCX_AVAILABLE = True
except Exception:
PYDOCX_AVAILABLE = False
def read_own_source() -> Tuple[bool, str]:
"""
Пытается прочитать исходный код текущего файла и вернуть (успех, текст).
Возвращает (False, сообщение об ошибке) при неудаче.
"""
try:
current_file = os.path.abspath(__file__)
except Exception:
current_file = os.path.abspath(sys.argv[0]) if sys.argv and sys.argv[0] else None
if not current_file or not os.path.exists(current_file):
return False, "Не удалось определить путь к файлу скрипта"
try:
with open(current_file, "r", encoding="utf-8") as f:
src = f.read()
return True, src
except Exception as e:
return False, f"Ошибка при чтении исходного кода: {e}"
class DecisionAnalysis:
"""Класс для анализа решений в условиях неопределенности"""
def __init__(self, payoff_matrix: List[List[float]], alternatives: List[str],
states: List[str], p_savage: float = 0.2, p_hurwitz: float = 0.4):
self.payoff_matrix = np.array(payoff_matrix)
self.alternatives = alternatives
self.states = states
self.p_savage = p_savage
self.p_hurwitz = p_hurwitz
self.risk_matrix = None
def check_dominance(self) -> Dict[str, Any]:
"""Проверка доминирования стратегий"""
n = len(self.alternatives)
dominated = []
for i in range(n):
for j in range(n):
if i != j:
# Проверяем, доминирует ли стратегия i над j
if all(self.payoff_matrix[i] >= self.payoff_matrix[j]) and any(self.payoff_matrix[i] > self.payoff_matrix[j]):
dominated.append((self.alternatives[j], self.alternatives[i]))
return {
'dominated_pairs': dominated,
'has_dominance': len(dominated) > 0
}
def build_risk_matrix(self) -> np.ndarray:
"""Построение матрицы рисков"""
# β_j = max(a_ij) для каждого столбца j
beta_j = np.max(self.payoff_matrix, axis=0)
# r_ij = β_j - a_ij
self.risk_matrix = beta_j - self.payoff_matrix
return self.risk_matrix
def wald_criterion(self) -> Dict[str, Any]:
"""Критерий Вальда (максиминный)"""
min_values = np.min(self.payoff_matrix, axis=1)
max_min = np.max(min_values)
best_index = np.argmax(min_values)
return {
'min_values': min_values.tolist(),
'max_min': max_min,
'best_alternative': self.alternatives[best_index],
'best_index': best_index
}
def savage_criterion(self) -> Dict[str, Any]:
"""Критерий Сэвиджа (минимаксный риск)"""
if self.risk_matrix is None:
self.build_risk_matrix()
max_risks = np.max(self.risk_matrix, axis=1)
min_max = np.min(max_risks)
best_index = np.argmin(max_risks)
return {
'max_risks': max_risks.tolist(),
'min_max': min_max,
'best_alternative': self.alternatives[best_index],
'best_index': best_index
}
def hurwitz_criterion_risk(self) -> Dict[str, Any]:
"""Критерий Гурвица для матрицы рисков"""
if self.risk_matrix is None:
self.build_risk_matrix()
max_risks = np.max(self.risk_matrix, axis=1)
min_risks = np.min(self.risk_matrix, axis=1)
# HR = p * max(r_ij) + (1-p) * min(r_ij)
hurwitz_values = self.p_hurwitz * max_risks + (1 - self.p_hurwitz) * min_risks
min_hurwitz = np.min(hurwitz_values)
best_index = np.argmin(hurwitz_values)
return {
'hurwitz_values': hurwitz_values.tolist(),
'min_hurwitz': min_hurwitz,
'best_alternative': self.alternatives[best_index],
'best_index': best_index,
'max_risks': max_risks.tolist(),
'min_risks': min_risks.tolist()
}
def analyze_all(self) -> Dict[str, Any]:
"""Полный анализ по всем критериям"""
dominance = self.check_dominance()
risk_matrix = self.build_risk_matrix()
wald = self.wald_criterion()
savage = self.savage_criterion()
hurwitz = self.hurwitz_criterion_risk()
return {
'dominance': dominance,
'risk_matrix': risk_matrix.tolist(),
'wald': wald,
'savage': savage,
'hurwitz': hurwitz,
'payoff_matrix': self.payoff_matrix.tolist()
}
class MarkdownReport:
"""Класс для генерации полного отчета в Markdown для 4-й лабораторной."""
def __init__(self, analysis_results: Dict[str, Any], alternatives: List[str], states: List[str]):
self.results = analysis_results
self.alternatives = alternatives
self.states = states
def add_header(self, title: str, level: int = 1) -> str:
return f"{'#' * level} {title}\n\n"
def add_table(self, data: List[List[Any]], headers: List[str], title: str = "") -> str:
table_str = ""
if title:
table_str += f"**{title}**\n\n"
table_str += "| " + " | ".join(headers) + " |\n"
table_str += "|" + "|".join(["---"] * len(headers)) + "|\n"
for row in data:
table_str += "| " + " | ".join(map(str, row)) + " |\n"
table_str += "\n"
return table_str
def add_code_section(self) -> str:
success, src = read_own_source()
section = self.add_header("Исходный код программы", 2)
if success:
section += "```python\n" + src
if not src.endswith("\n"):
section += "\n"
section += "```\n"
else:
section += f"*Не удалось прочитать исходный код: {src}*\n"
return section
def generate_report(self) -> str:
report = self.add_header("Лабораторная работа 4 — Анализ решений в условиях неопределенности", 1)
# Платежная матрица
report += self.add_header("Исходная платежная матрица", 2)
payoff_data = [[alt] + [val for val in row] for alt, row in zip(self.alternatives, self.results['payoff_matrix'])]
report += self.add_table(payoff_data, ["Альтернатива"] + self.states, "Платежная матрица (выигрыши)")
# Исходные параметры
report += self.add_header("Исходные данные", 2)
report += f"- Коэффициент для критерия Сэвиджа: {self.results['dominance'].get('p_savage', 0.2)}\n"
report += f"- Коэффициент для критерия Гурвица: {self.results['hurwitz'].get('p_hurwitz', 0.4)}\n\n"
# Критерий Вальда
report += self.add_header("1. Критерий Вальда", 2)
wald = self.results['wald']
wald_table = [[alt, val] for alt, val in zip(self.alternatives, wald['min_values'])]
report += self.add_table(wald_table, ["Альтернатива", "Минимальный выигрыш"], "Минимальные значения по стратегиям")
report += f"**Максимальное из минимальных значений:** {wald['max_min']}\n"
report += f"**Рекомендуемая стратегия:** {wald['best_alternative']}\n\n"
# Критерий Сэвиджа
report += self.add_header("2. Критерий Сэвиджа", 2)
savage = self.results['savage']
savage_table = [[alt, val] for alt, val in zip(self.alternatives, savage['max_risks'])]
report += self.add_table(savage_table, ["Альтернатива", "Максимальный риск"], "Максимальные риски по стратегиям")
report += f"**Минимум из максимальных рисков:** {savage['min_max']}\n"
report += f"**Рекомендуемая стратегия:** {savage['best_alternative']}\n\n"
# Критерий Гурвица
report += self.add_header("3. Критерий Гурвица (для матрицы рисков)", 2)
hurwitz = self.results['hurwitz']
hurwitz_table = []
for i, alt in enumerate(self.alternatives):
hurwitz_table.append([
alt,
f"{hurwitz['min_risks'][i]:.1f}",
f"{hurwitz['max_risks'][i]:.1f}",
f"{hurwitz['hurwitz_values'][i]:.3f}"
])
report += self.add_table(hurwitz_table, ["Альтернатива", "Мин. риск", "Макс. риск", "HR"], "Расчет критерия Гурвица для рисков")
report += f"**Минимальное значение HR:** {hurwitz['min_hurwitz']:.3f}\n"
report += f"**Рекомендуемая стратегия:** {hurwitz['best_alternative']}\n\n"
# Итоговые рекомендации
report += self.add_header("Итоговые рекомендации", 2)
report += f"- Критерий Вальда: {wald['best_alternative']}\n"
report += f"- Критерий Сэвиджа: {savage['best_alternative']}\n"
report += f"- Критерий Гурвица: {hurwitz['best_alternative']}\n\n"
# Исходный код
report += self.add_code_section()
return report
class DocxReport:
"""Класс для генерации отчета в формате DOCX"""
def __init__(self, analysis_results: Dict[str, Any], alternatives: List[str], states: List[str]):
self.results = analysis_results
self.alternatives = alternatives
self.states = states
def generate_docx_report(self, filename: str = "decision_report.docx") -> str:
"""
Генерация отчёта в формате DOCX с таблицами и результатами расчётов.
Включает исходный код скрипта как моноширинный текст (если python-docx установлен).
"""
out_path = os.path.join("results", filename)
if not PYDOCX_AVAILABLE:
print("python-docx не установлен — DOCX отчёт не будет создан")
return out_path
try:
doc = Document()
# --- Заголовок лабораторной ---
doc.add_heading('Лабораторная работа №4', level=1)
doc.add_paragraph('Принятие решений в условиях неопределенности. Игры с природой')
doc.add_paragraph('Вариант 1')
doc.add_paragraph('Студент: Меркулов Алексей Дмитриевич (ФИО)')
doc.add_paragraph('') # пустая строка
# Основная часть отчёта
doc.add_heading('Отчёт: Анализ решений в условиях неопределенности (Вариант 1)', level=1)
doc.add_paragraph(f"Дата генерации: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# Исходные данные
doc.add_heading('Исходные данные', level=2)
p = doc.add_paragraph()
p.add_run('Коэффициент пессимизма для критерия Сэвиджа: ').bold = True
p.add_run('0.2\n')
p.add_run('Коэффициент для критерия Гурвица (матрица рисков): ').bold = True
p.add_run('0.4\n')
# Платежная матрица
doc.add_heading('Платежная матрица', level=2)
table = doc.add_table(rows=len(self.alternatives) + 1, cols=len(self.states) + 1)
# Заголовки
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Альтернатива'
for j, state in enumerate(self.states, 1):
hdr_cells[j].text = state
# Данные
for i, alt in enumerate(self.alternatives, 1):
row_cells = table.rows[i].cells
row_cells[0].text = alt
for j in range(len(self.states)):
row_cells[j + 1].text = str(self.results['payoff_matrix'][i-1][j])
# Результаты по критериям
doc.add_heading('Результаты расчётов', level=2)
# Критерий Вальда
doc.add_heading('Критерий Вальда', level=3)
wald = self.results['wald']
for i, alt in enumerate(self.alternatives):
doc.add_paragraph(f"Минимальный выигрыш для {alt}: {wald['min_values'][i]}")
doc.add_paragraph(f"Максимальное из минимальных значений: {wald['max_min']}")
doc.add_paragraph(f"Рекомендуемая стратегия: {wald['best_alternative']}")
# Критерий Сэвиджа
doc.add_heading('Критерий Сэвиджа', level=3)
savage = self.results['savage']
for i, alt in enumerate(self.alternatives):
doc.add_paragraph(f"Максимальный риск для {alt}: {savage['max_risks'][i]:.1f}")
doc.add_paragraph(f"Минимальное из максимальных рисков: {savage['min_max']:.1f}")
doc.add_paragraph(f"Рекомендуемая стратегия: {savage['best_alternative']}")
# Критерий Гурвица
doc.add_heading('Критерий Гурвица', level=3)
hurwitz = self.results['hurwitz']
for i, alt in enumerate(self.alternatives):
doc.add_paragraph(f"HR для {alt}: {hurwitz['hurwitz_values'][i]:.3f}")
doc.add_paragraph(f"Минимальное значение HR: {hurwitz['min_hurwitz']:.3f}")
doc.add_paragraph(f"Рекомендуемая стратегия: {hurwitz['best_alternative']}")
# Итоговые рекомендации
doc.add_heading('Итоговые рекомендации', level=2)
doc.add_paragraph(f"Критерий Вальда: {wald['best_alternative']}")
doc.add_paragraph(f"Критерий Сэвиджа: {savage['best_alternative']}")
doc.add_paragraph(f"Критерий Гурвица: {hurwitz['best_alternative']}")
# Вставляем исходный код скрипта в docx
success_src, src_text = read_own_source()
doc.add_heading('Исходный код программы', level=2)
if success_src:
for line in src_text.splitlines():
p = doc.add_paragraph()
run = p.add_run(line)
try:
run.font.name = 'Courier New'
run.font.size = Pt(8)
except Exception:
pass
else:
doc.add_paragraph("Не удалось прочитать исходный код скрипта: " + src_text)
# Сохранение файла
doc.save(out_path)
print(f"DOCX-отчёт сохранён: {out_path}")
except Exception as e:
print(f"Ошибка при генерации DOCX-отчёта: {e}")
raise e
return out_path
def main():
"""Основная функция"""
# Исходная платежная матрица 4x6
payoff_matrix = [
[5, -3, 6, -8, 7, 4],
[7, 5, 5, -4, 8, 1],
[1, 3, -1, 10, 0, 2],
[9, -9, 7, 1, 3, -6]
]
alternatives = ["A1", "A2", "A3", "A4"]
states = ["П1", "П2", "П3", "П4", "П5", "П6"]
# Создаем экземпляр анализа
analyzer = DecisionAnalysis(payoff_matrix, alternatives, states)
# Выполняем полный анализ
results = analyzer.analyze_all()
# Создаем папку для результатов
os.makedirs("results", exist_ok=True)
# Генерируем Markdown отчет
md_reporter = MarkdownReport(results, alternatives, states)
md_report = md_reporter.generate_report()
# Сохраняем Markdown отчет
with open("README.md", "w", encoding="utf-8") as f:
f.write(md_report)
# Генерируем DOCX отчет
docx_reporter = DocxReport(results, alternatives, states)
docx_path = docx_reporter.generate_docx_report()
# Выводим основные результаты в консоль
print("\n=== РЕЗУЛЬТАТЫ АНАЛИЗА ЛАБОРАТОРНОЙ РАБОТЫ 4 ===")
print(f"Критерий Вальда: {results['wald']['best_alternative']}")
print(f"Критерий Сэвиджа: {results['savage']['best_alternative']}")
print(f"Критерий Гурвица: {results['hurwitz']['best_alternative']}")
print(f"\nОтчеты сохранены в папке results:")
print(f"- Markdown отчет: README.md")
if PYDOCX_AVAILABLE:
print(f"- DOCX отчет: {docx_path}")
return results
if __name__ == "__main__":
main()