Максимизация комбинации ряда ценностей

Это сложно, но я подозреваю, что есть какой-то принцип, который я могу применить, чтобы упростить его - я просто не знаю, что это такое.

Мне нужно распределить места для презентаций классу, заполненному студентами на семестр. Есть несколько возможных дат и несколько типов представления. Я провел опрос, в котором студенты могли оценить свой интерес к различным темам. Я бы хотел добиться наилучшего (или, по крайней мере, хорошего) распределения слотов для презентаций среди студентов.

Итак, что у меня есть:

  • Список из 12 дат
  • Список 18 студентов
  • CSV-файл, в котором каждый учащийся (строка) имеет оценку от 1 до 5 для каждой даты.

Что бы я хотел получить:

  • У каждого студента должна быть одна презентация типа A (intro), одна презентация типа B (figures) и 3 презентации типа C (aims)
  • На каждом свидании должно быть не менее 1 презентации каждого типа.
  • На каждой дате должно быть не более 2-х знаков типа A или типа B.
  • Постарайтесь провести студентам презентации, которые они высоко оценили (4 или 5).

Должен заметить, что я понимаю, что это похоже на домашнее задание, но это реальная жизнь :-). Я думал, что могу создать Student класс для каждого студента, который будет содержать даты для каждого типа презентации, но я не был уверен, как лучше всего его заполнить. На самом деле, я даже не знаю, с чего начать.


person kevbonham    schedule 25.01.2016    source источник
comment
Покажите нам, что вы пробовали, и мы поможем вам в этом   -  person inspectorG4dget    schedule 25.01.2016
comment
Есть ли ограниченное количество слотов для презентаций типа C в день? И должны ли студенты давать свои презентации A, B, C по порядку?   -  person Junuxx    schedule 25.01.2016
comment
Не более двух типов A или B означают (A+B) <= 2 или A =< 2 && B<= 2?   -  person Junuxx    schedule 25.01.2016
comment
@ InspectorG4dget Я дошел до class Student(object): и импортировал CSV с ответами на опрос через pandas, но я действительно не знаю, с чего начать. Извините - это может быть слишком аморфный вопрос для этого форума: - /   -  person kevbonham    schedule 26.01.2016
comment
@Junuxx Нет ограничений на C в день (хотя в идеале они должны быть распределены равномерно) и нет, порядок для каждого ученика не важен.   -  person kevbonham    schedule 26.01.2016
comment
@Junuxx re: ваш второй вопрос, _1 _... в идеале (A+B) <= 3   -  person kevbonham    schedule 26.01.2016


Ответы (1)


TL; DR: я думаю, вы предоставляете своим ученикам слишком большой выбор: D

Но у меня все равно была попытка решить эту проблему. На самом деле, довольно забавное упражнение, хотя некоторые ограничения были немного расплывчаты. Прежде всего, мне нужно было угадать, как будет выглядеть фактическое распределение предпочтений студентов. Я использовал равномерно распределенные независимые переменные, хотя это, вероятно, не очень реалистично. Тем не менее, я думаю, что он должен работать с реальными данными так же хорошо, как и с моими случайно сгенерированными данными.

Я считал это грубым форсированием, но грубый анализ дал мне оценку более 10 ^ 65 возможных конфигураций. Это довольно много. А поскольку у нас нет триллиона триллионов лет, чтобы рассмотреть их все, нам понадобится эвристический подход.

Из-за масштабов проблемы я старался не возвращаться. Но это означало, что можно застрять; Возможно, не будет решения, при котором все будут получать только даты, которые они поставили 4 и 5.

В итоге я реализовал обоюдоострый поиск в стиле итеративного углубления, в котором оба лучший случай, на который мы все еще надеемся (т. е. назначить ученикам дату, которую они поставили 5), и наихудший сценарий, который мы готовы принять ( некоторым ученикам, возможно, придется жить с 3), постепенно опускаются до тех пор, пока не будет найдено решение. Если мы застряли, сбросьте настройки, снизьте ожидания и попробуйте еще раз. Задачи A и B назначаются первыми, а C выполняется только после того, как A и B завершены, потому что ограничения на C гораздо менее строгие.

