Open Source implementation of a Spaced Repetition Algorithm in Java

Anki uses the SM2 algorithm. However, SM2 as described by that article has a number of serious flaws. Fortunately, they are simple to fix.

Explaining how to do so would be too lengthy of a subject for this post, so I've written a blog post about it here. There is no need to use an open-source library to do this, as the actual implementation is incredibly simple.


I haven't looked at Anki's implementation but have you seen this one? quiz-me an SRS in Java.

Basically it goes like this

public static void calcuateInterval(Card card) {
  if (card.getEFactor() < 3) {
      card.setCount(1);
  }
  int count = card.getCount();
  int interval = 1;
  if (count == 2) {
      interval = 6;
  } else if (count > 2) {
     interval =  Math.round(card.getInterval() * card.getEFactor());
  }
  card.setInterval(interval);
}

If you really want Anki's algorithm, look through the source of Anki in Android available in Github. It is GPL though so you might need to buy a license.


I did reinvent the square wheel in my own flashcard app. The algorithm is quite simple: The weight of an item is the product of an age component, a progress component, and an effort component.

Age component

The formula is A(x) = Cn^x, where

  • x is the time in days since the item was last tested,
  • C is the value you want when x is zero, and
  • n is a constant based on how fast you want the value to increase as x increases.

For example, if you want the value to double every five days, n = e^(ln(2/C)/5).

Progress component

The formula is P(x) = Cn^-x, where

  • x is a number that corresponds to how successful you've been with the item,
  • C is the value you want when x is zero, and
  • n is a constant based on how fast you want the value to decay as x increases.

For example, if you want the value to halve every five consecutive successes, n = e^(ln(1/2)/-5).

Effort component

This takes on one of two values:

  • 10 if you found your last recall of the item to be "hard", or
  • 1 otherwise.

The progress is adjusted thus:

  • New entries start with progress 0.
  • If you find an answer easy, the item's progress is increased by 1.
  • If you find an answer hard, the item's progress goes to min(int(previous / 2), previous - 1).
  • If you get an answer wrong, the item's progress goes to min(-1, previous - 1).

Yes, values can go negative. :)

The app selects the next item to test by making a random selection from all the items, with the probability of selection varying directly with an item's weight.

The specific numbers in the algorithm are tweakable. I've been using my current values for about a year, leading to great success in accumulating and retaining vocabulary for Spanish, German, and Latin.

(Sorry for potato quality of the math expressions. LaTeX isn't allowed here.)

Tags:

Algorithm

Java