Java中间隔重复算法的开源实现



我在做一个项目,其中间隔重复是必不可少的,但我不是这方面的专家,我害怕重新发明方轮。我的研究给了我两种不同的系统,即莱特纳系统和SM系列算法。我还没有决定哪个系统最适合我的项目。如果我采取SM的方向,我想我会尝试实现类似Anki使用的东西。

我最好的选择是使用现有的Java库。这可能很简单,我所需要的只是计算下一次重复的时间。
有人听说过这样的倡议吗?

我还没有看过Anki的实现,但是你见过这个吗?

基本上是这样的

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);
}

如果你真的想要Anki的算法,可以在Github上查看Android的Anki源代码。它是GPL,所以你可能需要购买许可证。

我确实在自己的抽认卡应用程序中重新发明了方轮。算法很简单:物品的重量是年龄分量进度分量努力分量的乘积。

年龄

组件

公式为A(x) = Cn^x,其中
  • x为自物品最后一次测试以来的时间(以天为单位),
  • C是当x为零时你想要的值,
  • n是一个常量,基于你希望该值随着x的增加而增加的速度。

例如,如果您希望该值每五天翻一番,则n = e^(ln(2/C)/5)。

<<p> 进步组件/strong>

公式为p (x) = Cn^-x,其中

  • x是一个数字,对应于你在这个项目上的成功程度,
  • C是当x为零时你想要的值,
  • n是一个常量,基于你希望值随x增加而衰减的速度。

例如,如果您希望每五次连续成功将值减半,则n = e^(ln(1/2)/-5)。

<<p> 工作组件/strong>

接受以下两个值之一:

  • 10,如果你发现你最后一次召回的项目是"硬",或
  • 1。

进度调整如下:

  • 新条目从进度0开始。
  • 如果你发现一个答案很容易,这个项目的进度增加1。
  • 如果你发现一个答案很难,项目的进度将变为min(int(previous/2), previous - 1)。
  • 如果你得到了一个错误的答案,项目的进度会降到最小(-1,前一个-1)。

是的,值可以变为负值。:)

应用程序通过从所有物品中随机选择来选择下一个要测试的物品,选择的概率与物品的重量直接变化。

算法中的具体数字是可调整的。我已经用我的当前值大约一年了,在积累和保留西班牙语、德语和拉丁语的词汇方面取得了巨大的成功。

(抱歉马铃薯质量的数学表达式。这里不允许使用LaTeX。)

Anki使用SM2算法。然而,该文章所描述的SM2有许多严重的缺陷。幸运的是,它们很容易修复。

对于这篇文章来说,解释如何做到这一点太长了,所以我在这里写了一篇博文。不需要使用开源库来完成此操作,因为实际实现非常简单。

这是一个记录良好且易于理解的间隔重复算法。

特性
  • 介绍了有效学习大型甲板的子甲板(Super有用!)
  • 直观的变量名和算法参数。完全
  • 容易配置参数以适应不同用户的记忆能力。
  • 计算下一张卡的成本很低。不需要对牌组中的每张牌进行计算。

https://github.com/Jakobovski/SaneMemo。

免责声明:我是SaneMemo的作者。

import random
import datetime
# The number of times needed for the user to get the card correct(EASY) consecutively before removing the card from
# the current sub_deck.
CONSECUTIVE_CORRECT_TO_REMOVE_FROM_SUBDECK_WHEN_KNOWN = 2
CONSECUTIVE_CORRECT_TO_REMOVE_FROM_SUBDECK_WHEN_WILL_FORGET = 3
# The number of cards in the sub-deck
SUBDECK_SIZE = 15
REMINDER_RATE = 1.6
class Deck(object):
    def __init__(self):
        self.cards = []
        # Used to make sure we don't display the same card twice
        self.last_card = None
    def add_card(self, card):
        self.cards.append(card)
    def get_next_card(self):
        self.cards = sorted(self.cards)  # Sorted by next_practice_time
        sub_deck = self.cards[0:min(SUBDECK_SIZE, len(self.cards))]
        card = random.choice(sub_deck)
        # In case card == last card lets select again. We don't want to show the same card two times in a row.
        while card == self.last_card:
            card = random.choice(sub_deck)
        self.last_card = card
        return card

class Card(object):
    def __init__(self, front, back):
        self.front = front
        self.back = back
        self.next_practice_time = datetime.utc.now()
        self.consecutive_correct_answer = 0
        self.last_time_easy = datetime.utc.now()
    def update(self, performance_str):
        """ Updates the card after the user has seen it and answered how difficult it was. The user can provide one of
        three options: [I_KNOW, KNOW_BUT_WILL_FORGET, DONT_KNOW].
        """
        if performance_str == "KNOW_IT":
            self.consecutive_correct_answer += 1
            if self.consecutive_correct_answer >= CONSECUTIVE_CORRECT_TO_REMOVE_FROM_SUBDECK_WHEN_KNOWN:
                days_since_last_easy = (datetime.utc.now() - self.last_time_easy).days
                days_to_next_review = (days_since_last_easy + 2) * REMINDER_RATE
                self.next_practice_time = datetime.utc.now() + datetime.time(days=days_to_next_review)
                self.last_time_easy = datetime.utc.now()
            else:
                self.next_practice_time = datetime.utc.now()
        elif performance_str == "KNOW_BUT_WILL_FORGET":
            self.consecutive_correct_answer += 1
            if self.consecutive_correct_answer >= CONSECUTIVE_CORRECT_TO_REMOVE_FROM_SUBDECK_WHEN_WILL_FORGET:
                self.next_practice_time = datetime.utc.now() + datetime.time(days=1)
            else:
                self.next_practice_time = datetime.utc.now()
        elif performance_str == "DONT_KNOW":
            self.consecutive_correct_answer = 0
            self.next_practice_time = datetime.utc.now()
    def __cmp__(self, other):
        """Comparator or sorting cards by next_practice_time"""
        if hasattr(other, 'next_practice_time'):
            return self.number.__cmp__(other.next_practice_time)

最新更新