Post Reply 
HHC 2018 Programming Contests
10-01-2018, 06:37 AM
Post: #44
RE: HHC 2018 Programming Contests
My take on elegance is a simple and concise program. That's what I present here; it contains several pre-calculated 'magic' numbers generated from the functions given in the challenge. It's probably not immediately obvious from the code exactly how they are derived, but that's what comments are for. So let's start with the general idea.
Each of the three functions has two extrema in their cycle, a maximum at a quarter of the cycle length and a minimum at three quarters of the cycle length. For the functions which don't have a cycle length evenly divisible by four, the example shows me that the rounded result of the division by four is enough. Experimentation has confirmed that, and it has also confirmed that the surrounding days are far enough away to yield function values with an absolute value of at most 99, which means we don't have to deal with extrema spanning more than a single day.
From these observations we can easily calculate for each function the first few days on which it is at an extremum.
For the 'physical' function (henceforth known as P): maximum at 5.75 (rounded to 6), minimum at 17.25 (rounded to 17), repeating every 23 days
- Maxima (first 33): 6, 29, 52, 75, 98, 121, 144, 167, 190, 213, 236, 259, 282, 305, 328, 351, 374, 397, 420, 443, 466, 489, 512, 535, 558, 581, 604, 627, 650, 673, 696, 719, 742
- Minima (first 33): 17, 40, 63, 86, 109, 132, 155, 178, 201, 224, 247, 270, 293, 316, 339, 362, 385, 408, 431, 454, 477, 500, 523, 546, 569, 592, 615, 638, 661, 684, 707, 730, 753
For the 'emotional' function (henceforth known as E): maximum at 7, minimum at 21, repeating every 28 days
- Maxima (first 33): 7, 35, 63, 91, 119, 147, 175, 203, 231, 259, 287, 315, 343, 371, 399, 427, 455, 483, 511, 539, 567, 595, 623, 651, 679, 707, 735, 763, 791, 819, 847, 875, 903
- Minima (first 33): 21, 49, 77, 105, 133, 161, 189, 217, 245, 273, 301, 329, 357, 385, 413, 441, 469, 497, 525, 553, 581, 609, 637, 665, 693, 721, 749, 777, 805, 833, 861, 889, 917
For the 'intellectual' function (henceforth known as I): maximum at 8.25 (rounded to 8), minimum at 24.75 (rounded to 25), repeating every 33 days
- Maxima (first 28): 8, 41, 74, 107, 140, 173, 206, 239, 272, 305, 338, 371, 404, 437, 470, 503, 536, 569, 602, 635, 668, 701, 734, 767, 800, 833, 866, 899
- Minima (first 28): 25, 58, 91, 124, 157, 190, 223, 256, 289, 322, 355, 388, 421, 454, 487, 520, 553, 586, 619, 652, 685, 718, 751, 784, 817, 850, 883, 916
For every pair of functions (P and E, P and I, E and I) there is a number of days where they meet at 0 again and both ascend towards +100, just like they do at birth. That is, the pattern produced by drawing both functions of a pair repeats, and so do their extrema as well as the points where both have an extremum on the same day. The length of this repeating pattern is the product of lengths of the two functions' cycles. (Technically it should be the LCM (lowest common multiple), but since the cycles lengths don't have any common factor, that equates to the same number.) The next pre-processing task is therefore to find for each pair of functions the days on which they both have an extremum.
For P and E the repeating pattern is 23 28 *, i.e. 644 days long, during which P goes through 28 cycles and E goes through 23. Looking at the first 28 maxima and minima of P as well as the first 23 maxima and minima of E, we find the common days 63 (minimum of P, maximum of E, the example), 259 (maximum of both), 385 (minimum of both), and 581 (maximum of P, minimum of E).
For P and I the repeating pattern is 23 33 *, i.e. 759 days long, during which P goes through 33 cycles and I goes through 23. Looking at the first 33 maxima and minima of P as well as the first 23 maxima and minima of I, we find the common days 190 (maximum of P, minimum of I), 305 (maximum of both), 454 (minimum of both), and 569 (minimum of P, maximum of I).
For E and I the repeating pattern is 28 33 *, i.e. 924 days long, during which E goes through 33 cycles and I goes through 28. Looking at the first 33 maxima and minima of E as well as the first 28 maxima and minima of I, we find the common days 91 (maximum of E, minimum of I), 371 (maximum of both), 553 (minimum of both), and 833 (minimum of E, maximum of I).
With this data, the challenge can be rewritten like this: Given a number (representing the age in days, which is calculated as the difference between the birthdate and now), find the lowest number higher than the given number that is either:
- equal to one of 63, 259, 385, 581, all modulo 644, or
- equal to one of 190, 305, 454, 569, all modulo 759, or
- equal to one of 91, 371, 553, 833, all modulo 924.
The found number needs to be converted back to a date by adding it to the current date, of course.
With this rewritten challenge in mind (or on hand), the code becomes relatively straight-forward.
The input and output for this program are performed through the stack. This way the program blends in with normal calculator commands, and it's much less messy than the code for e.g. an input form. As a bonus, it even respects system flag 42 (date format mm.ddyyyy or dd.mmyyyy).

Code:
\<<
  DATE DDAYS                                    (convert the birthdate given on the stack into an age in days)
  1. +                                          (when today is an extrema day, we don't want to get told about that, but rather about the next extrema day, but modulo arithmetics can easily return 0 as result (i.e. 0 days from now -> today); doing special cases for that is much more messy than simply starting the calculations with tomorrow as input)
  \-> age \<<
    { 644. 759. 924. }                          (lengths of the repeating patterns for each function pair, in the same order as the common extrema lists below)
    {
      { 63. 259. 385. 581. }                    (common extrema for the function pair P and E)
      { 190. 305. 454. 569. }                   (same for P and I)
      { 91. 371. 553. 833. }                    (same for E and I)
    }
    2.                                          (I'm going to use DOLIST to apply the same processing to every function pair; DOLIST needs to know that it gets two lists)
    \<<
                                                (all calculations in here should be thought of as modulo the length of the repeating pattern; this is enforced by the MOD below)
      age                                       (in this modulo this tells us how far we are in the pattern)
      -                                         (subtracting the position in the pattern from the positions of the common extrema tells us how far the next occurence of each of the common extrema is; the beauty of modulo arithmetics is that any that have already passed get wrapped around to their next occurence)
      OVER MOD                                  (I'm done doing arithmetics, now I want to compare, so the modulo needs to be applied now)
      1. \<< MIN \>> DOLIST                     (give me the smallest element of the list; the easiest way to do that is with the MIN command which takes two parameters, and feed the list to it one by one using DOLIST; the starting value for that is the modulo (which was still on the stack) because it is guaranteed to be bigger than the list elements)
    \>> DOLIST                                  (after this we have a list of how many days in the future the next extrema date involving each function pair is)
    1000. SWAP 1. \<< MIN \>> DOLIST            (reduce the list to its smallest value using the same trick as above; this time the starting value is 1000, which is bigger than the possible day counts because it's bigger than the biggest modulo)
    DUP age + 'age' STO                         (calculate age for the next maxima day, in order to display the function values on that day)
    1. +                                        (remember, the calculations were based on tomorrow; adjust them to be based on today)
    DATE SWAP DATE+ "Next extrema date" \->TAG  (tell the user what date corresponds to N days from now, and tag it appropriately)
    RCLF RAD                                    (preserve user's angle mode and switch to radians, because calculating the functions involves SIN which depends on the angle mode for correct results)
    { "P" "E" "I" }                             (names of the functions for tagging)
    { 23. 28. 33. }                             (cycle lengths of the functions)
    2.                                          (another DOLIST upcoming ... two lists for this one again)
    \<<
      age SWAP / 2. \pi * * SIN 100. *          (calculate the function for the cycle length on the stack; if this needs any explanation, go back to school and pay attention during trig lessons)
      0. RND                                    (the challenge specifies that the function values shall always be rounded to the nearest integer)
      SWAP \->TAG                               (tag it)
    \>> DOLIST
    SWAP STOF                                   (restore user's angle mode)
  \>>
\>>
At least it's more elegant than the efficiency-focused programs I usually write. It's still quite efficent, the only obvious optimization would be to remove the first 1 + and decrement all the common extrema to compensate, but doing that would make it a bit harder to follow.

Tests:
A baby born last Friday (Sep 28, 2018), should have its first extrema date when it is 63 days old, which will happen on Nov 30, 2018. This is the example given in the challenge, and the graphs given there make it easy to tell if the function values are in the vicinity of the correct results.
For input 28.092018 (my 50G has system flag 42 set, so that is the correct format) on stack level 1 my program outputs 30.112018 on stack level 2, and {P:(-100.) E:100. I:(-54.)} on stack level 1. Looks good so far, except for these weird parentheses around tagged negative numbers when they are on stack levels that get rendered in multiline mode. (I've set mine to only render level 1 in multiline mode.) Non-multiline stack levels have extra spaces at various places instead. Anyway, the data is fine.
A baby born on Jan 1, 2018 has passed its first four extrema dates already. The next one should happen when it is 305 days old, i.e. on Nov 2, 2018.
For input 1.012018 on stack level 1 my program outputs 2.112018 on stack level 2, and {P:100. E:(-62.) I:100.} on stack level 1. Yay, correct date. It appears my program can actually handle cases where the first result in the lists is not the best one.
Now for some stress testing. After 23 28 33 * * days (that's 21252 days) all three functions are at their starting value of 0 again. A person who is exactly that much older than the baby from last Friday should therefore get the exact same output as the baby. 21252 days before Sep 28, 2018 is July 22, 1960 according to my 50G. (Enter 28.092018 -21252. DATE+ to confirm.)
For input 22.071960 on stack level 1 my program outputs 30.112018 on stack level 2, and {P:(-100.) E:100. I:(-54.)} on stack level 1. Yaaay! It works with larger numbers too!
... So I can conclude that it does indeed work.
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
HHC 2018 Programming Contests - Joe Horn - 09-13-2018, 02:17 PM
RE: HHC 2018 Programming Contests - pier4r - 09-13-2018, 06:29 PM
RE: HHC 2018 Programming Contests - Zaphod - 09-13-2018, 10:10 PM
RE: HHC 2018 Programming Contests - Gene - 09-13-2018, 10:56 PM
RE: HHC 2018 Programming Contests - Gene - 09-14-2018, 12:06 AM
RE: HHC 2018 Programming Contests - Jlouis - 09-19-2018, 07:00 PM
RE: HHC 2018 Programming Contests - sasa - 09-19-2018, 11:17 AM
RE: HHC 2018 Programming Contests - pier4r - 09-29-2018, 07:41 PM
RE: HHC 2018 Programming Contests - 3298 - 09-30-2018, 05:32 PM
RE: HHC 2018 Programming Contests - 3298 - 09-30-2018, 08:47 PM
RE: HHC 2018 Programming Contests - Gene - 09-29-2018, 07:22 PM
RE: HHC 2018 Programming Contests - Gene - 10-01-2018, 02:55 AM
RE: HHC 2018 Programming Contests - sasa - 10-01-2018, 05:31 AM
RE: HHC 2018 Programming Contests - sasa - 10-01-2018, 09:54 AM
RE: HHC 2018 Programming Contests - 3298 - 10-01-2018 06:37 AM
RE: HHC 2018 Programming Contests - Werner - 10-01-2018, 01:42 PM
RE: HHC 2018 Programming Contests - Werner - 10-02-2018, 06:10 AM
RE: HHC 2018 Programming Contests - Namir - 10-04-2018, 06:09 PM
RE: HHC 2018 Programming Contests - Werner - 10-03-2018, 02:03 PM
RE: HHC 2018 Programming Contests - Werner - 10-04-2018, 05:55 AM
RE: HHC 2018 Programming Contests - 3298 - 10-04-2018, 02:48 PM
RE: HHC 2018 Programming Contests - 3298 - 10-05-2018, 08:26 PM
RE: HHC 2018 Programming Contests - 3298 - 10-06-2018, 12:07 PM
RE: HHC 2018 Programming Contests - 3298 - 10-06-2018, 04:21 PM



User(s) browsing this thread: 1 Guest(s)