You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
2.4 KiB

import 'dart:math';
import '../models/question.dart';
import 'spaced_repetition.dart';
/// Selects questions using weighted random selection.
class WeightedSelector {
final Random _random;
/// Creates a weighted selector with an optional random seed.
WeightedSelector({int? seed}) : _random = Random(seed);
/// Selects [count] questions from [candidates] using weighted random selection.
///
/// [currentAttemptIndex] is used for spaced repetition calculations.
/// Returns a list of selected questions (no duplicates).
List<Question> selectQuestions({
required List<Question> candidates,
required int count,
required int currentAttemptIndex,
}) {
if (candidates.isEmpty || count <= 0) {
return [];
}
if (count >= candidates.length) {
return List.from(candidates);
}
final selected = <Question>[];
final available = List<Question>.from(candidates);
while (selected.length < count && available.isNotEmpty) {
final question = _selectOne(
candidates: available,
currentAttemptIndex: currentAttemptIndex,
);
selected.add(question);
available.remove(question);
}
return selected;
}
/// Selects a single question using weighted random selection.
Question _selectOne({
required List<Question> candidates,
required int currentAttemptIndex,
}) {
if (candidates.length == 1) {
return candidates.first;
}
// Calculate weights for all candidates
final weights = candidates.map((q) {
if (q.isKnown) {
return SpacedRepetition.calculateKnownQuestionWeight(
lastAttemptIndex: q.lastAttemptIndex,
currentAttemptIndex: currentAttemptIndex,
);
} else {
// Unknown questions: priority + 1 to ensure non-zero weight
return (q.priorityPoints + 1).toDouble();
}
}).toList();
// Calculate cumulative weights
final cumulativeWeights = <double>[];
double sum = 0.0;
for (final weight in weights) {
sum += weight;
cumulativeWeights.add(sum);
}
// Select random value in range [0, sum)
final randomValue = _random.nextDouble() * sum;
// Find the index corresponding to the random value
for (int i = 0; i < cumulativeWeights.length; i++) {
if (randomValue < cumulativeWeights[i]) {
return candidates[i];
}
}
// Fallback to last item (shouldn't happen, but safety)
return candidates.last;
}
}

Powered by TurnKey Linux.