すのものの「いろいろ」(その24)

万年カレンダーを作成(というか再現というか)してみた

むかし見たような万年カレンダーを、思い出しつつ、自分で考えつつ、 作ってみた。

年\月 123 456 789 101112
1967 1978 1989 1995 2006 2017 Ad'DgB eGCfAdF
1984 2012 d"Ea CfA DgB eG
19731979 1990 20012007 2018 Be'
1968 1996 e"Fb DgB EaC fA
1974 19851991 2002 20132019 Cf'
1980 2008 f"Gc EaC FbD gB
19691975 1986 19972003 2014 Dg'
1992 2020 g"Ad FbD GcE aC
1970 19811987 1998 20092015 Ea'
1976 2004 a"Be GcE AdF bD
1971 1982 1993 1999 2010 2021 Fb'
1988 2016 b"Cf AdF BeG cE
19771983 1994 20052011 2022 Gc'
1972 2000 c"DgBeG CfAdF

アルファベットはその月が何曜日から始まるかを示す。 A が日曜、B が月曜、C が火曜、……、G が土曜だ。 小文字は小の月、「a'」などの小文字に「'」は 28 日までの月、 「a"」などの小文字に「"」は 29 日までの月を表わす。

あまりいいできではないな。

2001-01-18 (4) 19:43:55 +0900

TABLE に /TH, /TD, /TR タグを補った。

2004-05-07 (5) 21:50:29 +0900

「〜すればよい」は軽い命令だろうが、広辞苑ではどこを見ればよい?

「三角形の面積を求めるには底辺に高さを掛けて2で割ればよい」 の「〜すればよい」は軽い命令だろうか?  では、この意味を知るには、 広辞苑ではどこを見ればよいのであろうか?  「よい」にそれらしき意味は出ているが、はっきりとは書いてない。

「どこを見ればよいのであろうか?」の 「見ればよい」はそれとは少し違うか。

2001-01-18 (4) 19:40:40 +0900

「底辺×高さ÷2」の「底辺」は厳密には「底辺の長さ」では?

「底辺」は“図形”であり 「高さ」は“量”であるから、 「底辺×高さ÷2」というのは厳密にはおかしいのかも知れない。 「底辺の長さ×高さ÷2」では長すぎるので覚えやすくするため短縮した、 というところだろうか?

2001-01-18 (4) 00:16:58 +0900

「掛けて2で割る」の「割る」は(なぜか)「割った商を求める」の意味

「三角形の面積を求めるには底辺に高さを掛けて2で割る」の 「割る」は「割った商を求める」の意味である。 割れば商と余りが求まるが、決して「割った余りを求める」ではない。 そちらを表わす簡単な表現はあるだろうか?

2001-01-18 (4) 00:09:30 +0900

動物占いの「年と月」の表には 0 があるが「日」を足したあとは 1 から 60

動物占いでは「年と月」の表から数を求め、 それに「日」を足して最終的な数を決めるが、 「年と月」の表には 0 があるのに対し最終的な数から動物名を見る表には 0 はなく 1 から 60 となっている。 “61 以上だったら 60 を引け”というのだが、 60 以上だったら 60 を引くようにして 0 から 59 のほうがいいような気がするのは数学屋だけだろうか?

2001-01-14 (0) 01:54:10 +0900

玖保キリコの絵を使っていない「動物占い」のサイト

Yahoo! で「動物占い」を検索すればいくつかのサイトが見つかるが、 その中のひとつである「動物村」は閉鎖され 「ノラコム」 へリンクが張ってあった。 「ノラコム」の動物の絵は、 玖保キリコのあの絵とは違うようだ。

2001-01-14 (0) 01:47:46 +0900

通算日数を返す関数は「動物占い」にも使える --- 大昔の人もこれで OK !

グレゴリウス暦 0 年 3 月 1 日からの通算日数に 9 を足してから 60 で割った余りを求めれば、 ちょっと前によく見かけた「動物占い」の数字になるようだ。 (0 は 60 とみなす。)

ナザレのイエス(1 年 12 月 24 日生まれ)は 13 で「狼」だった。 1989 年 11 月 12 日と 726060 = 60 * 12101 日違いなので、 くわしく知りたいかたはこの日付で占ってみるとよいかもしれない (というのはイエスはこの日に生まれたわけではないのでもちろん冗談)。

2001-01-14 (0) 01:44:25 +0900

コンピュータ上で万年カレンダーを再現した、こんなカレンダーはいかが?

                         1  2  3  4  5  6
    1  2  3  4  5  6  7  8  9 10 11 12 13
    8  9 10 11 12 13 14 15 16 17 18 19 20
   15 16 17 18 19 20 21 22 23 24 25 26 27
   22 23 24 25 26 27 28 29 30 31
   29 30 31
のように幅を広くとった“カレンダー”のうち、 連続した7列だけを見せる万年カレンダーがある。 その月の曜日に従って、見せる7列を変えるのだ。 それをコンピュータ上で再現した次のようなカレンダーはどうだろうか?  薄く表示された部分は“単なる模様”だと思って、濃い字のところだけを見る。

    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
    8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  1  2  3  4
   22 23 24 25 26 27 28 29 30 31  1  2  3  4  5  6  7  8  9 10 11
   29 30 31  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
    5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  1  2
   19 20 21 22 23 24 25 26 27 28 29 30  1  2  3  4  5  6  7  8  9
   26 27 28 29 30  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
    3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  1  2  3  4  5  6
   24 25 26 27 28 29 30 31  1  2  3  4  5  6  7  8  9 10 11 12 13
   31  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
    7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  1  2  3  4
   21 22 23 24 25 26 27 28 29 30  1  2  3  4  5  6  7  8  9 10 11
   28 29 30  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
    5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  1
   19 20 21 22 23 24 25 26 27 28 29 30 31  1  2  3  4  5  6  7  8
   26 27 28 29 30 31  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
    2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22
    9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  1  2  3  4  5
   23 24 25 26 27 28 29 30 31  1  2  3  4  5  6  7  8  9 10 11 12
   30 31  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
    6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  1  2  3  4  5
   20 21 22 23 24 25 26 27 28  1  2  3  4  5  6  7  8  9 10 11 12
   27 28  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19

コンピュータ上なら、その月の分だけを生成して表示するほうがほんとは楽。 そこをわざわざやるところがおもしろい(かもしれない)。

一月分だけなら、こんなたくさんの行は不要だ。 これだけあれば、次の月、その次の月も表示できる。 上の例は、ある年の 4 月と、翌年の 2 月を表示している。

※ 上の見本は手作業によるものなので間違いがあるかもしれません。 プログラムは興味ある人が作ってください。 背景にも色をつけるなどするとより見やすいかも。

……とここまでを 2001-01-03 (3) 03:06:20 +0900 に書いたが公開はしなかった。 割とすぐ書けそうな気がしてきたから。

で、できたのがこれだ。 2000 年の 3 月から 2001 年の 2 月までを示している。 3 月から 9 月までの大小の月のパターンが 8 月から 2 月までのそれと一致することを利用し、 二列にしている。

16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9
23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6
22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13
29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 1 2 3 4
19 20 21 22 23 24 25 26 27 28 29 30 1 2 3 4 5 6 7 8 9 10 11
26 27 28 29 30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8
24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 1 2 3 4 5 6
21 22 23 24 25 26 27 28 29 30 1 2 3 4 5 6 7 8 9 10 11 12 13
28 29 30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3
19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10
26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7
23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14
30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 1 2 3 4 5 6 7
20 21 22 23 24 25 26 27 28 1 2 3 4 5 6 7 8 9 10 11 12 13 14

灰色で表示されている日は実在しないが、 実在する日の隣に表示されている日は、 たとえば 2 月 25 日(日曜日)の次の日曜日はこのカレンダー通り 4 日であるという意味で“正しい”。

作成に利用したプログラムは以下の通り。 コマンドライン引数に「年」をつけて起動すると その年の 3 月から翌年 2 月までのカレンダーを出力する。

上のカレンダーは、最上行、最下行、それと残りの部分を左と右に分けたもの、 の四つに分かれている。 (ある日からの)通算日数は各行の中では右に行くごとに 1 日ずつ増え、 各行の冒頭では下の行へ移るごとに 7 日ずつ増えることに注意する。 ちょっと前に書いた関数でそれを月と日に直して表示する。

最初の部分にある定数を変えれば、 比較的容易に背景や文字の色を変えることができる。

#include <stdio.h>
#include <stdlib.h> /* atoi */

/* 各月の背景の色を「R, G, B」(それぞれ 0 から 255 まで)の形で指定 */

#define NAM 0xFF, 0xFF, 0xFF    /* Not a Month の意。カレンダー「外」の色 */

#define MAR C1, C1, C5
#define APR C2, C1, C4
#define MAY C3, C1, C3
#define JUN C4, C1, C2
#define JUL C5, C1, C1
#define AUG C4, C2, C1
#define SEP C3, C3, C1
#define OCT C2, C4, C1
#define NOV C1, C5, C1
#define DEC C1, C4, C2
#define JAN C1, C3, C3
#define FEB C1, C2, C4

/* 薄めの色 */
#define C1 (255-0)
#define C2 (255-8)
#define C3 (255-12)
#define C4 (255-16)
#define C5 (255-20)

/* ちょっと濃いめの色 */
/*
#define C1 (255-0)
#define C2 (255-16)
#define C3 (255-24)
#define C4 (255-32)
#define C5 (255-40)
*/
/* もっと濃いめの色 */
/*
#define C1 (255-0)
#define C2 (255-24)
#define C3 (255-36)
#define C4 (255-48)
#define C5 (255-60)
*/

/* 各曜日の文字の色を文字列で指定 */

#define NAD "\"#C0C0C0\""    /* Not a Day の意。カレンダー「外」の色 */
#define SUN "\"#FF0000\""
#define MON "\"#000000\""
#define TUE "\"#000000\""
#define WED "\"#000000\""
#define THU "\"#000000\""
#define FRI "\"#000000\""
#define SAT "\"#0000FF\""

#define isleap(X) (!((X)%4) && (X)%100 || !((X)%400))   /* うるう年? */

long zeller(int year, int month, int day);
void unzeller(long days, int* year, int* month, int* day);

int main(int argc, char* argv[]) {
    long days;                          /* 0000-03-01 からの通算日数 */
    int year, month, day;               /* 年、月、日 */
    int i, j;                           /* 表示の縦、横を動くパラメータ */
    int lstart, lend, rstart, rend;     /* 左[右]の表示開始[終了]位置 */
    int lines;                          /* 表示すべき行の数 */

    if (argc != 2) {
        printf("引数として「年」をとります.\n");
        return 1;
    }
    year = atoi(argv[1]);
    days = zeller(year, 3, 1);

    days -= 14;                         /* これが左上の端に表示される日 */

    lstart = 7 - (days + 2) % 7;        /* いろいろ考えるとこうなるのだ */
    lend = lstart + 6;
    rstart = lend + 2;
    rend = rstart + 6;

    lines = 30 + (isleap(year+1) && (lstart == 1)); /* 表示すべき行の数 */

    printf("<TABLE cellspacing=\"0\" cellpadding=\"3\" border=\"0\">\n");
    printf("<TR align=\"right\">\n");
    for (j=0; j<23; j++) {              /* 一番上の行 */
        unzeller(days+j, &year, &month, &day);
        printf("<TD bgcolor=\"#%02X%02X%02X\">", NAM);
        printf("<FONT color=%s>%3d</FONT>\n", NAD, day);
    }
    for (i=1; i<=lines; i++) {          /* カレンダー本体 */
        printf("<TR align=\"right\">\n");
        for (j=0; j<rstart; j++) {          /* 左半分(3月〜8月)*/
            unzeller(days+7*i+j, &year, &month, &day);
            printf("<TD bgcolor=\"#");          /* 背景色 */
            if (lstart <= j && j <= lend) {
                switch (month) {
                    case  3: printf("%02X%02X%02X\">", MAR); break;
                    case  4: printf("%02X%02X%02X\">", APR); break;
                    case  5: printf("%02X%02X%02X\">", MAY); break;
                    case  6: printf("%02X%02X%02X\">", JUN); break;
                    case  7: printf("%02X%02X%02X\">", JUL); break;
                    case  8: printf("%02X%02X%02X\">", AUG); break;
                    default: printf("%02X%02X%02X\">", NAM);
                }
            } else {
                printf("%02X%02X%02X\">", NAM);
            }
            printf("<FONT color=");             /* 文字 */
            if (month < 3 || month > 8) {
                printf("%s", NAD);
            } else {
                switch (j - lstart) {
                    case 0:  printf("%s", SUN); break;
                    case 1:  printf("%s", MON); break;
                    case 2:  printf("%s", TUE); break;
                    case 3:  printf("%s", WED); break;
                    case 4:  printf("%s", THU); break;
                    case 5:  printf("%s", FRI); break;
                    case 6:  printf("%s", SAT); break;
                    default: printf("%s", NAD);
                }
            }
            printf(">%3d</FONT>\n", day);
        }
        for (    ; j<23; j++) {             /* 右半分(9月〜翌年2月)*/
            unzeller(days+153+7*i+j, &year, &month, &day);
            printf("<TD bgcolor=\"#");          /* 背景色 */
            if (rstart <= j && j <= rend) {
                switch (month) {
                    case  9: printf("%02X%02X%02X\">", SEP); break;
                    case 10: printf("%02X%02X%02X\">", OCT); break;
                    case 11: printf("%02X%02X%02X\">", NOV); break;
                    case 12: printf("%02X%02X%02X\">", DEC); break;
                    case  1: printf("%02X%02X%02X\">", JAN); break;
                    case  2: printf("%02X%02X%02X\">", FEB); break;
                    default: printf("%02X%02X%02X\">", NAM);
                }
            } else {
                printf("%02X%02X%02X\">", NAM);
            }
            printf("<FONT color=", NAD);     /* 文字 */
            if (!(month >= 9) && !(month < 3)) {
                printf("%s", NAD);
            } else {
                switch (j - rstart) {
                    case 0:  printf("%s", SUN); break;
                    case 1:  printf("%s", MON); break;
                    case 2:  printf("%s", TUE); break;
                    case 3:  printf("%s", WED); break;
                    case 4:  printf("%s", THU); break;
                    case 5:  printf("%s", FRI); break;
                    case 6:  printf("%s", SAT); break;
                    default: printf("%s", NAD);
                }
            }
            printf(">%3d</FONT>\n", day);
        }
    }
    printf("<TR align=\"right\">\n");
    for (j=0; j<23; j++) {              /* 一番下の行 */
        unzeller(days+153+7*i+j, &year, &month, &day);
        printf("<TD bgcolor=\"#%02X%02X%02X\">", NAM);
        printf("<FONT color=%s>%3d</FONT>\n", NAD, day);
    }
    printf("</FONT>\n");
    printf("</TABLE>\n");
    return 0;
}

