How to take X amount of questions, that sum up Y amount of difficulty

前端 未结 1 912
温柔的废话
温柔的废话 2021-01-15 18:27

I have a table

Questions -> Question(string), Difficulty (int, 1-10)

I need to create a method, that as the title mentions, take

1条回答
  •  死守一世寂寞
    2021-01-15 18:56

    Here's one way to do it, using a modified version of the recursive solution found here: Finding all possible combinations of numbers to reach a given sum

    First, a public method that will do some quick validation and then call a recursive method to get the results:

    /// 
    /// Gets lists of numQuestions length of all combinations 
    /// of questions whose difficulties add up to sumDifficulty
    /// 
    /// The list of questions to search
    /// The number of questions required
    /// The amount that the difficulties should sum to
    /// 
    public static List> GetQuestions(List questions,
        int numQuestions, int sumDifficulty)
    {
        if (questions == null) throw new ArgumentNullException("questions");
    
        var results = new List>();
    
        // Fail fast argument validation
        if (numQuestions < 1 || 
            numQuestions > questions.Count ||
            sumDifficulty < numQuestions * Question.MinDifficulty ||
            sumDifficulty > numQuestions * Question.MaxDifficulty)
        {
            return results;
        }
    
        // If we only need single questions, no need to do any recursion
        if (numQuestions == 1)
        {
            results.AddRange(questions.Where(q => q.Difficulty == sumDifficulty)
                .Select(q => new List {q}));
    
            return results;
        }
    
        // We can remove any questions who have a difficulty that's higher
        // than the sumDifficulty minus the number of questions plus one
        var candidateQuestions =
            questions.Where(q => q.Difficulty <= sumDifficulty - numQuestions + 1)
                .ToList();
    
        if (!candidateQuestions.Any())
        {
            return results;
        }
    
        GetSumsRecursively(candidateQuestions, sumDifficulty, new List(), 
            numQuestions, results);
    
        return results;
    }
    

    And then the recursive method that does the heavy lifting:

    private static void GetSumsRecursively(IReadOnlyList questions, 
        int sumDifficulty, List candidates, int numQuestions, 
        ICollection> results)
    {
        int candidateSum = candidates.Sum(x => x.Difficulty);
    
        if (candidateSum == sumDifficulty && candidates.Count == numQuestions)
        {
            results.Add(candidates);
        }
    
        if (candidateSum >= sumDifficulty)
            return;
    
        for (int i = 0; i < questions.Count; i++)
        {
            var remaining = new List();
    
            for (int j = i + 1; j < questions.Count; j++)
            {
                remaining.Add(questions[j]);
            }
    
            var filteredCandidates = new List(candidates) {questions[i]};
    
            GetSumsRecursively(remaining, sumDifficulty, filteredCandidates, 
                numQuestions, results);
        }
    }
    

    Here's an example usage:

    public static void Main()
    {
        const int numberOfQuestions = 3;
        const int sumOfDifficulty = 15;
    
        // Since I don't have your table, I'm using a list of objects to fake it
        var questions = new List();
        for (int i = 1; i < 11; i++)
        {
            questions.Add(new Question {Difficulty = i % 10 + 1, 
                QuestionString = "Question #" + i});
        }
    
        var results = GetQuestions(questions, numberOfQuestions, sumOfDifficulty);
    
        // Write output to console to verify results
        foreach (var result in results)
        {
            Console.WriteLine("{0} = {1}", string.Join(" + ", 
                result.Select(r => r.Difficulty)), sumOfDifficulty);
        }
    }
    

    And just so you have everything to make this work, here's my Question class used to fake your table:

    internal class Question
    {
        public const int MinDifficulty = 1;
        public const int MaxDifficulty = 10;
        private int _difficulty;
    
        public int Difficulty
        {
            get { return _difficulty; }
            set
            {
                if (value < MinDifficulty) _difficulty = MinDifficulty;
                else if (value > MaxDifficulty) _difficulty = MaxDifficulty;
                else _difficulty = value;
            }
        }
    
        public string QuestionString { get; set; }
    }
    

    0 讨论(0)
提交回复
热议问题