Brauche Hilfe beim Rechnen...

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Antworten
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

nachdem ich neben den Grundrechenarten gerade mal noch einen Dreisatz aufstellen kann, stehe ich momentan am Ende meiner Möglichkeiten... :?

Ich suche nach einem Weg, um die Distanz zwischen ``(year, month)`` zur nächst größeren ``(year, month)`` Einheit innerhalb einer Serie zu erhalten. Hier ein konkretes Beispiel: Ein Termin wiederholt sich alle 3 Monate. Der Termin selbst beginnt ``(2010, 6)``. Somit ergibt sich folgende Serie:

Code: Alles auswählen

(2010, 9), (2010, 12), (2011, 3), (2011, 6), (2011, 9) ...
Wenn ich nun die Distanz von ``(2010, 4)`` zur nächsten Wiederholung möchte, geht das noch recht einfach:

Code: Alles auswählen

(2010, 9) - (2010, 4) = 4 Monate
Suche ich die Distanz von ``(2011, 5)`` zur nächsten Wiederholung, schaut mir meine Lösung schon recht abstrus aus:

Code: Alles auswählen

(2010, 9) - (2011, 5) = -8 Monate
Interval 3 - 8 Monate = -5 Monate
Interval 3 - 5 Monate = -2 Monate
Interval 3 - 2 Monate = 1 Monat
Ich habe ein paar Möglichkeiten getestet, die Lösung scheint zu funktionieren, es würde mich aber doch wundern, wenn es für dieses "Herunterbrechen zum ersten positiven Ergebnis oder 0" nicht eine Standardformel gäbe...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Hm, ich bin bei 9 - 4 = 4 ausgestiegen. Das danach ist mir völlig unklar. Was willst Du tun? Und wieso sind die Distanzen plötzlich 4 (bzw. 5) und 8 Monate, wenn es doch vorab definiert ist, dass sich der Termin alle 3 Monate wiederholt? Und was ist das mit Interval 3 - 8 Monate usw.?
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@mutetella: So ganz verstehe ich auch nicht, was du möchtest, aber vielleicht suchst du ja divmod():

Code: Alles auswählen

>>> start = 2010 * 12 + 9
>>> end = 2011 * 12 + 5
>>> end - start
8
>>> divmod(end - start, 12)
(0, 8)
In specifications, Murphy's Law supersedes Ohm's.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Hm, vielleicht möchtest Du ja wissen, wie lang es noch bis zum nächsten Termin ist. Dann würde ich ad-hoc folgendes vorschlagen:
Du weißt, die Termine sind immer in den Monaten 3, 6, 9 und 12.
Nimm den aktuellen Monat, also z.B. 4.
Wirf alles aus der Monatsliste raus, was kleiner 4 ist und nimm dann den kleinsten (hier:6) und berechne die Differenz (6 - 4 = 2 Monate bis zum nächsten Termin).
Aber Achtung: das funktioniert so nur, weil der letzte Termin im Dezember ist und es somit keinen Monat größer als diesen geben kann.
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

N317V hat geschrieben:Hm, ich bin bei 9 - 4 = 4 ausgestiegen.
Ok, ich revidiere die Behauptung, ich würde die Grundrechenarten beherrschen... :mrgreen: Nochmals:
Ich benötige die Distanz von einem Monat zum nächst größeren Monat innerhalb einer Wiederholungsserie, die ich aber nicht jedes mal als lookup-Liste erstellen möchte. Wenn also in dieser Serie z. B. der September (9) vorkommt und ich die Distanz vom vorausgehenden April (4) dorthin suche, subtrahiere ich den April vom September und komme auf mein Ergebnis, natürlich nicht 4 sondern 5 Monate :wink: .
Wenn der Termin, dessen Distanz zur ersten Wiederholung ich suche, sich vor der ersten Wiederholung befindet, ist alles kein Problem. Befindet er sich allerdings danach, wird es haariger. Am besten nochmals Beispiele, basierend auf:

Termin: (2010, 6) - Wiederholungsinterval: 3 Monate - Erste Wiederholung: (2010, 9)

Beispiel 1: Von (2010, 10) zur nächsten Wiederholung:

Code: Alles auswählen

(2010, 9) - (2010, 10) = -1 Monat = negatives Ergebnis, also:
Interval 3 Monate - 1 Monat = 2 Monate = positives Ergebnis, also:
(2010, 10) + 2 Monate = (2010, 12) <- nächste Wiederholung nach (2010, 10)
Beispiel 2: Von (2011, 5) zur nächsten Wiederholung:

Code: Alles auswählen

(2010, 9) - (2011, 5) = -8 Monate = negatives Ergebnis, also:
Interval 3 Monate - 8 Monate = -5 Monate = negatives Ergebnis, also:
Interval 3 Monate - 5 Monate = -2 Monate = negatives Ergebnis, also:
Interval 3 Monate - 2 Monate = 1 Monat = positives Ergebnis, also
(2011, 5) + 1 Monat = (2011, 6) <- nächste Wiederholung nach (2011, 5)
Ich hoffe, es wird etwas verständlicher, was ich suche...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Vielleicht einfach mal das, was ich bisher habe:

Code: Alles auswählen

def month_distance(left, right):
    l_months, r_months = (
        (left[0] * 12) + left[1], (right[0] * 12) + right[1]
    )
    return l_months - r_months

def add_months(year, month, months):
    y, month = divmod(month + months, 12)
    if not month:
        month = 12
        y -= 1
    return year + y, month

def first_interval():
        first_interval = month_distance(
            add_months(self.base.begin.year, self.base.begin.month, self.interval),
            (scope.begin.year, scope.begin.month)
        )
        while first_interval < 0:
            first_interval = self.interval - abs(first_interval)
`self` ist die Wiederholung, `scope` der Zeitraum, innerhalb dessen ich die erste auftretende Wiederholung suche.

Letztlich suche ich eine "mathematischere" Lösung für die ``while``-Schleife.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
lunar

N317V hat geschrieben:Hm, vielleicht möchtest Du ja wissen, wie lang es noch bis zum nächsten Termin ist. Dann würde ich ad-hoc folgendes vorschlagen:
Du weißt, die Termine sind immer in den Monaten 3, 6, 9 und 12.
Nimm den aktuellen Monat, also z.B. 4.
Wirf alles aus der Monatsliste raus, was kleiner 4 ist und nimm dann den kleinsten (hier:6) und berechne die Differenz (6 - 4 = 2 Monate bis zum nächsten Termin).
Aber Achtung: das funktioniert so nur, weil der letzte Termin im Dezember ist und es somit keinen Monat größer als diesen geben kann.

Das geht nur, wenn das Wiederholungsinterval Teiler oder Vielfaches von 12 ist, weil die Wiederholungen nur in diesen Fällen regulär sind.

Bei anderen Intervallen, z.B. 5 hängt der Abstand vom aktuellen Monat zum nächsten wiederholten Termin aber davon ab wie viele Jahre seid dem ersten Termin vergangen sind, da sich die Monate, in denen eine Wiederholung stattfindet, in jedem Jahr verschieben.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Das war ja auch ad-hoc, lunar. Andere Fälle erst in der nächsten Iteration. Wenns neue Tests gibt. ;-)

@mutadella: Was spricht denn jetzt gegen divmod() oder den Modulo-Operator?
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
lunar

N317V hat geschrieben:Das war ja auch ad-hoc, lunar. Andere Fälle erst in der nächsten Iteration. Wenns neue Tests gibt. ;-)
Test driven development at its worst, oder was? ;)

@mutetella Ich würds ja einfach “ausiterieren”. Was spricht schon dagegen einfach alle Wiederholung auszurechen bis zur ersten, die nach dem gegebenen Datum liegt, und dann die Differenz zu berechnen.

Mit einem endlosen Generator und einem Generatorausdruck lässt sich das imho sehr schon und elegant ausdrücken. Ist vielleicht nicht mathematisch “schön”, aber ohne viel Nachdenken verständlich, und um die Effizienz brauchst Du Dir auch keine Sorgen machen. Selbst wenn ein Termin sich 20 Jahre lang monatlich wiederholt, sind das gerade mal 240 Werte.

@mutadella: Was spricht denn jetzt gegen divmod() oder den Modulo-Operator?[/quote]
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

>>> def to_months(date):
...     return 12*date[0] + date[1]
... 
>>> def delta(reference, interval, test):
...     return (to_months(reference) - to_months(test)) % interval
... 
>>> delta((2010, 9), 3, (2011, 5))
1
>>> delta((2010, 9), 3, (2010, 4))
2
>>> delta((2010, 9), 3, (2011, 5))
1
In "reference" kommt das erste Datum des Intervalls, "interval" ist der Abstand zwischen zwei Terminen und in "test" kommt das zu testende Datum. Als Ergebnis erhältst du die Anzahl der Monate, welche zu auf das test-Datum addieren musst.
Das Leben ist wie ein Tennisball.
BlackJack

Man könnte auch einfach mal dateutil verwenden statt das Rad neu zu erfinden. :-)

Code: Alles auswählen

In [216]: from dateutil import rrule, relativedelta

In [217]: r = rrule.rrule(dtstart=datetime(2010, 9, 1), freq=rrule.MONTHLY, interval=3)

In [218]: d = datetime(2010, 4, 1)

In [219]: r.after(d)
Out[219]: datetime.datetime(2010, 9, 1, 0, 0)

In [220]: rd = relativedelta.relativedelta(r.after(d), d)

In [221]: rd
Out[221]: relativedelta(months=+5)

In [222]: rd.months
Out[222]: 5

In [223]: d = datetime(2011, 5, 1)

In [224]: rd = relativedelta.relativedelta(r.after(d), d)

In [225]: rd.months
Out[225]: 1
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@EyDu
Danke für die Lösung... Mein Problem (deshalb auch diese ``while`` Schleife) war, dass ich auch Werte verarbeiten wollte, die weit vor `reference` liegen und... egal, so passt's!

@BlackJack
`dateutil` ist mir bekannt, mit `rrule` kann ich mich aber nicht wirklich anfreunden, zumal es bei vielen Abfragen schon ein wenig träge ist. `relativedelta` ist 'ne schöne Sache, das verwende ich sehr gerne!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten