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í:

ADDRCODEXYZT
Y001LBL Yy


Y002STO Yy


Y0031919y

Y004RMDRy % 19


Y00511y % 19

Y006+g


Y007LASTx1g

Y008RCL Yy1g
Y009100100y1g
Y010INT÷intg(y / 100)1gg
Y011+cggg
Y0120.320.32cgg
Y013REGYc0.32cg
Y014×0.32 ccgg
Y0150.20.20.32 ccg
Y016+0.32 c + 0.2cgg
Y017INTGzcgg
Y018x<>yczgg
Y0190 4/34/3czg
Y020INT÷intg(0.75 c)zgg
Y02122intg(0.75 c)zg
Y022-xzgg
Y023-z - xggg
Y024LASTxxz - xgg
Y0251.251.25xz - xg
Y026RCL× Y1.25 yxz - xg
Y027INTGintg(1.25 y)xz - xg
Y028x<>yxintg(1.25 y)z - xg
Y029-dz - xgg
Y030x<>yz - xdgg
Y0312525z - xdg
Y032+25 + z - xdgg
Y033111125 + z - xdg
Y034STO H1125 + z - xdg
Registr/proměnná H = 11
Y035R↑g1125 + z - xd
Y036STO- Hg1125 + z - xd
H = 11 - g
Y037×11 g25 + z - xdd
Y038+11 g + 25 + z - xddd
Y039303011 g + 25 + z - xdd
Y040RMDReddd
Y04111edd
Y042x<>ye1dd
Y0432424e1d
Y044-f = 24 - e1dd
Y045x=0?f1dd
Y046GTO Y056f1dd
Y047x<>y1fdd
Y048R↓fdd1
Y049RCL H11 - gfdd
Y050SGNsgn(11 - g)fdd
Y05122sgn(11 - g)fd
Y052INT÷sgn(11 - g) / 2fdd
Y053+/-hfdd
Y054x<>yfhdd
Y055x=y?fhdd
Y056+fddd
Y057+/--f dd
Y0583030-f d
Y059x<>y-f30 d
Y060x≤0?-f30 d
Y061+-f + 30  d
Y062R↑d-f + 30

Y063x<>y-f + 30d

Y0642020-f + 30d
Y065+nd

Y066x<>ydn

Y067REGYndn
Y068+d + nn

Y06977d + nn
Y070RMDR(d + n) % 7n

Y0717.037.03(d + n) % 7n
Y072x<>y(d + n) % 77.03n
Y073-7.03 - (d + n) % 7n

Y074+m


Y07531.0331.03m

Y076x<>ym31.03

Y077x≤y?m31.03

Y078GTO Y081(v březnu)


Y07930.9930.99m

Y080-(v dubnu)


Y081RCL Yrokden.měsíc

Y082RTN



LN=298, CK=ACEA

Použití programu:
Stisk klávesČinnostDisplay
1 9 6 4 rok, pro který se hledá datum velikonoční neděle0.0000
1964_
XEQ Y ENTERspuštění programu
RUNNING
výsledek: 29. březen 1964 29.0300
1,964.0000

Kontrolní výsledky lze získat zde.

Poznámka:
Program používá dvě proměnné (postaru řečeno paměťové registry) Y a H. V případě konfliktu se stejnojmennými proměnnými jiných programů je třeba je přejmenovat. Programové řádky, kterých se taková úprava týká, jsou ve výpisu barevně odlišeny.

Ještě jedna poznámka:
Někomu se může zdát, že použití dvou proměnných pro tak jednoduchý výpočet je neodpustitelná rozežranost. I pro něj tu je řešení: proměnnou H vůbec nepoužít, nahradit jí proměnnou Y (bez ní se to celé opravdu neobejde) a řádek Y081 RCL Y vypustit. Výsledek potom bude v X-registru RPN stacku a zadaný rok nebude zobrazen. Holt, každá sranda něco stojí :-).


Závěr

Pokud má vůbec smysl na závěr něco zdůraznit, tak snad jedině fakt obecně platný pro všechny ukázky RPN programů na těchto webových stránkách: předkládaný program v žádném případě nebyl sestaven za účelem něco vyřešit (zde konkrétně stanovit termín velikonoc v daném roce). Účel tvorby programu je právě samotná tvorba. A to bez ohledu na její přínos. Těžko si lze představit zoufalce, který na kalkulačce píše program proto, aby zjistil datum velikonočních svátků letos, loni nebo kdykoliv v budoucnu. Daleko snáze si lze představit jiného zoufalce, který se "hnípe" s instrukcemi použitého kalkulátoru a snaží se je - jen tak ze sportu - seřadit co nejefektivněji...