Поиск алгоритма для эффективного размещения баннеров событий календаря - PullRequest
0 голосов
/ 30 декабря 2008

Я ищу алгоритм, позволяющий эффективно размещать баннеры событий на весь день / несколько дней, так же, как в представлении месяца в Outlook или Календаре Google. У меня есть ряд событий с начальной и конечной датой, упорядоченные по возрастанию начальной (и затем конечной) даты (или любой другой порядок, который вы хотите, я собираю события из таблицы базы данных). Я хотел бы минимизировать среднее количество использованного вертикального пространства, потому что после баннеров событий мне нужно будет разместить другие события только на этот день (они всегда идут после баннеров на определенную дату). Так, например, если бы у меня было два события, одно 1 / 10-1 / 11 и одно 1 / 11-1 / 15, я бы предпочел расположить их так (каждый столбец - один день):

 bbbbb
aa

а не как:

aa
 bbbbb

потому что, когда я добавляю события только для дня (x, y и z), я могу сделать это (я бы предпочел первое, не хочу второе):

 bbbbb    vs.    aa
aa xyz            bbbbb
                    xyz

Но это не так просто, как сначала разместить более длинные события, потому что с 1 / 10-1 / 11, 1 / 13-1 / 14 и 1 / 11-1 / 13 я бы хотел:

aa cc
 bbb

в отличие от:

 bbb
aa cc

потому что это учитывает события x и y:

aa cc    vs.     bbb
xbbby           aa cc
                x   y

И, конечно, я бы предпочел сделать это за один проход. Для структуры данных в настоящее время я использую карту от даты к списку, где для каждого дня события я добавляю событие в соответствующий список. Таким образом, трехдневное событие появляется в трех списках, каждый под одним из дней на карте. Это удобная структура для преобразования результата в визуальный вывод, но я открыт и для других структур данных. В настоящее время я использую жадный алгоритм, где я просто добавляю каждое событие по порядку, но это может привести к нежелательным артефактам, таким как:

aa ccc          
 bbbbb
    dd
     eeeeeeeeeeeeeeeee

Это тратит лот места для большинства дней "е".

Есть идеи?

Ответы [ 2 ]

6 голосов
/ 01 января 2009

Вот высокоуровневый эскиз одного из возможных решений (с использованием целых чисел дня недели вместо полноценных дат). Этот интерфейс:

public interface IEvent {

    public abstract int getFirst();  // first day of event
    public abstract int getLast();   // last day of event
    public abstract int getLength(); // total number of days
    public abstract char getLabel(); // one-char identifier

    // true if this and that have NO days in common
    public abstract boolean isCompatible(IEvent that);

    // true if this is is compatible with all events
    public abstract boolean isCompatibleWith(Collection<IEvent> events);

}

должен быть реализован для использования алгоритма, выраженного в методе layout ниже.

Кроме того, конкретный класс должен реализовать Comparable, чтобы создать естественный порядок, в котором более длинные события предшествуют более коротким. (В моем примере реализации для демонстрации ниже использовался порядок убывания длины, затем восходящая дата начала, затем восходящая метка.)

Метод layout принимает коллекцию IEvent экземпляров и возвращает Map, который присваивает каждой строке в презентации набор событий, которые могут быть показаны в этой строке.

public Map<Integer,Set<IEvent>> layout(Collection<IEvent> events) {
    Set<IEvent> remainingEvents = new TreeSet<IEvent>(events);
    Map<Integer,Set<IEvent>> result = new TreeMap<Integer,Set<IEvent>>();
    int day = 0;
    while (0 < remainingEvents.size()) {
        Set<IEvent> dayEvents = new TreeSet<IEvent>();
        for(IEvent e : remainingEvents) {
            if (e.isCompatibleWith(dayEvents)) {
                dayEvents.add(e);
            }
        }
        remainingEvents.removeAll(dayEvents);
        result.put(day, dayEvents);
        ++day;
    }
    return result;
}

Каждая строка состоит из выбора самого длинного оставшегося события и постепенного выбора всех дополнительных событий (в порядке, описанном выше), которые совместимы с ранее выбранными событиями для текущей строки. В результате все события «всплывают» как можно дальше вверх без столкновения.

Следующая демонстрация показывает два сценария в вашем вопросе вместе со случайно созданным набором событий.

Event collection:
    x(1):4
    b(5):2..6
    y(1):5
    a(2):1..2
    z(1):6
Result of layout:
    0 -> {b(5):2..6}
    1 -> {a(2):1..2, x(1):4, y(1):5, z(1):6}
Visual presentation:
      bbbbb
     aa xyz

Event collection:
    x(1):1
    b(3):2..4
    a(2):1..2
    c(2):4..5
    y(1):5
Result of layout:
    0 -> {b(3):2..4, x(1):1, y(1):5}
    1 -> {a(2):1..2, c(2):4..5}
Visual presentation:
     xbbby 
     aa cc 

Event collection:
    f(2):1..2
    h(2):1..2
    d(4):1..4
    e(4):2..5
    c(1):6
    a(2):5..6
    g(4):2..5
    b(2):0..1
Result of layout:
    0 -> {d(4):1..4, a(2):5..6}
    1 -> {e(4):2..5, b(2):0..1, c(1):6}
    2 -> {g(4):2..5}
    3 -> {f(2):1..2}
    4 -> {h(2):1..2}
Visual presentation:
     ddddaa
    bbeeeec
      gggg 
     ff    
     hh    
0 голосов
/ 30 декабря 2008

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

Например, организуйте данные в строки, которые вам понадобятся на определенный день, и организуйте события наилучшим из возможных способов, начиная с самых длинных событий (их не нужно сначала отображать, но они нужны быть организованным в первую очередь) и переходя к самым коротким событиям. Это позволит вам визуализировать выходные данные соответственно, не тратя впустую пространство и избегая тех дней «е». Кроме того, тогда:

 bbb
aa cc

или

aa cc
 bbb

не имеет значения, потому что x и y всегда могут идти по обе стороны от bbb или даже между aa и cc

Надеюсь, вы найдете это полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...