用于学习的间隔重复(SRS)

时间:2010-06-03 09:05:31

标签: sql algorithm

客户要求我为在线学习网站添加简单间隔重复算法(SRS)。但在把自己投入其中之前,我想与社区讨论。

基本上,网站向用户询问一堆问题(通过自动选择数据库中100个问题中的10个),用户给出正确或错误的答案。然后将用户结果存储在数据库中,例如:

userid  questionid  correctlyanswered  dateanswered
1       123         0 (no)             2010-01-01 10:00
1       124         1 (yes)            2010-01-01 11:00
1       125         1 (yes)            2010-01-01 12:00    

现在,为了最大限度地提高用户学习所有答案的能力,我应该能够应用SRS算法,以便用户在下次参加测验时,能够更频繁地接收错误答案的问题;比正确回答的问题。此外,以前回答错误但最近经常回答正确的问题应该不经常发生。

有没有人之前实现过这样的事情?有任何提示或建议吗?

Theese是我发现的最佳链接:

3 个答案:

答案 0 :(得分:8)

Anki是一个实现间隔重复的开源程序。 作为开源,您可以浏览libanki的来源,这是Anki的间隔重复库。 截至2013年1月,可以浏览Anki版本2来源here

源代码位于Python可执行伪代码语言。 读取源以理解算法可能是可行的。数据模型使用sqlalechmey,Python SQL工具包和对象关系映射器定义,为应用程序开发人员提供了SQL的全部功能和灵活性。

答案 1 :(得分:6)

您要做的是为所有问题X_i设置一个i号码。您可以将这些数字标准化(使它们的总和为1)并进行优先选择。

如果N是不同问题的数量而M是每个问题的平均回答次数,那么您可以在X时间内找到M*N像这样:

  • 创建数组X[N]设置为0。
  • 浏览数据,每当您看到问题i回答错误时,请N[i]增加f(t),其中t为回答时间f是一种发挥作用。

因为f正在增加,很久以前回答错误的问题比昨天回答错误的问题影响小。您可以尝试使用不同的f来获得良好的行为。

更聪明的方式 更快的方法是每次选择问题时都不生成X[],而是将其保存在数据库表中。 您无法使用此解决方案应用f。相反,每次错误地回答问题时只需添加1,然后定期浏览表格 - 比如说每个午夜 - 并将所有X[i]乘以常数 - 比如0.9

更新:实际上,您的数据应该基于更正,而不是错误。否则长时间没有回答既不是真实也不是错误的问题将被选中的机会较小。应该是相反的。

答案 2 :(得分:1)

这是一种有间隔的重复算法,有据可查且易于理解。

功能

  • 介绍用于有效学习大型甲板的子甲板(超级 有用!)
  • 直观的变量名称和算法参数。完全 具有人类可读示例的开源。
  • 易于配置 参数以适应不同用户的记忆 能力。
  • 计算下一张卡在计算上很便宜。没有必要 在卡组中的每张卡上进行计算。

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)