Алгоритм сортировки для проблемы сортировки без сравнения?
В настоящее время я столкнулся с трудной проблемой сортировки. У меня есть набор событий, которые нужно сортировать друг против друга (сортировка сравнения) и против их относительного положения в списке.
В простейших терминах есть список событий, каждый из которых имеет приоритет (целое число), продолжительность (секунды) и самое раннее время появления события в списке. Мне нужно отсортировать события на основе приоритета, но ни одно событие не может появиться в списке до самого раннего времени его появления. Вот пример, чтобы (надеюсь) сделать его более ясным:
// Psuedo C# code
class Event { int priority; double duration; double earliestTime ; }
void Example()
{
Event a = new Event { priority = 1, duration = 4.0, earliestTime = 0.0 };
Event b = new Event { priority = 2, duration = 5.0, earliestTime = 6.0 };
Event c = new Event { priority = 3, duration = 3.0, earliestTime = 0.0 };
Event d = new Event { priority = 4, duration = 2.0, earliestTime = 0.0 };
// assume list starts at 0.0 seconds
List<Event> results = Sort( new List<Event> { a, b, c, d } );
assert( results[ 0 ] == a ); // 4.0 seconds elapsed
assert( results[ 1 ] == c ); // 7.0 seconds elapsed
assert( results[ 2 ] == b ); // 12.0 seconds elapsed
assert( results[ 3 ] == d ); // 14.0 seconds elapsed
}
Элемент "b" должен быть последним, потому что ему не разрешено начинать до 6,0 секунд в списке, поэтому он отложен, а "c" переходит до "b", хотя его приоритет ниже. (Надеюсь, это объясняет мою проблему, если не сообщите мне, и я ее отредактирую.)
Моя нынешняя идея - использовать inserting sort для управления процессом сортировки. В отличие от многих других общих алгоритмов сортировки, сортировка вставки решает порядок списка по одному за раз и по порядку. Поэтому для каждого индекса я должен иметь возможность найти следующее событие с наименьшим приоритетом, чье раннее время выполнения будет выполнено.
Я надеюсь найти ресурсы по сортировке алгоритмов и структур данных, чтобы помочь мне создать хорошее решение для этой "сортировки" проблемы. Моя реальная проблема на самом деле сложнее: иерархическая сортировка, переменные буферы между событиями, множественные непостоянные временные ограничения, поэтому чем больше информации или идей, тем лучше. Скорость и пространство на самом деле не вызывает беспокойства. Точность в сортировке и ремонтопригодности кода вызывает озабоченность.
Изменить: Разъяснения (на основе комментариев)
- События потребляют всю свою продолжительность (то есть нет перекрытия разрешенных событий).
- События должны встречаться с ранним временем или после них, они не могут произойти до их раннего времени.
- События могут происходить позже, чем их раннее время, если существуют события с более низким приоритетом.
- События не могут быть прерваны
- Максимальная продолжительность - это сумма всех событий, которые могут вписываться в список. Это не показано выше. (В действительности продолжительность всех событий будет намного больше, чем максимальная продолжительность списка времени.)
- Не может быть пробелов. (Нет никаких отверстий для повторного заполнения.)
Изменить: Ответ
В то время как Дэвид Нехме дал ответ, который я выбрал, я хотел бы указать, что его ответ - это вставка, и несколько других людей предоставили вставки типа типа ответов. Это подтверждает мне, что специализированный сортировка вставки - это, вероятно, путь. Спасибо всем вам за ваши ответы.
Ответы
Ответ 1
На самом деле это больше, чем проблема сортировки. Это проблема с одиночной машиной с датами выпуска. В зависимости от того, что вы пытаетесь сделать, проблема может быть NP-Hard. Например, если вы пытаетесь имитировать взвешенную сумму времени завершения (вес обратно пропорционален приоритету), проблема заключается в classized as
1|ri;pmtn|Σ wiCi
и NP-жесткий. Есть многочисленные статьи по этой теме, но это может быть больше, чем вам нужно.
В вашем случае вы никогда не хотите решения с пробелами, поэтому вам может потребоваться простое моделирование дискретных событий (время O (n log (n))). Вам необходимо сохранить выпущенные_jobs в качестве очереди приоритетов.
unreleased_jobs = jobs // sorted list of jobs, by release date
released_jobs = {} // priority queue of jobs, by priority
scheduled_jobs = {} // simple list
while (!unreleased_jobs.empty() || !released_jobs.empty()) {
while (unreleased_jobs.top().earliestTime <= t) {
released_jobs.push(unreleased_jobs.pop())
}
if (!released_jobs.empty()) {
next_job = released_jobs.pop();
scheduled_jobs.push_back(next_job)
t = t + next_job.duration
} else {
// we have a gap
t = unreleased_jobs.top().earliestTime
}
}
Одна из проблем заключается в том, что у вас может быть низкоприоритетное задание с временем освобождения непосредственно перед короткой высокоприоритетной задачей, но оно создаст расписание с тем свойством, что пробелов нет (если расписание без пробелов возможно).
Ответ 2
Другими словами, вы хотите оптимизировать общее время работы при формулировании двух ограничений (сильная: самая ранняя точка выполнения, слабый: приоритет)? Это называется проблемой проблема ограничения ограничений. Существуют специальные решатели для такого рода проблем.
Кстати, решение jakber не работает. Даже без длительности, очевидно, следующий пример:
event a (priority = 1, start = 5)
event b (priority = 2, start = 0)
Сортированная последовательность будет a
, b
, а желаемый результат - b
, a
.
Ответ 3
Я думаю:
- Сортировка задач по приоритету
- Задайте задачи в строку времени, взяв первый доступный слот после их раннего времени, у которого есть отверстие, достаточно большое для задачи.
Преобразуйте строку времени в список задач и подождите (для пробелов).
Вопросы:
- Разрешены ли пробелы?
- Можно ли разделять задачи?
- Учитывая задачи, как в вопросе: лучше ли задержать b для завершения c, или d, чтобы b мог начать вовремя?
Edit:
О, ответы на мои вопросы:
- Нет (ish - если нечего запускать, я думаю, у нас может быть разрыв)
- Нет
- Все еще не понятно, но я предполагаю, что пример предлагает запустить c и задержать b.
В этом случае алгоритм может быть:
- Сортировка по приоритету
- Держите счетчик для текущего "времени", начиная с t = 0
- Найдите отсортированный список для элемента с наивысшим приоритетом, который можно запустить с помощью t.
- Добавьте элемент в выполняемый порядок и добавьте его продолжительность в t.
- Повторите 3 и 4 до тех пор, пока список не будет исчерпан. Если в t нет задач, выполняемых при t, а задачи остаются незавершенными, вставьте оставшуюся 1-секундную задачу ожидания в текущем порядке.
Этот алгоритм также O (n ^ 2).
Ответ 4
Я думаю, вам нужно отсортировать список дважды: сначала по приоритету, а затем по раннему времени, используя любой алгоритм сортировки стабильный, например, сортировка вставки. Таким образом, время будет увеличиваться, и каждый раз все будет сортироваться по приоритету.
Если вы ничего не видите, я не могу полностью игнорировать продолжительность каждого события с целью сортировки.
http://en.wikipedia.org/wiki/Category:Stable_sorts
Ответ 5
Похоже, вы действительно хотите сортировку на основе сравнения. Ваш ключ сортировки - {earlyliestTime, priority}, в этом порядке. Поскольку ваш пример псевдо-С#, я дам вам псевдо-С# решение:
class Event : IComparable<Event>, IComparable{
int priority;
double duration;
double earliestTime;
public int CompareTo(Event other){
if(other == null)
return 1; /* define: non-null > null */
int cmp = earliestTime.CompareTo(other.earliestTime);
if(cmp != 0)
return cmp;
/* earliestTimes were equal, so move on to next comparison */
return priority.CompareTo(other.priority);
}
int IComparable.CompareTo(object other){ /* for compatibility with non-generic collections */
if(other == null)
return 1; /* define: non-null > null */
Event e_other = other as Event;
if(e_other == null) /* must have been some other type */
throw new ArgumentException("Must be an Event", "other");
return CompareTo(e_other); /* forward to strongly-typed implementation */
}
}
Теперь ваш список будет сортироваться так, как ожидали ваши утверждения.
ИЗМЕНИТЬ
Моя первоначальная презумпция заключалась в том, что события будут удалены из списка и переданы в отдельный поток, так что диспетчер очереди мог бы отключить следующее событие вовремя, но из комментариев, которые я получил, у меня появилась идея, что, возможно, подход, который был однопоточным, но все же позволял более высокоприоритетным событиям срабатывать как можно ближе к их времени начала, было более желательным. В этом случае функция CompareTo
должна изменяться следующим образом:
public int CompareTo(Event other){
if(other == null)
return 1; /* define: non-null > null */
int cmp = priority.CompareTo(other.priority);
if(cmp == 0)
/*
* calculate and compare the time each event will be late
* if the other one were to start first. This time may be
* negative if starting one will not make the other one late
*/
return (earliestTime + duration - other.earliestTime).CompareTo(
other.earliestTime + other.duration - earliestTime);
/*
* they're different priorities. if the lower-priority event
* (presume that greater priority index means lower priority,
* e.g. priority 4 is "lower" priority than priority 1), would
* would make the higher-priority event late, then order the
* higher-priority one first. Otherwise, just order them by
* earliestTime.
*/
if(cmp < 0){/* this one is higher priority */
if(earliestTime <= other.earliestTime)
/* this one must start first */
return -1;
if(other.earliestTime + other.duration <= earliestTime)
/* the lower-priority event would not make this one late */
return 1;
return -1;
}
/* this one is lower priority */
if(other.earliestTime <= earliestTime)
/* the other one must start first */
return 1;
if(earliestTime + duration <= other.earliestTime)
/* this event will not make the higher-priority one late */
return -1;
return 1;
}
Проверьте это на любые предположения, но я думаю, что это то, что мы ищем.
Ответ 6
Если у вас ограниченный набор уровней приоритета, вы можете сохранить набор отсортированных по времени списков, по 1 для каждого уровня. Всякий раз, когда вам нужно следующее событие, проверьте главу каждого списка в порядке приоритета, пока не найдете тот, время начала которого прошло. (Отслеживайте минимальное время начала, пока вы не проверяете, пока событие еще не готово, вы знаете, какой из них ждать)
Ответ 7
Звучит как проблема, с которой я столкнулся на днях, на который был дан ответ здесь.
Предполагая, что вы используете С#...
Ответ 8
Кстати, в самом общем случае не может быть никакого решения (если не разрешены пробелы, как указал Дуглас). Например:
Event a = new Event { priority = 1, duration = 1.0, earliestTime = 4.0 };
Event b = new Event { priority = 2, duration = 1.0, earliestTime = 4.0 };
Event c = new Event { priority = 3, duration = 1.0, earliestTime = 4.0 };
Event d = new Event { priority = 4, duration = 1.0, earliestTime = 4.0 };
Ответ 9
Я не совсем уверен, что понимаю тонкости вашей проблемы, но мой инстинкт подсказывает мне, что вам нужно определить взаимосвязь между приоритетом и временем начала. Пример:
Event a = new Event { priority = 1, duration = 4.0, earliestTime = 1.0 };
Event b = new Event { priority = 2, duration = 5.0, earliestTime = 0.0 };
Итак, продолжаем ли мы и начинаем b
во время = 0, или мы ждем отметки, а затем запустим a
, потому что это более высокий приоритет? Предположим, что было больше событий с большим количеством приоритетов и более длительных компромиссов. Я думаю, вам нужно правило по строкам "если следующее событие имеет более высокий приоритет X, а разрыв (между настоящим и самым ранним временем) меньше, чем Y секунд, ждать, а затем запускать событие с более высоким приоритетом, иначе начинать низкий приоритет событие (таким образом, отбрасывает высокоприоритетный)".
Ответ 10
Вот несколько кодов Python в соответствии с ответами Дугласа. Сначала мы сортируем по приоритету, затем мы устанавливаем временную шкалу в порядке сортировки:
#!/usr/bin/env python
MIN_PRIORITY = 100
class Event(object):
def __init__(self, name, priority, duration, earliestTime):
self.name = name
self.priority = priority
self.duration = duration
self.earliestTime = earliestTime
def __str__(self):
return "%-10s: P %3d D %3.1f T %3.1f" % (self.name, self.priority, self.duration, self.earliestTime)
def sortEvents(_events):
def comparePriority(event1, event2):
if event1.priority < event2.priority: return -1
if event1.priority > event2.priority: return 1
return 0
# Get a copy of the events and sort by priority
events = [e for e in _events]
events.sort(cmp=comparePriority)
# Select one event at a time, checking for compatibility with elapsed time
elapsedTime = 0.0
sortedEvents = []
while events:
minGap = events[0].earliestTime - elapsedTime
for e in events:
currentGap = e.earliestTime - elapsedTime
if currentGap < minGap:
minGap = currentGap
if currentGap <= 0.0:
sortedEvents.append(e)
elapsedTime += e.duration
events.remove(e)
break
# If none of the events fits, add a suitable gap
if minGap > 0:
sortedEvents.append( Event("gap", MIN_PRIORITY, minGap, elapsedTime) )
elapsedTime += minGap
return sortedEvents
if __name__ == "__main__":
#e1 = Event("event1", 1, 1.0, 4.0)
#e2 = Event("event2", 2, 1.0, 6.0)
#e3 = Event("event3", 3, 1.0, 8.0)
#e4 = Event("event4", 4, 1.0, 10.0)
e1 = Event("event1", 1, 4.0, 0.0)
e2 = Event("event2", 2, 5.0, 6.0)
e3 = Event("event3", 3, 3.0, 0.0)
e4 = Event("event4", 4, 2.0, 0.0)
events = [e1, e2, e3, e4]
print "Before:"
for event in events: print event
sortedEvents = sortEvents(events)
print "\nAfter:"
for event in sortedEvents: print event