Я также использовал весовой коэффициент, чтобы смоделировать компромисс между максимальным удовлетворением учащихся и соблюдением ограничений на количество презентаций в день.

В настоящее время он, кажется, находит решение практически для каждого случайно сгенерированного набора предпочтений. Я включил метрику оценки; соотношение между суммой значений предпочтений всех назначенных комбинаций ученик / свидание и суммой всех идеальных / трех лучших значений предпочтений ученика. Например, если у ученика X в списке две пятерки, одна четверка и остальные тройки, и ему назначена одна из своих пятерок и две тройки, он получит 5 + 3 + 3 = 11, но в идеале мог бы получить 5 + 5 + 4 = 14; он удовлетворен на 11/14 = 78,6%.

После некоторого тестирования кажется, что моя реализация имеет тенденцию обеспечивать в среднем около 95% удовлетворенности студентов, что намного лучше, чем я ожидал :) Но опять же, это с поддельными данными. Реальные предпочтения, вероятно, более сложны, и удовлетворить их труднее.

Ниже приведено ядро ​​алгоритма. Полный сценарий составляет ~ 250 строк и, я думаю, слишком длинный для этого. Проверьте это на Github.

...

# Assign a date for a given task to each student, 
# preferring a date that they like and is still free.
def fill(task, lowest_acceptable, spread_weight=0.1, tasks_to_spread="ABC"):
        random_order = range(nStudents) # randomize student order, so everyone
        random.shuffle(random_order)    # has an equal chance to get their first pick
        for i in random_order:
            student = students[i]
            if student.dates[task]: # student is already assigned for this task?
                continue

            # get available dates ordered by preference and how fully booked they are
            preferred = get_favorite_day(student, lowest_acceptable,  
                                         spread_weight, tasks_to_spread)
            for date_nr in preferred:
                date = dates[date_nr]
                if date.is_available(task, student.count, lowest_acceptable == 1):
                    date.set_student(task, student.count)
                    student.dates[task] = date
                    break

# attempt to "fill()" the schedule while gradually lowering expectations
start_at = 5
while start_at > 1:
    lowest_acceptable = start_at
    while lowest_acceptable > 0:
        fill("A", lowest_acceptable, spread_weight, "AAB")
        fill("B", lowest_acceptable, spread_weight, "ABB")
        if lowest_acceptable == 1:
            fill("C", lowest_acceptable, spread_weight_C, "C")
        lowest_acceptable -= 1

А вот пример результата, напечатанного сценарием:

                                      Date                                      
================================================================================
Student |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |  10 |  11 |  12 |
================================================================================
   1    |     |  A  |  B  |     |  C  |     |     |     |     |     |     |     |
   2    |     |     |     |     |  A  |     |     |     |     |  B  |  C  |     |
   3    |     |     |     |     |  B  |     |     |  C  |     |  A  |     |     |
   4    |     |     |     |  A  |     |  C  |     |     |     |     |     |  B  |
   5    |     |     |  C  |     |     |     |  A  |  B  |     |     |     |     |
   6    |     |  C  |     |     |     |     |     |     |  A  |  B  |     |     |
   7    |     |     |  C  |     |     |     |     |  B  |     |     |     |  A  |
   8    |     |     |  A  |     |  C  |     |  B  |     |     |     |     |     |
   9    |  C  |     |     |     |     |     |     |     |  A  |     |     |  B  |
   10   |  A  |  B  |     |     |     |     |     |     |  C  |     |     |     |
   11   |  B  |     |     |  A  |     |  C  |     |     |     |     |     |     |
   12   |     |     |     |     |     |  A  |  C  |     |     |     |  B  |     |
   13   |  A  |     |     |  B  |     |     |     |     |     |     |     |  C  |
   14   |     |     |     |     |  B  |     |     |     |  C  |     |  A  |     |
   15   |     |     |  A  |  C  |     |  B  |     |     |     |     |     |     |
   16   |     |     |     |     |     |  A  |     |     |     |  C  |  B  |     |
   17   |     |  A  |     |  C  |     |     |  B  |     |     |     |     |     |
   18   |     |     |     |     |     |     |  C  |  A  |  B  |     |     |     |
