Create a gist now

Instantly share code, notes, and snippets.

@fasiha /README.md
Last active Jan 28, 2018

What would you like to do?
How Anki calculates intervals

Reading _nextRevIvl and its subfunction _constrainedIvl plus _rescheduleLapse will illuminate how Anki calculates the due date (the “interval”) of a flashcard, based on whether you answer

  • 1 (fail)
  • 2 (pass but hard)
  • 3 (pass)
  • 4 (pass and easy)

This is more of a self-note, so I assume you’ve read the Anki manual top-to-bottom a couple of times.

Let d >= 0, “delay”, be the days between the due date and the date you actually reviewed. This can be important because if you successfully answer a flashcard a long time after it was due for study, that means you probably know it really well.

Let f, “factor” be the flashcard’s factor, which is Anki’s estimate for how well you’ve memorized this flashcard. The bigger the number, the better you understand it. It’s maximum is 1300 and it gets incremented by 150 to 200 “points” every review, as we will show below. The manual also calls this “ease”.

A few user-configured parameters. Let m be the “interval modifier”, a number nominally around 1.0 but that a user can set higher or lower. Anki multiplies the interval by this number, letting the user ask for more or less time between reviews uniformly, across all cards. Let m4 be another user-specified interval modifier, by default 1.3, which applies for “easy” reviews (hence the “4” in the name). And let m0 be the user-specified “new interval”, i.e., a small number closer to 0 than 1 that’s used when you fail a card. By default m0 = 0.

Finally, let i be the most-recent interval.

Here’s how to calculate the new interval:

  • i1 = m0 * i is the new interval for failed reviews. It can have a minimum.
  • i2 = max(i + 1, (i + d/4) * 1.2 * m) is the new interval for hard reviews.
  • i3 = max(i2 + 1, (i + d/2) * (f / 1000) * m) is the new interval for ok reviews.
  • i4 = max(i3 + 1, (i + d) * (f / 1000) * m * m4) is the new interval for easy reviews.

