import random
import numpy as np
from pprint import pprint
from collections import defaultdict
# Генератор вопросов
def generate_questions(num_questions):
questions = []
for q_id in range(num_questions):
q_type = random.choice(['single', 'multi'])
# Для вариантов ответов
if q_type in ['single', 'multi']:
num_options = random.randint(3, 5)
options = [f"Option_{chr(65+i)}" for i in range(num_options)]
if q_type == 'single':
correct = [random.choice(options)]
else:
correct = random.sample(options, k=random.randint(1, 2))
else:
options = None
correct = round(random.uniform(1, 100), 1) # Число с одним decimal
questions.append({
'id': q_id,
'type': q_type,
'text': f"Question {q_id+1}",
'options': options,
'correct': correct
})
return questions
# Генератор ответов пользователей
def generate_answers(num_users, questions, base_prob=0.3):
user_answers = []
for u_id in range(num_users):
answers = []
total_score = 0
for q in questions:
# Вероятность правильного ответа с нормальным распределением
user_prob = np.clip(np.random.normal(base_prob, 0.1), 0.1, 0.9)
if q['type'] == 'single':
if random.random() < user_prob:
ans = q['correct'][0]
else:
ans = random.choice([o for o in q['options'] if o not in q['correct']])
score = 1 if ans in q['correct'] else 0
elif q['type'] == 'multi':
correct = set(q['correct'])
ans = set()
for o in q['options']:
if o in correct:
if random.random() < user_prob:
ans.add(o)
else:
if random.random() < 0.3: # Вероятность выбрать неверный
ans.add(o)
score = len(ans & correct) / len(correct)
else: # numeric
if random.random() < user_prob:
ans = round(q['correct'] + random.uniform(-2, 2), 1)
else:
ans = round(random.uniform(q['correct']-10, q['correct']+10), 1)
score = 1 - min(abs(ans - q['correct'])/10, 1)
answers.append({'q_id': q['id'], 'answer': ans})
total_score += score
user_answers.append({
'user_id': u_id,
'answers': answers,
'score': round(total_score, 2)
})
return user_answers
# Алгоритм определения правильных ответов
def detect_correct_answers(questions, user_answers):
stats = defaultdict(lambda: defaultdict(lambda: {'total_score': 0, 'count': 0}))
# Сбор статистики
for user in user_answers:
score = user['score']
for ans in user['answers']:
q = questions[ans['q_id']]
answer = ans['answer']
if q['type'] == 'single':
key = answer
elif q['type'] == 'multi':
for o in answer:
stats[q['id']][o]['total_score'] += score
stats[q['id']][o]['count'] += 1
continue
else: # numeric - группируем с округлением
key = round(answer, 0)
stats[q['id']][key]['total_score'] += score
stats[q['id']][key]['count'] += 1
# Определение правильных ответов
predicted = {}
for q in questions:
q_stats = stats[q['id']]
if not q_stats:
predicted[q['id']] = None
continue
if q['type'] == 'single':
max_avg = -1
best_option = None
for opt, data in q_stats.items():
if data['count'] < 3: continue # порог минимальных ответов
avg = data['total_score'] / data['count']
if avg > max_avg:
max_avg = avg
best_option = opt
predicted[q['id']] = [best_option] if best_option else None
elif q['type'] == 'multi':
avg_scores = {}
for opt, data in q_stats.items():
if data['count'] < 3: continue
avg_scores[opt] = data['total_score'] / data['count']
if not avg_scores:
predicted[q['id']] = None
continue
threshold = np.mean(list(avg_scores.values()))
predicted[q['id']] = [k for k, v in avg_scores.items() if v > threshold]
else: # numeric
best_val = None
max_avg = -1
for val, data in q_stats.items():
if data['count'] < 3: continue
avg = data['total_score'] / data['count']
if avg > max_avg:
max_avg = avg
best_val = val
predicted[q['id']] = best_val
return predicted
from itertools import product
from collections import defaultdict
def brute_force_analysis(questions, attempts):
"""
Анализ методом перебора всех возможных комбинаций ответов
"""
# Генерируем все возможные комбинации правильных ответов
possible_answers = []
for q in questions:
possible_answers.append(q['options'])
all_combinations = list(product(*possible_answers))
valid_combinations = []
# Проверяем каждую комбинацию
for combination in all_combinations:
valid = True
for attempt in attempts:
expected_score = 0
# Считаем ожидаемый балл для студента
for q_idx, answer in enumerate(attempt['answers']):
if answer == combination[q_idx]:
expected_score += 1
# Проверяем соответствие реальному баллу
if abs(expected_score - attempt['score']) > 1e-9:
valid = False
break
if valid:
valid_combinations.append(combination)
# Считаем статистику
result = []
total = len(valid_combinations)
counter = defaultdict(int)
for combo in valid_combinations:
counter[combo] += 1
for combo, count in counter.items():
probability = count / total * 100 if total > 0 else 0
result.append({
'combination': combo,
'probability': round(probability, 1)
})
return sorted(result, key=lambda x: -x['probability'])
# Тестирование
for i in range(20, 21, 10):
# Генерация данных
questions = generate_questions(10)
user_answers = generate_answers(i, questions)
# Запуск алгоритма
# predicted = detect_correct_answers(questions, user_answers)
predicted = brute_force_analysis(questions, user_answers)
# pprint(questions)
# pprint(user_answers[:10])
# pprint(predicted)
# Оценка точности
correct_count = 0
for q in questions:
true = q['correct']
pred = predicted[q['id']]
if q['type'] == 'multi':
match = set(pred) == set(true) if pred else False
else:
match = (pred == true) if pred else False
if match:
correct_count += 1
print(f"{i} => {correct_count/len(questions)*100:.1f}%")
To embed this project on your website, copy the following code and paste it into your website's HTML: