Algorytm łączenia okresów

Algorytm łączenia okresów
WP
  • Rejestracja:prawie 7 lat
  • Ostatnio:16 dni
  • Postów:145
0

Mam takie ciekawe zagadnienie:
Mam listę okresów zdefiniowanych jako:

Kopiuj
List<Period> PeriodList;

class Period
{
  public DateTime DateStart { get; set; }
  public DateTime DateStop { get; set; }
}

Chciałbym tą listę zmienić tak aby okresy łączące się ze sobą oraz pokrywające się zamienić na jeden wpis, np: 2023-01-01 - 2023-01-10 oraz 2023-01-05 - 2023-01-20 zamienić na jedną pozycję: 2023-01-01 - 2023-01-20.

Czy jest na to jakiś algorytm pozwalający oprogramować taką operację?
Próbowałem to sam zrobić ale ilość możliwych przypadków mnie przerosła.
Może ktoś spotkał się z takim zagadnieniem?

Pozdrawiam

S4
To się nazywa sychronizacja jajników.
obscurity
ponoć synchronizacja okresów to mit
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
1
WojtexProgramista napisał(a):

Próbowałem to sam zrobić ale ilość możliwych przypadków mnie przerosła.

Czyli ile ? Czy nie zrobiłeś podejścia ?

Podam przykład. W naiwym podejściu do okresów wydaje się że istnienie części wspólnej wymaga 8 porównań, ale naprawdę 2 . Ale dojście do tego wymaga oswojenia z logiką matematyczną (co niektorzy tu czują się lepsi, że teorii nie znają - nie, to nie do ciebie @WojtexProgramista )

Gogole jest pełne range intersect, range ovelap, z dodatkiem C# lub bez

Jak wiesz, czy to zachodzi, to wiesz czy jest to wykonalne (i prosty wynik) , lub niewykonalne (dwa rozdzielne i kiszka)


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:28 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0

A jakby posortować listę po DateStart ? To wtedy możne było by łatwiej?
Szkoda że nie umiem C#, musze się w końcu nauczyć. W pseudokodzie napisałbym tak

Kopiuj

import scala.math._

case class Period(start: Int, stop: Int)

def joinPeriod(list: List[Period]): List[Period] = list match {
  case Nil => Nil
  case p :: Nil => List(p)
  case p1 :: p2 :: l if p1.stop >= p2.start => joinPeriod(Period(p1.start, max(p1.stop, p2.stop)) :: l)
  case p1 :: p2 :: l => p1 :: joinPeriod(p2 :: l)
}

val sortedList: List[Period] = List(Period(1, 2), Period(2, 3), Period(5, 10), Period(7, 8), Period(11, 12), Period(11, 13))

print(joinPeriod(periodList))

Dla uproszczenia zamieniłem typ datowy na zwykłego inta


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
AK
Niepotrzebnie siłą
WeiXiao
przecież to nie pseudokod a jakaś skala
lion137
jak wyżej, pseudokod, import scala.math._ :D
lion137
ale sortowanie to dobry trop:)
lion137
na pierwszy rzut oka, O(nlogn) i pamięciowo, O(n)
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:28 minut
0

Jakiś czas temu się tym zajmowałem, już nie pamiętam dobrze algorytmu i nie mam dostępu, ale wyglądało to mniej więcej tak: Na wejściu lista okresów, które mogą się pokrywać lub mogą się dotykać, na wyjściu lista rozłącznych okresów.

Sam algorytm był mniej więcej taki (okresy wejściowe posortowane wg daty początkowej):

  1. Na początku jest pusta tablica okresów rozłącznych
  2. Dla każdego okresu wejściowego:
    1. Dla każdego okresu rozłącznego (na początku algorytmu nie ma ani jednego) sprawdzić, czy spełnia łącznie warunki:
      1. Data początkowa okresu wejściowego jest wcześniejsza od daty końcowej okresu rozłącznego.
      2. Data końcowa okresu wejściowego jest późniejsza od daty początkowej okresu rozłącznego.
    2. Jeżeli znaleziono okres spełniający powyższe warunki, to należy go rozszerzyć tak, żeby obejmował skrajne daty dotychczasowego zakresu i zakresu okresu wejściowego.
    3. W przeciwnym wypadku:
      1. Dodać nowy okres rozłączny o zakresie takim samym, jak okres wejściowy, tak, żeby okresy były w kolejności wg daty początkowej.
      2. Jeżeli to nie jest pierwszy okres i data początkowa wstawionego okresu jest o co najwyżej dzień późniejsza od daty końcowej poprzedniego okresu, zamienić te dwa okresy na jeden.
      3. Jeżeli to nie jest ostatni okres i data końcowa wstawionego okresu jest o co najwyżej dzień wcześniejsza od daty początkowej następnego okresu, zamienić te dwa okresy na jeden.
  3. Tablica okresów rozłącznych zawiera okresy rozłączne będące wynikiem działania algorytmu
edytowany 1x, ostatnio: andrzejlisek
lion137
  • Rejestracja:około 8 lat
  • Ostatnio:43 minuty
  • Postów:4928
0

Szukać: interval merging algorithm:
https://duckduckgo.com/?t=ffab&q=interval+merging+algorithm&atb=v366-1&ia=web

Jakiś tam kod, (nie jestem biegły w C#, testowane lekko na https://repl.it, koniecznie dotestuj):

Kopiuj
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<Period> periods = new List<Period>
        {
            new Period {DateStart = new DateTime(2023, 01, 01), DateStop = new DateTime(2023, 01, 10)},
            new Period {DateStart = new DateTime(2023, 01, 05), DateStop = new DateTime(2023, 01, 20)}
        };

        List<Period> mergedPeriods = MergePeriods(periods);

        foreach (Period period in mergedPeriods)
        {
            Console.WriteLine($"{period.DateStart} - {period.DateStop}");
        }
    }

    static List<Period> MergePeriods(List<Period> periods)
    {
        periods = periods.OrderBy(p => p.DateStart).ToList();
        List<Period> result = new List<Period>();

        foreach (Period period in periods)
        {
            if (!result.Any())
            {
                result.Add(period);
            }
            else
            {
                Period last = result.Last();
                if (last.DateStop < period.DateStart)
                {
                    result.Add(period);
                }
                else if (last.DateStop < period.DateStop)
                {
                    last.DateStop = period.DateStop;
                }
            }
        }
        return result;
    }
}

class Period
{
    public DateTime DateStart { get; set; }
    public DateTime DateStop { get; set; }
}

JD
  • Rejestracja:około 19 lat
  • Ostatnio:około 10 godzin
Kubuś Puchatek
  • Rejestracja:ponad 7 lat
  • Ostatnio:5 miesięcy
  • Postów:235
0

Jeśli chodzi o czas to polecam https://nodatime.org/ tutaj pewnie twój problem został rozwiązany :)


Lubię miodek :)
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
2
Kubuś Puchatek napisał(a):

Jeśli chodzi o czas to polecam https://nodatime.org/ tutaj pewnie twój problem został rozwiązany :)

Jeden z fajnych projektów, którego nazwę jakby już słyszałem ;) , tylko nie miała N na początku. Dobre wzory NALEŻY kopiować


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
Kubuś Puchatek
Nawet nie wiedziałem że Java ma taki sam odpowiednik xD
AK
Wrecz przeciwnie. Joda Time ratowało dupę wobec fatalnego spieprzeniea (pierwszego)fabrycznego pakietu datowo-czasowego (mutowalne, setery, totalny miszmasz w koncepcjach, rok od 1900 ???, mc od 0 a dzień o 1 (chyba) ). W oficjalnej linii java 8 to zrealizowała na nowo, dobrze (a "przy okazji" bardzo podobnie do j/w)
AK
C# będący Javą 2.0 o 10 lat młodszą nigdy nie było na tym etapie, pominęli fatalne błędy młodości. Nodatime jest miłym zestawem utility, z ideami z tamtego, ale nie musiał niczyjej d ratować, zastępować klas fabrycznych itd...

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.