2
$\begingroup$

I need to calculate the birthdate of an ancestor based on a list of dates and ages he had, the problem is that I stink at circular maths and rotating stuff.

I have this dictionary in python: The left side are dates and the right side after ':' are ages

traces={
'2/2/1985':4,
'27/12/1988':7,
'25/12/1990':9,
'8/5/1994':13,
'6/7/1996':15,
'12/1/2001':20
}

What I more or less get to is: Converting all the dates to unitary years and resting the years:

1981.1698630136987 + a =birthdate
1982.0602739726028 + b =birthdate
1982.054794520548 + c =birthdate
1981.4328767123288 + d =birthdate
1981.5917808219178 + e =birthdate
1981.1150684931506 + f =birthdate

-1<{a,b,c,d,e,f}<1

It defeated me. Is there any solution for this?

Edit, the key of the problem always will be:

4 =floor( 1985.1698630136987 -birthdate)
7 =floor( 1989.0602739726028 -birthdate)
9 =floor( 1991.054794520548 -birthdate)
13 =floor( 1994.4328767123288 -birthdate)
15 =floor( 1996.5917808219178 -birthdate)
20 =floor( 2001.1150684931506 -birthdate)
$\endgroup$
5
  • 1
    $\begingroup$ We can determine from the given information that this person was born on some date between 1980-12-28 and 1981-01-12. Is that "exact" enough for you? $\endgroup$
    – Dan
    Commented Sep 14, 2022 at 21:10
  • 2
    $\begingroup$ I think you would do better to turn the dates into whole numbers of days. A brute force search working backwards from 2/2/1985 will then find all the possibilities for you (and is computational perfectly feasible: you don't need to search more than 5*366 days back and the check for each day only involves a handful of arithmetic operations). There are slicker ways to do the search, but it's often instructive to start with the simplest approach first. $\endgroup$
    – Rob Arthan
    Commented Sep 14, 2022 at 21:19
  • $\begingroup$ @Dan I couldn't know my birthdate if not for you, thanks! If there wasn't an error in my input; '27/12/1988':7 but I had 8 in that date. $\endgroup$ Commented Sep 14, 2022 at 21:32
  • $\begingroup$ @RobArthan well those were my dates but is my grandpa's birthday I want to know(him's and many more) so I'd need to go 88 years back at much for each date. $\endgroup$ Commented Sep 14, 2022 at 21:35
  • 1
    $\begingroup$ Well 88*366 isn't that big, but in any case you can easily estimate to within a year or two to improve the search. $\endgroup$
    – Rob Arthan
    Commented Sep 14, 2022 at 21:45

3 Answers 3

2
$\begingroup$

(Edit: Whoops, fixed an off-by-one error.)

If you know that a person was $n$ years old at time $t$ (expressed in fractions of a year) then you know that they were born between $t - n - 1$ (maybe plus a day or so) and $t - n$ (maybe minus a day or so). So every pair $(n_i, t_i)$ of an age and a date gives you a lower bound and an upper bound on the birthdate. That means all you need to do to aggregate all the information you get from multiple pairs is to store two variables, the best lower bound so far $\ell$ and the best upper bound so far $u$, and update them whenever a new lower bound $\ell_i = t_i - n_i - 1$ or upper bound $u_i = t_i - n_i$ beats the previous old one. Formally the update rule is

$$\ell \to \text{max}(\ell, t_i - n_i - 1)$$ $$u \to \text{min}(u, t_i - n_i).$$

After going through all pairs $(n_i, t_i)$ you end up with a range $\ell \le t_{\text{birth}} \le u$ in which the birthdate can fall, and this is the best you can say given the data. If at any point this range is empty (that is, if you ever get $\ell > u$) then the data was contradictory; if it narrows down to within a day then that's great; and otherwise it's always at most a year in size.

(This is ignoring any subtleties of calendar math weirdness involving leap years and so forth, which hopefully is being taken care of by some kind of dedicated calendar math package.)

$\endgroup$
1
$\begingroup$

Since you're using Python, here's an implementation of the algorithm you want. Note that I'm using datetime.date objects for your sample data. If you have strings, you can parse them with datetime.datetime.strptime(date_string, '%d/%m/%Y').date().

import datetime

def add_years(base_date, num_years):
    '''
    Add/subtract years to a given date, handling leap years.
    
    Args:
        base_date = date to calculate from
        num_years = number of years to add (can be negative to subtract)
    
    Return:
        The date num_years after the given date.
    '''
    y = base_date.year + num_years
    m = base_date.month
    d = base_date.day
    # Instead of passing (y,m,d) directly, use offset from start of month.
    # Feb 29 -> Feb 29 in leap years, but Mar 01 in non-leap years
    return datetime.date(y, m, 1) + datetime.timedelta(days=d-1)

def birthdate_range(date_age_seq):
    '''
    Determine the range of possible birth dates for a person.
    
    Args:
        date_age_seq = sequence of (date, age) tuples.
    
    Return:
        Tuple of (earliest, latest) possible date.
    '''
    min_date = datetime.date.min
    max_date = datetime.date.max
    for (date, age) in date_age_seq:
        max_date = min(max_date, add_years(date, -age))
        min_date = max(min_date, add_years(date, -age - 1) + datetime.timedelta(days=1))
    return (min_date, max_date)

traces=[
    (datetime.date(1985, 2, 2), 4),
    (datetime.date(1988, 12, 27), 7),
    (datetime.date(1990, 12, 25), 9),
    (datetime.date(1994, 5, 8), 13),
    (datetime.date(1996, 7, 6), 15),
    (datetime.date(2001, 1, 12), 20)
]

print('You were born between %s and %s, inclusive.' % birthdate_range(traces))
$\endgroup$
1
$\begingroup$

From the first condition, you get that the person was born 2/2/1981 at the latest, or they would have been only 3 years old on 2/2/1985, and 3/2/1980 at the earliest, or they would have been five years or older on 2/2/1985.

Each condition gives you a range of possible birth dates. You just calculate the intersection of these ranges. If the intersection is empty then some of your information is incorrect. If the range contains only one day, then you know the birth date.

$\endgroup$

Not the answer you're looking for? Browse other questions tagged .