Buscando um algoritmo para eficientemente layout de banners de eventos de calendário

StackOverflow https://stackoverflow.com/questions/400288

  •  03-07-2019
  •  | 
  •  

Pergunta

Eu estou procurando um algoritmo para colocar de forma eficiente todos os dias banners de eventos / multi-dia, bem como a vista mensal no Outlook ou Google Calendar. Eu tenho um número de eventos com um início e fim data, ordenou aumentando começar (e acabar) data (ou qualquer outra ordem, por favor, eu estou reunindo eventos de uma tabela de banco de dados). Gostaria de minimizar a quantidade média de espaço vertical usado para cima, porque depois os banners do evento vou precisar para colocar outros eventos apenas para esse dia (estes vêm sempre depois dos banners para uma determinada data). Assim, por exemplo, se eu tivesse dois eventos, um 1 / 10-1 / 11 e um 1 / 11-1 / 15, eu preferiria organizá-los assim (cada coluna há um único dia):

 bbbbb
aa

e não como:

aa
 bbbbb

porque quando eu adicionar os eventos apenas para o dia (x, y, e z), eu posso fazer isso (eu prefiro o primeiro, não quer que o segundo):

 bbbbb    vs.    aa
aa xyz            bbbbb
                    xyz

Mas não é tão simples como colocar os eventos mais longos primeiro lugar, porque com 1 / 10-1 / 11, 1 / 13-1 / 14 e 1 / 11-1 / 13, eu gostaria:

aa cc
 bbb

em oposição a:

 bbb
aa cc

porque isso permitiria eventos x e y:

aa cc    vs.     bbb
xbbby           aa cc
                x   y

E é claro que eu preferiria fazer isso em uma passagem. Para a estrutura de dados, Atualmente estou usando um mapa a partir da data de lista, onde para cada dia de um evento que eu adicionar o evento à lista correspondente. Assim, um evento de três dias aparece em três listas, cada um sob um dos dias do mapa. Esta é uma estrutura conveniente para transformar o resultado na saída visual, mas estou aberto a outras estruturas de dados também. Atualmente estou usando um algoritmo guloso, onde basta adicionar cada evento em ordem, mas que pode produzir artefatos indesejados como:

aa ccc          
 bbbbb
    dd
     eeeeeeeeeeeeeeeee

Este desperdiça muito de espaço para a maioria dos "e" dias de evento.

Todas as idéias?

Foi útil?

Solução

Aqui está um esboço de alto nível de uma solução possível (usando números inteiros dia-de-semana em vez de datas full-blown). Esta interface:

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);

}

devem ser implementadas para usar o algoritmo expressa no método layout abaixo.

Além disso, a classe concreta deve implementar Comparable para criar uma ordem natural onde os eventos mais longos preceder eventos mais curtos. (My implementação de exemplo para a demonstração abaixo usou uma ordem decrescente de comprimento, então ascendente data de início, então ascendente rótulo.)

O método layout leva uma coleção de instâncias IEvent e retorna um Map que atribui a cada linha na apresentação do conjunto de eventos que pode ser mostrado nessa linha.

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;
}

Cada linha é composta por selecionando o mais longo evento restantes e selecionando progressivamente todos os eventos adicionais (na ordem descrita acima) que são compatíveis com eventos previamente selecionados para a linha atual. O efeito é que todos os eventos "flutuar" para cima, tanto quanto possível sem colisão.

A seguir mostra demonstração os dois cenários em sua pergunta, junto com um conjunto aleatório-criado de eventos.

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    

Outras dicas

Eu acho que em uma situação como esta, você está muito melhor ter certeza que seus dados são organizados corretamente em primeiro lugar e, em seguida, tornando-o. Eu sei que você quer uma única passagem, mas acho que os resultados seriam muito melhor.

Por exemplo, organizar os dados nas linhas que você precisa ter para um determinado dia e organizar os eventos da melhor maneira possível, começando com os eventos mais longos (não precisa ser exibido pela primeira vez, mas eles precisam a ser organizada pela primeira vez) e movendo-se para baixo para os eventos mais curtos. Isso permitirá que você para tornar a sua saída de acordo não desperdiçar qualquer espaço, e evitar aquelas "e" dias de evento. Além disso, a seguir:

 bbb
aa cc

ou

aa cc
 bbb

não importará porque x e y pode sempre ir em cada lado da bbb ou mesmo entre aa e cc

Eu espero que você encontrar este útil.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top