And here’s another very important piece: how to calculate the new factor f. Recall that this is Anki’s guess of the strengh of the memory in your mind, and it controls the exponential growth of intervals for ok (#3) and easy (#4) reivews.

  • f' = max(1300, f - 200) when you fail a review (#1, reference)
  • f' = max(1300, f - 150) for a hard review (#2).
  • f' = f for an ok review (#3). No change in factor.
  • f' = max(1300, f + 150) for an easy review (#4, reference).

In words, failed and hard reviews decrease the rate of exponential growth of intervals. Ok reivews maintain it. Easy reviews increase the rate of growth.

@6lancmange

This comment has been minimized.

Show comment Hide comment
@6lancmange

6lancmange Jul 23, 2017

So the "factor" is what is actually called "easiness" in the manual and the settings?
Are you sure it's f/1000? That would reduce the interval. Since the easiness is given in % and you're using just a regular number as f, it would make sense to divide it by 100 - that's basically translating the number in % into a normal one.

So the "factor" is what is actually called "easiness" in the manual and the settings?
Are you sure it's f/1000? That would reduce the interval. Since the easiness is given in % and you're using just a regular number as f, it would make sense to divide it by 100 - that's basically translating the number in % into a normal one.

@Golddouble

This comment has been minimized.

Show comment Hide comment
@Golddouble

Golddouble Jan 7, 2018

@6lancmange

So the "factor" is what is actually called "easiness" in the manual and the settings?

Yes, I think so.

Are you sure it's f/1000? ... Since the easiness is given in % ...

f is in pro mille. So 2500 = 250% = 2.5

@fasiha
Thank you for your work. I appreciate it, and it was useful for me. But

f' = f - 200 when you fail a review
Shouldn't it be
f' = max (1300, f - 200) when you fail a review
and
f' = f - 150 for a hard review
Shouldn't it be
f' = max (1300, f - 150) for a hard review

further:

i3 = max(i2 + 1, (i + d/2) * (f / 1000) * m) is the new interval for ok reviews.
i4 = max(i3 + 1, (i + d) * (f / 1000) * m * m4) is the new interval for easy reviews.

shouldn't it be:

i3 = max(i + 1, (i + d/2) * (f / 1000) * m) is the new interval for ok reviews.
i4 = max(i + 1, (i + d) * (f / 1000) * m * m4) is the new interval for easy reviews

Source: https://github.com/dae/anki/blob/24b451b0e4cfb50191e294052363a79f69f35c02/anki/sched.py#L815

and further:

Let f, “factor” be the flashcard’s factor, which is Anki’s estimate for how well you’ve memorized this flashcard. The bigger the number, the better you understand it. It’s maximum is 1300 ...

"It's maximum is 1300". Shouldn't it be: "It's minimum is 1300"? Factor will never be lower than 1300.

Golddouble commented Jan 7, 2018

@6lancmange

So the "factor" is what is actually called "easiness" in the manual and the settings?

Yes, I think so.

Are you sure it's f/1000? ... Since the easiness is given in % ...

f is in pro mille. So 2500 = 250% = 2.5

@fasiha
Thank you for your work. I appreciate it, and it was useful for me. But

f' = f - 200 when you fail a review
Shouldn't it be
f' = max (1300, f - 200) when you fail a review
and
f' = f - 150 for a hard review
Shouldn't it be
f' = max (1300, f - 150) for a hard review

further:

i3 = max(i2 + 1, (i + d/2) * (f / 1000) * m) is the new interval for ok reviews.
i4 = max(i3 + 1, (i + d) * (f / 1000) * m * m4) is the new interval for easy reviews.

shouldn't it be:

i3 = max(i + 1, (i + d/2) * (f / 1000) * m) is the new interval for ok reviews.
i4 = max(i + 1, (i + d) * (f / 1000) * m * m4) is the new interval for easy reviews

Source: https://github.com/dae/anki/blob/24b451b0e4cfb50191e294052363a79f69f35c02/anki/sched.py#L815

and further:

Let f, “factor” be the flashcard’s factor, which is Anki’s estimate for how well you’ve memorized this flashcard. The bigger the number, the better you understand it. It’s maximum is 1300 ...

"It's maximum is 1300". Shouldn't it be: "It's minimum is 1300"? Factor will never be lower than 1300.

@fasiha

This comment has been minimized.

Show comment Hide comment
@fasiha

fasiha Jan 28, 2018

@Golddouble thanks for getting in touch (note that GitHub doesn't notify me when people post comments >.< so please leave me a message some other way to make sure I see your response).

You're right, all f' must be less than or equal to 1300, I will update that.

About i3 = max(i + 1, ...), I am not sure if you're right: if you look at this and this carefully, you'll see that Damien is changing the _prev passed into _constrainedIvl (the last argument) each time he calls it, which has the effect of steadily increasing the interval like I've shown. Am I making a mistake here, could you double-check your reading?

Finally, 1300 is the maximum factor right, since here and here he assigns factor = max(1300, ...)? The factor will never be more than 1300?

@6lancmange yes, "factor" is what the manual calls "ease". It's a bit confusing—I think Damien has changed what he calls many of these concepts over the years while keeping the code's variable names the same. I'll clarify this in the text.

Owner

fasiha commented Jan 28, 2018

@Golddouble thanks for getting in touch (note that GitHub doesn't notify me when people post comments >.< so please leave me a message some other way to make sure I see your response).

You're right, all f' must be less than or equal to 1300, I will update that.

About i3 = max(i + 1, ...), I am not sure if you're right: if you look at this and this carefully, you'll see that Damien is changing the _prev passed into _constrainedIvl (the last argument) each time he calls it, which has the effect of steadily increasing the interval like I've shown. Am I making a mistake here, could you double-check your reading?

Finally, 1300 is the maximum factor right, since here and here he assigns factor = max(1300, ...)? The factor will never be more than 1300?

@6lancmange yes, "factor" is what the manual calls "ease". It's a bit confusing—I think Damien has changed what he calls many of these concepts over the years while keeping the code's variable names the same. I'll clarify this in the text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment