Ankiのソースコードはこちらに公開されているが、これをもとにアルゴリズムを読み解くのは一筋縄ではいかない。
別サイトにて、アルゴリズムを噛み砕いてシンプルなPythonコードに落としてくれたものがあったため、これをもとにスケジューリング規則の理解を深めた。
以下、コードそのままの説明ではあるが、Anki初心者にとっては各種設定値の理解に繋がると思い、コードの解釈を書き下ろしてみた。
デッキオプションとカード内部の値
デッキオプションの画面で任意に変更できる、各設定値は以下のような用語で定義する。
# "New Cards" tab
NEW_STEPS = [1, 10] # in minutes (学習ステップ)
GRADUATING_INTERVAL = 1 # in days (最終ステップから復習開始までの間隔)
EASY_INTERVAL = 4 # in days (「簡単」と回答してから復習開始までの間隔 )
STARTING_EASE = 250 # in percent (復習開始時の間隔の伸び率)
# "Reviews" tab
EASY_BONUS = 130 # in percent (「簡単」と回答した時のボーナス)
INTERVAL_MODIFIER = 100 # in percent (復習ペースの調整)
MAXIMUM_INTERVAL = 36500 # in days (復習間隔の上限)
# "Lapses" tab
LAPSES_STEPS = [10] # in minutes (再学習ステップ)
NEW_INTERVAL = 70 # in percent (新しい復習間隔(前回比))
MINIMUM_INTERVAL = 1 # in days (忘却カードの復習開始までの間隔)
また、1つ1つのカードは、次のような4つの値を内部に持っている。
self.status = 'learning'
self.steps_index = 0
self.ease_factor = STARTING_EASE
self.interval = None
- ステータス: ’learning'(学習中), ‘learned'(復習中), ‘relearning'(再学習中)のいずれかの値をとる
- ステップ数: 「学習中」「再学習中」プロセスにおける何ステップにいるか
- 簡単さ: 「復習開始時の間隔の伸び率」(250%)が初期値となる
- 復習間隔
「復習中」カードについて
まず、一番わかりやすい「復習中」のカードについて考える。
復習中のカードに対して、「やり直し」「難しい」「普通」「簡単」の4つの選択肢が与えられる。
# 「復習中(learned)」カードの場合
if response == "hard":
card.ease_factor = max(130, card.ease_factor - 15)
card.interval = card.interval * 1.2 * INTERVAL_MODIFIER/100
return min(MAXIMUM_INTERVAL, card.interval)
elif response == "good":
card.interval = (card.interval * card.ease_factor/100 * INTERVAL_MODIFIER/100)
return min(MAXIMUM_INTERVAL, card.interval)
elif response == "easy":
card.ease_factor += 15
card.interval = (card.interval * card.ease_factor/100 * INTERVAL_MODIFIER/100 * EASY_BONUS/100)
return min(MAXIMUM_INTERVAL, card.interval)
復習中のカードに対して、・・・
「難しい」と解答すると、
①カードの簡単さ(ease factor)が最小130%になるまで15%ずつ減る。
②カードの復習間隔値が、前回の復習間隔 × 1.2倍 × INTERVAL_MODIFIER の値となる。
③最大の復習間隔に達するまで②が次の復習間隔となる。
「普通」と解答すると、
①カードの簡単さ(ease factor)は変化しない。
②カードの復習間隔値が、前回の復習間隔 × 簡単さ(ease factor) × INTERVAL_MODIFIER の値となる。
③最大の復習間隔に達するまで、②が次の復習間隔となる。
「簡単」と解答すると、
①カードの簡単さ(ease factor)が15%ずつ増える。
②カードの復習間隔値が、前回の復習間隔 × 簡単さ(ease factor) × INTERVAL_MODIFIER × EASY_BONUS の値となる。
③最大の復習間隔に達するまで、②が次の復習間隔となる。
もし「やり直し」を選択した場合は、以下のようになる。
if response == "again":
card.status = 'relearning'
card.steps_index = 0
card.ease_factor = max(130, card.ease_factor - 20)
card.interval = max(MINIMUM_INTERVAL, card.interval * NEW_INTERVAL/100)
return minutes_to_days(LAPSES_STEPS[0])
すなわち、
①カードのステータスが「再学習中」に変わる。
②再学習ステップの1ステップ目に配置される。
③簡単さ(ease factor)が最小でも130%になるまで20%引かれる。
④復習間隔値が、最小でもMINIMUM_INTERVAL(忘却カードの復習開始までの間隔)になるまで、前回の復習間隔 × NEW_INTERVAL(新しい復習間隔の比率)の値となる。
「再学習中」カードについて ※後日修正
elif card.status == 'relearning':
if response == "again":
card.steps_index = 0
return minutes_to_days(LAPSE_STEPS[0])
elif response == "good":
card.steps_index += 1
if card.steps_index < len(LAPSE_STEPS):
return minutes_to_days(LAPSE_STEPS[card.steps_index])
else:
# we have re-graduated!
card.status = 'learned'
# we don't modify the interval here because that was already done when
# going from 'learned' to 'relearning'
return card.interval
「学習中」カードについて ※後日修正
if card.status == 'learning':
# for learning cards, there is no "hard" response possible
if response == "again":
card.steps_index = 0
return minutes_to_days(NEW_STEPS[card.steps_index])
elif response == "good":
card.steps_index += 1
if card.steps_index < len(NEW_STEPS):
return minutes_to_days(NEW_STEPS[card.steps_index])
else:
# we have graduated!
card.status = 'learned'
card.interval = GRADUATING_INTERVAL
return card.interval
elif response == "easy":
card.status = 'learned'
card.interval = EASY_INTERVAL
return EASY_INTERVAL