/* グレゴリウス暦 0 年 3 月 1 日からの通算日数を返す */

long zeller(int year, int month, int day) {
    if (month < 3) {
        year--; month+=12;
    }
    return 365L * year + year / 4 - year / 100 + year / 400 \
                            + (153 * month - 457) / 5 + day;
}

/* zeller() の逆。通算日数 days を渡すと year に(十進の)「年」を、month に */
/* 「月」(一月が 1, ……, 十二月が 12)を、day に「日」を入れて返す。       */

void unzeller(long days, int* year, int* month, int* day) {
    int century;    /* (十進の)「年」を右に二桁だけシフトしたもの */

    century = (4 * days - 1)/ 146097L;
    days -= 146097L * century / 4;
    *year = (4 * days - 1) / 1461;
    days -= 1461L * *year / 4;
    *year += 100 * century;
    *month = (5 * days - 3) / 153 + 3;
    days -= (153 * *month - 457) / 5;
    if (*month > 12) {
        *month -= 12; (*year)++;
    }
    *day = days;
}

ちょっと前に、 背景色の TABLE を作ったりしていたのはこのプログラムに利用するためだったのだ。 最初の計画では、 各月ごとにそれらしい色を選ぶことになっていた。 次のようなところまでやって、それは断念した。

MAR
APRSEP
MAYOCT
JUNNOV
JULDEC
AUGJAN
FEB

3 月は菜の花の色、 4 月は桜の色、 5 月は新緑の色、 6 月はアジサイの色、 7 月は青空の色、 8 月は太陽の色、 9 月はコスモスの色、 10 月も青空の色、 11 月はなんとなく、 12 月はクリスマスらしい赤、 1 月はお餅の色、 2 月は氷や雪のイメージの色、 のつもりだった。 でもなかなか思ったようにゆかないので上のようにした。

256 × 256 × 256 の立方体で、 (RR, GG, BB) の位置にある単位立方体の色が #RRGGBB のものを考えると、 上のカレンダーに使った十二の色はもっとも明るい #FFFFFF の頂点の回りをとりまいている。

できたと思っていたのだが、8 日になって、 3 月が土曜に始まって翌年 2 月が 29 日まである年 (1947 年、1975 年、2003 年など) は表示上で1行多いのを見落としていることに気づいた。 どうやってパッチを当てようかちょっと考えたが、 表示行数を表わす変数 lines を導入して切り抜けた。

2001-01-13 (6) 02:12:02 +0900

最後の TABLE に /TD, /TR タグを補った。

2004-05-07 (5) 21:28:47 +0900


すのもの Sunomono