Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient algorithm for determining values not as frequent in a list

I am building a quiz application which pulls questions randomly from a pool of questions. However, there is a requirement that the pool of questions be limited to questions that the user has not already seen. If, however, the user has seen all the questions, then the algorithm should "reset" and only show questions the user has seen once. That is, always show the user questions that they have never seen or, if they have seen all of them, always show them questions they have seen less frequently before showing questions they have seen more frequently.

The list (L) is created in a such a way that the following is true: any value in the list (I), may exist once or be repeated in the list multiple times. Let's define another value in the list, J, such that it's not the same value as I. Then 0 <= abs(frequency(I) - frequency(J)) <= 1 will always be true.

To put it another way: if a value is repeated in the list 5 times, and 5 times is the maximum number of times any value has been repeated in the list, then all values in the list will be repeated either 4 or 5 times. The algorithm should return all values in the list with frequency == 4 before it returns any with frequency == 5.

Sorry this is so verbose, I'm struggling to define this problem succinctly. Please feel free to leave comments with questions and I will further qualify if needed.

Thanks in advance for any help you can provide.

Clarification

Thank you for the proposed answers so far. I don't think any of them are there yet. Let me further explain.

I'm not interacting with the user and asking them questions. I'm assigning the question ids to an exam record so that when the user begins an exam, the list of questions they have access to is determined. Therefore, I have two data structures to work with:

  • List of possible question ids that the user has access to
  • List of all question ids this user has ever been assigned previously. This is the list L described above.

So, unless I am mistaken, the algorithm/solution to this problem will need to involve list &/or set based operations using the two lists described above.

The result will be a list of question ids I can associate with the exam record and then insert into the database.

like image 983
Randy Syring Avatar asked Dec 06 '22 02:12

Randy Syring


1 Answers

Rewritten with the database stuff in fill-in pseudocode.

If I understand the problem correctly, I'd treat the questions (or their IDs as proxies) like a physical deck of cards: for each user, shuffle the deck and deal them a question at a time; if they want more than len(deck) questions, just start over: shuffle the deck into a new order and do it again. When a question is seen for the nth time, all other questions will have been seen n or n-1 times.

To keep track of what questions the user has had available, we put the unused question IDs back in the database and increment the "how many times through" counter whenever we need a new deal.

Something like:

from random import shuffle

def deal():
    question_IDs = get_all_questions(dbconn) # all questions
    shuffle(question_IDs)
    increment_deal_count(dbconn, userID) # how often this student has gotten questions
    return question_IDs


count_deals = get_stored_deals(dbconn, userID) # specific to this user
if count_deals: 
    question_IDs = get_stored_questions(dbconn, userID) # questions stored for this user 
else: # If 0 or missing, this is the first time for this student
    question_IDs = deal()


while need_another_question(): #based on exam requirements
    try:
        id = question_IDs.pop()
    except IndexError:
        question_IDs = deal()
        id = question_IDs.pop() # Trouble if db is ever empty. 

    use_question(id) # query db with the ID, then put question in print, CMS, whatever

# When we leave that while loop, we have used at least some of the questions
# question_IDs lists the *unused* ones for this deal
# and we know how many times we've dealt.

store_in_db(dbconn, userinfo, question_IDs)
# If you want to know how many times a question has been available, it's
# count_deals - (ID in question_IDs)
# because True evaluates to 1 if you try to subtract it from an integer. 
like image 191
cphlewis Avatar answered Dec 28 '22 08:12

cphlewis