================================================================================
Total student satisfaction: 250/261 = 95.00%
person Junuxx    schedule 27.01.2016
comment
Вау, я не ожидал такого уровня участия - потрясающе! Думаю, мне придется некоторое время его изучить, чтобы понять. Вы получите чек в любом случае, но, если хотите, у меня есть пара последующих действий. Во-первых, чтобы использовать мои фактические данные, правильно ли я понимаю, что мне просто нужен упорядоченный список их предпочтений для загрузки с помощью student.prefs = ordered_list? Кроме того, похоже, что Student.count - это идентификатор для каждого экземпляра класса Student? (извините, если это наивный вопрос, я мало работал с классами. (продолжение ниже) - person kevbonham; 27.01.2016
comment
Наконец (извините, если это было непонятно), каждому студенту действительно нужно провести C презентацию 3 раза. Поскольку ограничения намного ниже, мне будет тривиально заполнить его вручную, поэтому, пожалуйста, не прилагайте много усилий для исправления. Я не совсем понимаю центральную функцию, но в строке 202 в сущности не могли бы вы позвонить fill("C", lowest_acceptable, spread_weight_C, "CCC") или позвонить на линию 202 3 раза? На самом деле, я думаю, что у меня проблемы с get_favorite_day функцией. Тем не менее, спасибо за ваши усилия - я рад, что вам это понравилось! - person kevbonham; 27.01.2016
comment
Пожалуйста. По моему опыту, StackOverflow - это то, что нужно, когда у вас есть очень конкретный вопрос. Но это все равно может быть вашим лучшим выбором. В любом случае, вы совершенно правы насчет student.prefs, их рейтингового предпочтения для каждого из 12 дней по порядку. И да, Student.count, переменная класса, увеличивается на единицу при каждом вызове конструктора, так что self.count, переменная экземпляра, может быть установлена ​​на правильный номер. - person Junuxx; 27.01.2016
comment
Что касается 3 C, самый простой способ добавить их - просто иметь задачи C1, C2, C3 и решать их индивидуально. Я соответственно обновил суть. Переменная tasks_to_spread в fill и get_favorite_day определяет, насколько она позволяет избежать добавления задач в дни, в которых уже есть задачи этих типов. AAB означает, что он дисконтирует ранее существовавшие A в два раза больше, чем B. Так что CCC на самом деле не имеет значения: я полагаю, мог бы быть более ясным / менее неуклюжим :) Теперь мне интересно, что именно означают A, B и C? - person Junuxx; 27.01.2016
comment
Я понятия не имел, что это будет связано, чтобы сказать вам правду. Этот курс представляет собой обзор научной литературы для выпускников, связанный с серией семинаров. A и B - это презентации по основам и основному содержанию документов соответственно. C - это задание, в котором учащиеся должны разработать и представить новые эксперименты, чтобы расширить работу статьи, которую мы читаем. - person kevbonham; 28.01.2016
comment
к сведению - я обновил свою версию сути, включив в нее код для импорта csv значений опроса. . Вызвал скрипт из командной строки python2 class_projects.py /path/to/survey_results.csv. - person kevbonham; 28.01.2016
comment
Просто прочтите этот пост (дискурс .julialang.org / t /) на форуме julia и вспомнил этот ответ ... До сих пор не могу поверить, сколько работы вы вложили в него! Хотел бы я дать больше очков :-D - person kevbonham; 19.05.2020