pub struct SwitchingStrategy {
history_threshold: usize,
}
impl SwitchingStrategy {
#[must_use]
pub fn new(history_threshold: usize) -> Self {
Self { history_threshold }
}
#[must_use]
pub fn select_method(&self, context: &SwitchContext) -> RecommendMethod {
if context.user_history_length < self.history_threshold {
return RecommendMethod::ContentBased;
}
if context.user_history_length >= self.history_threshold * 2 {
return RecommendMethod::Collaborative;
}
if context.is_peak_hours && context.user_history_length > 0 {
return RecommendMethod::Trending;
}
RecommendMethod::Hybrid
}
#[must_use]
pub fn should_switch(&self, current: &RecommendMethod, context: &SwitchContext) -> bool {
let optimal = self.select_method(context);
!matches!(
(current, optimal),
(RecommendMethod::ContentBased, RecommendMethod::ContentBased)
| (
RecommendMethod::Collaborative,
RecommendMethod::Collaborative
)
| (RecommendMethod::Trending, RecommendMethod::Trending)
| (RecommendMethod::Hybrid, RecommendMethod::Hybrid)
)
}
}
impl Default for SwitchingStrategy {
fn default() -> Self {
Self::new(5)
}
}
#[derive(Debug, Clone)]
pub struct SwitchContext {
pub user_history_length: usize,
pub is_peak_hours: bool,
pub engagement_score: f32,
pub content_availability: usize,
}
impl Default for SwitchContext {
fn default() -> Self {
Self {
user_history_length: 0,
is_peak_hours: false,
engagement_score: 0.5,
content_availability: 100,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecommendMethod {
ContentBased,
Collaborative,
Trending,
Hybrid,
}
pub struct AdaptiveSwitching {
base_strategy: SwitchingStrategy,
method_performance: MethodPerformance,
}
impl AdaptiveSwitching {
#[must_use]
pub fn new() -> Self {
Self {
base_strategy: SwitchingStrategy::default(),
method_performance: MethodPerformance::new(),
}
}
#[must_use]
pub fn select_method(&self, context: &SwitchContext) -> RecommendMethod {
let base_method = self.base_strategy.select_method(context);
let best_performing = self.method_performance.get_best_method();
if self.method_performance.get_confidence(best_performing) > 0.8 {
best_performing
} else {
base_method
}
}
pub fn update_performance(&mut self, method: RecommendMethod, success: bool) {
self.method_performance.record_outcome(method, success);
}
}
impl Default for AdaptiveSwitching {
fn default() -> Self {
Self::new()
}
}
struct MethodPerformance {
successes: [u32; 4],
totals: [u32; 4],
}
impl MethodPerformance {
fn new() -> Self {
Self {
successes: [0; 4],
totals: [0; 4],
}
}
fn record_outcome(&mut self, method: RecommendMethod, success: bool) {
let idx = method_to_index(method);
self.totals[idx] += 1;
if success {
self.successes[idx] += 1;
}
}
fn get_confidence(&self, method: RecommendMethod) -> f32 {
let idx = method_to_index(method);
if self.totals[idx] == 0 {
0.0
} else {
self.successes[idx] as f32 / self.totals[idx] as f32
}
}
fn get_best_method(&self) -> RecommendMethod {
let mut best_idx = 0;
let mut best_confidence = 0.0;
for i in 0..4 {
if self.totals[i] > 0 {
let confidence = self.successes[i] as f32 / self.totals[i] as f32;
if confidence > best_confidence {
best_confidence = confidence;
best_idx = i;
}
}
}
index_to_method(best_idx)
}
}
fn method_to_index(method: RecommendMethod) -> usize {
match method {
RecommendMethod::ContentBased => 0,
RecommendMethod::Collaborative => 1,
RecommendMethod::Trending => 2,
RecommendMethod::Hybrid => 3,
}
}
fn index_to_method(idx: usize) -> RecommendMethod {
match idx {
0 => RecommendMethod::ContentBased,
1 => RecommendMethod::Collaborative,
2 => RecommendMethod::Trending,
_ => RecommendMethod::Hybrid,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_switching_strategy() {
let strategy = SwitchingStrategy::new(5);
let context = SwitchContext {
user_history_length: 3,
..Default::default()
};
let method = strategy.select_method(&context);
assert_eq!(method, RecommendMethod::ContentBased);
}
#[test]
fn test_switching_with_history() {
let strategy = SwitchingStrategy::new(5);
let context = SwitchContext {
user_history_length: 15,
..Default::default()
};
let method = strategy.select_method(&context);
assert_eq!(method, RecommendMethod::Collaborative);
}
#[test]
fn test_should_switch() {
let strategy = SwitchingStrategy::new(5);
let current = RecommendMethod::ContentBased;
let context = SwitchContext {
user_history_length: 15,
..Default::default()
};
assert!(strategy.should_switch(¤t, &context));
}
#[test]
fn test_adaptive_switching() {
let mut adaptive = AdaptiveSwitching::new();
adaptive.update_performance(RecommendMethod::ContentBased, true);
adaptive.update_performance(RecommendMethod::ContentBased, true);
let context = SwitchContext::default();
let _method = adaptive.select_method(&context);
}
}