Výpočet data velikonoční neděle

Bystrému a hlavně pravidelnému čtenáři těchto webových stránek dozajista neuniklo, že jejich autor se v předchozích ukázkách pro různé shluky programových řádků zdráhal užít názvu program. V případě ukázky výpočtu data velikonoční neděle je takové pojmenovaní naprosto legitimní, neboť se jedná o "monument" zvíci osmdesáti programových řádků.

Program (Všimli jste si? Ani náznak uzardění :-) je pro kalkulátor HP-35s, a tentokrát se sluší podotknout, že výhradně pro tento. Jeho transkripce pro jiné typy je samozřejmě možná, ale ne zcela jednoduchá. Zdaleka ne všechny modely disponují funkcemi RMDR nebo INT÷. Ani přímá adresace libovolného registru RPN stacku (viz instrukce REGY) není zcela běžnou výbavou. Snad jen typy HP-41C nebo HP-42s jí disponují.


Postup výpočtu

Inspirací pro tento program je postup publikovaný v knize Umění programování - Základní algoritmy amerického autora Donalda E. Knutha, kterému bude lidstvo vděčit další tisíciletí za jeho famózní soubor knih o programování. Jak na to Mistr šel, popatřte sem. Mistr jistě promine, ale my mladí (rozumějte: narozeni až po válce - té druhé, pochopitelně) vše snáze pochopíme, bude-li postup napsán v jazyku C. Názvy proměnných jsou ponechány a jejich významy rekapituluje následující seznam.

Y Year
G Golden number
C Century
X Century leap year adjustment
Z Moon's orbit adjustment
D Sunday date
E Epact
N Day of month

A zde konečně program "Céčku".

#define  INTG

void easter(int y)
{
    int     g, c, x, z, d, e, n, m;

    g  =  y % 19 + 1;
    c  =  INTG(y / 100) + 1;
    x  =  INTG((3 * c) / 4) - 12;
    z  =  INTG((8 * c + 5) / 25) - 5;
    d  =  INTG((5 * y) / 4) - x - 10;
    e  =  (11 * g + 20 + z - x) % 30;
    if(e == 25  &&  g > 11  ||  e == 24)
        ++e;
    n  =  44 - e;
    if(n < 21)
        n += 30;
    m  =  n + 7 - (d + n) % 7;
    if(m > 31)
    {
        m -= 31;
        printf("%d. dubna %d\n", m, y);
    }
    else
        printf("%d. brezna %d\n", m, y);
}		

Upravený postup

Pro snazší realizaci v "kalkulačkovém pojetí" je postup upraven. Jsou zde patrné tři výrazné změny:

  1. Násobení a dělení je převedeno na desetinná čísla. (V hotovém programu na jednom místě dokonce ponechána zlomková notace.) Nemá smyslu "programově" násobit konstantou a následně jinou konstantou dělit. Totéž se dá provést násobením (nebo dělením - zvolit, co je výhodnější) "na jeden zátah".
  2. Testy hodnot proměnných jsou pokud možno prováděny s hodnotou 0.
  3. Počet testů a skoků je minimalizován použitím speciálních funkcí (v programu v jazyku C bitový posun).
Přibyly dvě proměnné f a h. Jejich význam je jasný: jsou to pomocné proměnné a vazbu na proměnné původní charakterizují litery tvořící jejich názvy. Nová proměnná f má pomáhat původní e a další nová h souvisí s původní g. Výsledek face-liftingu tedy vypadá následovně:

#define  INTG(v)    ((int)floor(v))
#define  SGN(v)     ((v) == 0  ?  0  :  ((v) > 0  ?  1  :  -1))

void easter(int y)
{
    int     g, c, x, z, d, e, n, m,
            f, h;

    g  =  y % 19 + 1;
    c  =  INTG(y / 100) + 1;
    x  =  INTG(0.75 * c) - 2;
    z  =  INTG(0.32 * c + 0.2);
    d  =  INTG(1.25 * y) - x;
    e  =  (11 * g + 25 + z - x) % 30;
    f  =  e - 24;
    do
    {
        if(f != 0)
        {
            h  =  -(SGN(11 - g) >> 1);
            if(f != h)
                break;
        }
         ++f;
    } while(0);
    n  =  -f;
    if(n ≤ 0)
        n += 30;
    n += 20;
    m  =  n + 7 - (d + n) % 7;
    if(m > 31)
    {
        m -= 31;
        printf("%d. dubna %d\n", m, y);
    }
    else
        printf("%d. brezna %d\n", m, y);
}		

Hotový program

A nyní nebojácně a s plným odhodláním přistupme k hotovému kódu pro HP-35s. Je třeba přiznat, že celková délka (počet bajtů) programu je v podstatě srovnatelná s použitím algebraického módu. Znamená to, že se vzorce "slepě" opíšou do sekvence výrazů s proměnnými G, C, X, Z, D, E, N, M (viz EQN). Jaký je potom přínos předkládaného RPN běsnění? Dvojí: