Skip to content

Commit 97b8522

Browse files
authored
Feat/update to FSRS-4.5 (#568)
1 parent 321d0f8 commit 97b8522

4 files changed

Lines changed: 409 additions & 407 deletions

File tree

fsrs4anki_optimizer.ipynb

Lines changed: 363 additions & 372 deletions
Large diffs are not rendered by default.

fsrs4anki_scheduler.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// FSRS4Anki v4.10.0 Scheduler Qt6
1+
// FSRS4Anki v4.11.0 Scheduler Qt6
22
set_version();
33
// The latest version will be released on https://github.com/open-spaced-repetition/fsrs4anki/releases/latest
44

@@ -74,7 +74,7 @@ if (deck_name = get_deckname()) {
7474
}
7575
}
7676
// Arrange the deckParams of sub-decks in front of their parent decks.
77-
deckParams.sort(function(a, b) {
77+
deckParams.sort(function (a, b) {
7878
return -a.deckName.localeCompare(b.deckName);
7979
});
8080
for (let i = 0; i < deckParams.length; i++) {
@@ -94,8 +94,8 @@ if (Object.keys(params).length === 0) {
9494
var w = params["w"];
9595
var requestRetention = params["requestRetention"];
9696
var maximumInterval = params["maximumInterval"];
97-
// auto-calculate intervalModifier
98-
const intervalModifier = 9 * (1 / requestRetention - 1);
97+
const DECAY = -0.5;
98+
const FACTOR = 0.9 ** (1 / DECAY) - 1;
9999
// global fuzz factor for all ratings.
100100
const fuzz_factor = set_fuzz_factor();
101101
const ratings = {
@@ -138,7 +138,7 @@ if (is_new()) {
138138
const interval = states.current.normal?.review.elapsedDays ? states.current.normal.review.elapsedDays : states.current.filtered.rescheduling.originalState.review.elapsedDays;
139139
const last_d = customData.again.d;
140140
const last_s = customData.again.s;
141-
const retrievability = Math.pow(1 + interval / (9 * last_s), -1)
141+
const retrievability = forgetting_curve(interval, last_s);
142142
if (display_memory_state) {
143143
fsrs_status.innerHTML += "<br>D: " + last_d + "<br>S: " + last_s + "<br>R: " + (retrievability * 100).toFixed(2) + "%";
144144
}
@@ -182,8 +182,11 @@ function apply_fuzz(ivl) {
182182
}
183183
return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
184184
}
185+
function forgetting_curve(elpased_days, stability) {
186+
return Math.pow(1 + FACTOR * elpased_days / stability, DECAY);
187+
}
185188
function next_interval(stability) {
186-
const new_interval = apply_fuzz(stability * intervalModifier);
189+
const new_interval = apply_fuzz(stability / FACTOR * Math.pow(requestRetention, (1 / DECAY) - 1));
187190
return Math.min(Math.max(Math.round(new_interval), 1), maximumInterval);
188191
}
189192
function next_difficulty(d, rating) {
@@ -204,9 +207,9 @@ function next_recall_stability(d, s, r, rating) {
204207
easyBonus)).toFixed(2);
205208
}
206209
function next_forget_stability(d, s, r) {
207-
return +Math.min(w[11] *
208-
Math.pow(d, -w[12]) *
209-
(Math.pow(s + 1, w[13]) - 1) *
210+
return +Math.min(w[11] *
211+
Math.pow(d, -w[12]) *
212+
(Math.pow(s + 1, w[13]) - 1) *
210213
Math.exp((1 - r) * w[14]), s).toFixed(2);
211214
}
212215
function init_states() {
@@ -292,7 +295,7 @@ function is_empty() {
292295
return !customData.again.d | !customData.again.s | !customData.hard.d | !customData.hard.s | !customData.good.d | !customData.good.s | !customData.easy.d | !customData.easy.s;
293296
}
294297
function set_version() {
295-
const version = "v4.10.0";
298+
const version = "v4.11.0";
296299
customData.again.v = version;
297300
customData.hard.v = version;
298301
customData.good.v = version;

fsrs4anki_scheduler_qt5.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// FSRS4Anki v4.10.0 Scheduler Qt5
1+
// FSRS4Anki v4.11.0 Scheduler Qt5
22
set_version();
33
// The latest version will be released on https://github.com/open-spaced-repetition/fsrs4anki/releases/latest
44

@@ -96,8 +96,8 @@ if (Object.keys(params).length === 0) {
9696
const w = params["w"];
9797
const requestRetention = params["requestRetention"];
9898
const maximumInterval = params["maximumInterval"];
99-
// auto-calculate intervalModifier
100-
const intervalModifier = 9 * (1 / requestRetention - 1);
99+
const DECAY = -0.5;
100+
const FACTOR = 0.9 ** (1 / DECAY) - 1;
101101
// global fuzz factor for all ratings.
102102
const fuzz_factor = set_fuzz_factor();
103103
const ratings = {
@@ -143,7 +143,7 @@ if (is_new()) {
143143
const interval = (_states$current$norma = states.current.normal) !== null && _states$current$norma !== void 0 && _states$current$norma.review.elapsedDays ? states.current.normal.review.elapsedDays : states.current.filtered.rescheduling.originalState.review.elapsedDays;
144144
const last_d = customData.again.d;
145145
const last_s = customData.again.s;
146-
const retrievability = Math.pow(1 + interval / (9 * last_s), -1);
146+
const retrievability = forgetting_curve(interval, last_s);
147147
if (display_memory_state) {
148148
fsrs_status.innerHTML += "<br>D: " + last_d + "<br>S: " + last_s + "<br>R: " + (retrievability * 100).toFixed(2) + "%";
149149
}
@@ -188,8 +188,11 @@ function apply_fuzz(ivl) {
188188
}
189189
return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
190190
}
191+
function forgetting_curve(elpased_days, stability) {
192+
return Math.pow(1 + FACTOR * elpased_days / stability, DECAY);
193+
}
191194
function next_interval(stability) {
192-
const new_interval = apply_fuzz(stability * intervalModifier);
195+
const new_interval = apply_fuzz(stability / FACTOR * Math.pow(requestRetention, (1 / DECAY) - 1));
193196
return Math.min(Math.max(Math.round(new_interval), 1), maximumInterval);
194197
}
195198
function next_difficulty(d, rating) {
@@ -301,7 +304,7 @@ function is_empty() {
301304
return !customData.again.d | !customData.again.s | !customData.hard.d | !customData.hard.s | !customData.good.d | !customData.good.s | !customData.easy.d | !customData.easy.s;
302305
}
303306
function set_version() {
304-
const version = "v4.10.0";
307+
const version = "v4.11.0";
305308
customData.again.v = version;
306309
customData.hard.v = version;
307310
customData.good.v = version;

fsrs4anki_simulator.ipynb

Lines changed: 24 additions & 19 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)