ママのスキンケアのお悩みにおすすめアイテム

今与えられた開始日から与えられた終了日までの期間を求めるプログラムを作りました。勿論その中で閏年の計算も行います。
自分の作ったプログラムでは1つだけ気に入らないところがありました。それは閏年の計算部分です。


■うるう年の判定
・年が4で割り切れる時はうるう年
 従って、2004年はうるう年である
・ただし、100で割り切れる時はうるう年でない
 従って、1900年はうるう年ではない
・ただし、400で割り切れる時はうるう年である。
 従って、2000年はうるう年である



この条件を行うため自分は次のように記述しました

for(year = First_Year; year <= End_Year; year++){
  if(((year  % 4) == 0) && ((((year % 100) != 0) && ((year % 400) != 0)) || (((year % 100) == 0) && ((year % 400) == 0)))){
    閏年の個数を数える
   }
}

for文変数の初期でFirst_Yearを与えていますが、First_Yearが開始年でEnd_Yearが終了年です。
if文がむかつくほど長くなってしまいました。やっていることはyearが4で割り切れかつ100と400で割ったときに両方共に余りが出る、またはyearが4で割り切れかつ100と400で割ったときに両方共に余りが出ない場合に 閏年の個数を数える ようにしています。


かなり強引な質問ではありますが、みなさんならどのように組むでしょうか? 別に自分の考える”美しい”プログラムでなくてもかまいません。色々な考えを聞かせてもらえないでしょうか?

このQ&Aに関連する最新のQ&A

A 回答 (10件)

プログラムを簡素で美しく記述する王道は、


・何をするのかを日本語で簡潔にまとめる(整理する)
→ うるう年判定であれば、判定方法を整理します。
  (1)4で割れる年をうるう年とする
  (2)100で割れる年は平年にする
  (3)400で割れる年はうるう年とする
・凝ったことをしようとしない。
→ ビット操作とかポインタ操作とかできるのがCの醍醐味ですが、いわゆる「テクニック」を使わないほうが実は簡素で美しくなる傾向にあります。

これを心がけてうるう年判定関数を作ってみました。
bool is_leap(int year){
  if(year % 400 == 0) return true;
  if(year % 100 == 0) return false;
  if(year % 4 == 0) return true;
  return false;
}
書き方によっては実行速度がはやい方法もあるでしょうけど、うるう年関数なんかチューニングするより、他のアーキテクチャを見直したほうがよっぽど効果的です。

また、この関数であれば、3200年に一度うるう年でないことに対応
させたい(いわゆる仕様追加みたいな場面)ときに
  if(year % 3200 == 0) return false;
を追加すればよいことが一目瞭然です。
複雑な条件式を書かずに素直に書くと、メンテが楽になりますね。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

・・・・提示してくださったコード、すばらしいと思います。
自分の中では”短いコード”を追求したものが美しいと感じていましたが、そうではないんですよね・・・・・

提示してくださったコードはプログラムが上から順に実行されていくことをうまく使った方法だと思います。

のちのちのことを考え、他の人が分かりやすく、保守がしやすいコードもまた美しく書く条件の1つになるでしょう。

ありがとうございました。

お礼日時:2007/06/16 11:30

> まぁテーブルで使用されるメモリ容量が気になる場合は、単に0,1のみなので、ビット列としてでも表現できますね。



そうなのですが、除算器を持たないような非力なCPUの場合、大抵シフト演算も遅い(ハード的には1ビット単位でしかシフトできない場合が多い)ので、ビット列にしてしまうと結局パフォーマンスが上がらなくなります。
    • good
    • 0

> そして最後の記述ですが、ちょっと自分の経験不足で分からないのですが、表引きにするということは、4桁の西暦を上2桁を行番号、下2桁を列番号とし、それぞれにtrueかfalseを入れるといった方法でしょうか?



そんな難しい話ではなく、例えば1970年~2099年までしか扱わないのであれば、西暦から1970を引いた値を添え字とするブール値の配列を作るなどすればよいということです。
具体的には、

int is_leap(int year)
{
 static const char leap_table[130] = { 0,0,0,1,... };
 if (yaer < 1970 || 2099 < year) return -1;
 return leap_table[year - 1970];
}

のようにします。
返却値は、単にうるう年かどうかだけでなく、引数が不正な場合に報告できるように、少なくとも三値を表現できる必要があります。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。
なるほど・・・基準値を決め、そこから閏年の時だけフラグを立てるという方法ですね。

"少なくとも三値を表現できる必要があります"
TRUE、FALSE、-1ということで返り値がintなのですね。

まぁテーブルで使用されるメモリ容量が気になる場合は、単に0,1のみなので、ビット列としてでも表現できますね。

また違った考え方が聞けてよかったと思います。ありがとうございました^^

お礼日時:2007/06/17 02:38

3200年や80000年に関しては、シミュレーションなどでは必要になる可能性がありますし、遠未来を扱ったSFのゲームなどでも関係してくるかもしれませんね。

逆に、グレゴリオ暦ができる(1582年)以前を、または国によって扱うべき暦法を変える必要があるなら、それも考慮する必要があります。

ここ100年か200年だけを扱う場合でも、#3の回答だけでは不十分で、引数の有効範囲をチェックする必要があるかと思います。最低限、assertを仕込んでおくぐらいは必要でしょう。

ところで、ここ100年か200年を扱う場合で、かつCPUが非力な環境(特に除算器を持たないCPU)であれば、表引きにするのが一番効率がよいかと思います。ただし、その分メモリを食いますので、どうするかは総合的に判断する必要があります。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

確かに有効範囲をチェックする場合は#3の回答では不十分です。

問題は有効範囲までif文を追加するか、または有効範囲をチェックするかの方法ですが、それに関してもメンテはしやすいと思います。
まぁassertを仕込むだけでも一行で終わりですからね。


そして最後の記述ですが、ちょっと自分の経験不足で分からないのですが、表引きにするということは、4桁の西暦を上2桁を行番号、下2桁を列番号とし、それぞれにtrueかfalseを入れるといった方法でしょうか?

お礼日時:2007/06/16 12:18

> ところで、3200で割れる年が平年と言うのははじめて聞きましたが、


私も気になったのでgoogleしてみたら、
http://www.water.sannet.ne.jp/kazuya-ai/14/week. …
によると、3200で平年、また80000年でうるう年だそうです。

原理的には、その先も何百万年とか何億年単位で例外があると思いますが、3200年後がうるう年かどうかは実用上問題ないので、一般には無視されるという話を聞いたことがあります。

でも、3200年後がうるう年かどうかがシステムに無関係であることを確認したうえで無視するべきで、盲目的に無視したら2000年問題と同じですね。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

そしてわざわざ調べていただきありがとうございます。

80000年でうるう年ですか・・・・・・天文学的な数字になってきましたね。

たしかにそこまでがシステムに関係なかったら無視するべきで、関係するシステムもそんなにあるわけではないだろうし、無視してもよいでしょうね。

お礼日時:2007/06/16 12:06

aに年数が入っているときに、


閏年なら0、そうでないとき0以外を返す処理

if (!(a % 100))
a = a /100;
return a % 4;

これは、閏年を論理和、論理積を使わずに判定せよと言う課題の
解答です。

ところで、3200で割れる年が平年と言うのははじめて聞きましたが、
本当かしら。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

そしてすばらしいコードを提示していただきありがとうございます。
自分は論理和、論理積 と このような計算により算出する場合とで、どちらのほうが速度が速いか分かりませんが、このコードも理解しやすいと思います。

つまり100年がいくつ含まれているかを判定し、その個数が4の倍数かどうかを判定していますね。

しかし最後におっしゃられた3200年ももし閏年であっても

if(!(a % 3200))
a = a/3200;
if(!(a % 100))
a = a/100;
return a % 4;

一応上のように条件をただ付け足すだけですよね。

ありがとうございました

お礼日時:2007/06/16 12:02

カリカリに速度チューニングが必要(保守性よりも優先)なケースでもなければ、


ソースレビューしたら私も#3の方の方針を支持します。

「凝ったことをしようとしない。」←これ大事。

「簡素で美しい」コードは、「解説」を必要としないシンプルなコードだと思います。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

”「凝ったことをしようとしない。」←これ大事。”
確かに計算やアルゴリズムに依存して、プログラムのコードも複雑になるケースは沢山ありますが、解釈の仕様によってとてもシンプルなコードになりますよね。 こったことをしようとせず、そのままを頭の中で整理し、コードを作成してみる。すると思ったより分かりやすく、かつシンプルなコードが出来上がることはままあるものですよねw

しかし時々あることを重要と考えずに通り過ぎてしまっている。
それはご指摘されたようにこったことをしないことはとても大事だということですね。

そして新しい考え方を聞けてとても感謝しています。


”簡素で美しい」コードは、「解説」を必要としないシンプルなコード” これもまた簡素で美しいコードの条件の一つに含まれると思いますし、誰もが納得するでしょう。

お礼日時:2007/06/16 11:40

全然回答ではありませんが、#3のかたに一票です。


プログラムは、他人がみて最もわかりやすいものが、簡素で美しい記述になります。自分一人で趣味で作る範囲では、どのようなものでもかまいませんが、業務用の場合は、そのプログラムが、次のひとに引き継がれていきます。
従って、
他人がみてわかりやすい。
仕様変更が容易にできる。
ことが必要です。
#3のかたの回答は、その意味で、満点です。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。

自分も#3様に一票ですw
確かにおっしゃられるとおり、とても分かりやすくシンプルで、保守もしやすいと思います。

ぱっと見、これ以上そぎ落とすものがなく、条件の追加が容易で分かりやすい。

自分もこのようなコードを書けるよう日々努力しようと思います。

お礼日時:2007/06/16 11:34

★アドバイス


・うるう年の判定はあまり複雑に考えなくても判定できますよ。
 そこで昔、作成したマクロ関数と関数を載せておきます。

/* 閏年判定のマクロ関数 */
#define MacroIsLeapYear(n) ((!((n) % 4) && ((n) % 100) && ((n) % 3200)) || !((n) % 400))

/* 西暦から閏年の判定 */
bool FuncIsLeapYear( int year )
{
 if ( !(year % 4) && (year % 100) && (year % 3200) ){
  return( true );
 }
 return( !(year % 400) );
}
/*
●解説
(1)4で割れる年をうるう年とする
(2)ただし、100で割れる年は平年にする
(3)ただし、400で割れる年は閏年とする
(4)ただし、3200で割れる年は平年とする
※まとめると4年に1回は閏年、でも 400 年に3回は閏年ではない事になる。
*/

その他:
・開始日から終了日の期間の計算は西暦1年1月1日からの通算日数を求めた後で引き算をした方が
 for 文でうるう年を数えるよりも早く計算できると思います。まずは通算日数を計算する関数を
 作成して下さい。もちろん、この計算にもうるう年を含めた計算を追加します。
・つまり、4 年に1回はうるう年なので『西暦 / 4』を通算日数に加算します。
 でも 400 年に3回は平年になるため『西暦 / 400 * 3』を通算日数から引きます。
 これで西暦1年1月1日からの通算日数が求まります。
・その後に『終了日』から『開始日』を単純に引くことで期間の日数が計算できます。
 下に1月1日からの通算日数を求める関数を紹介します。

/* 年月日から1月1日からの日数を取得 */
int FuncGetDays( int year, int month, int day )
{
 static int table[] = {
/* 0 */ 0,
/* 1 */ 0,
/* 2 */ 31,
/* 3 */ 31 + 28,
/* 4 */ 31 + 28 + 31,
/* 5 */ 31 + 28 + 31 + 30,
/* 6 */ 31 + 28 + 31 + 30 + 31,
/* 7 */ 31 + 28 + 31 + 30 + 31 + 30,
/* 8 */ 31 + 28 + 31 + 30 + 31 + 30 + 31,
/* 9 */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
/* 10 */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
/* 11 */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
/* 12 */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
 };
 
 // 2月以降で閏年なら+1日
 if ( (month >= 3) && FuncIsLeapYear(year) ){
  day++;
 }
 return (table[month] + day);
}
/*
●解説
※1月1日からの日数を返す。(1-366)
*/

最後に:
・開始日を Y1/M1/D1、
 終了日を Y2/M2/D2 とすると
 days1 = ((Y1 * 365) + (Y1 / 4) - (Y1 / 400 * 3)) + FuncGetDays(Y1,M1,D1);
 days2 = ((Y2 * 365) + (Y2 / 4) - (Y2 / 400 * 3)) + FuncGetDays(Y2,M2,D2);
 diff = (days2 - days1);
  ↑
 この diff が開始日から終了日の期間の日数です。
・以上。
    • good
    • 0
この回答へのお礼

返答ありがとうございました。
/*
●解説
(1)4で割れる年をうるう年とする
(2)ただし、100で割れる年は平年にする
(3)ただし、400で割れる年は閏年とする
(4)ただし、3200で割れる年は平年とする
※まとめると4年に1回は閏年、でも 400 年に3回は閏年ではない事になる。
*/

3200で割れる年が平年だとは知りませんでした。
そして解釈の仕様によってとてもシンプルなアルゴリズムになりますね。”4年に1回は閏年、でも 400 年に3回は閏年ではない”と解釈の仕方をすることができなかったため、自分のようなソースになってしまいました。

自分が作ったプログラムは配列を13個用意し、添え字が1から12までの各要素を月として、その月の最大日数を格納して計算していました。
しかしOh-Orange様が示されたように、1月を0として、そのつきの前の日数を足して配列に格納していけば、たとえば2月22日といった場合には添え字2の場所には1月分の日数が入っているわけだから、添え字2に格納されている数字と22を足すだけでいいですね。
こういう方法もあるんだと関心です。

お礼日時:2007/06/15 10:28

bool is_leap(int year); みたいな関数にする



(End_Year / 4 ) - (First_Year / 4) みたいにすれば、間にある 4の倍数を数えることも易しいけど、関数にした方が意味を把握しやすいでしょう。
    • good
    • 0
この回答へのお礼

返信ありがとうございます。
関数にしてみたところ確かにすっきりしました。
すばやい回答に感謝します。

お礼日時:2007/06/15 10:21

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q関数を使わないで日付の計算をしたいのですが。

2007,7,5,4
年,月,日,曜日(0が日曜日)
の入力から、+10日後の日付と曜日を求めたいのですが、
関数を一切使わず計算式と場合分けのみで計算する方法ありますか?

今考えている方法は、地道に、ある基準1970年1月1日をシリアル値で0としそこから、今日までの日数を1月は31、2月は28にちなどを考えてを足していき、シリアル値を算出。
そのシリアル値に+10した値を再び年月日曜日の形に戻す方法しか無いのかと考えています。

Aベストアンサー

★最初に
>関数を一切使わず計算式と場合分けのみで計算する方法ありますか?
 ↑
 あります。というか関数で記述することを記述すればよいだけ。
 汎用性も考えれば専用の関数か、日付操作ライブラリを自作して使いまわした方が
 良いと思いますよ。
>今考えている方法は、地道に、ある基準1970年1月1日をシリアル値で0としそこから、
>今日までの日数を1月は31、2月は28にちなどを考えてを足していき、シリアル値を算出。
>そのシリアル値に+10した値を再び年月日曜日の形に戻す方法しか無いのかと考えています。
 ↑
 私もこの方法しか思い浮かびません。
 このときの注意点として『閏年』の判定、扱いをきちんと考えないと正確になりません。
 表計算で有名な Excel について興味深い記事を見つけましたので読んで見て下さい。
 http://pc.nikkeibp.co.jp/article/NPC/20070530/272938/→『DATEDIF関数の“受難” (第1回)』
 ↑
 3ページ目です。
>10日ではなくて10000日だったらどんな感じになりますか?
 ↑
 加減算する日付を西暦1年1月1日を1としたシリアル値に変換して加算、減算とするしかないかな。
・開始日を Y1/M1/D1 とすると
 days = ((Y1 * 365) + (Y1 / 4) - (Y1 / 400 * 3)) + FuncGetDays(Y1,M1,D1);
 days += 10000; ←加算
 days -= 10000; ←減算
 この days のシリアル値を年、月、日に変換します。
 ※FuncGetDays() 関数は年月日から1月1日の日数を計算する関数です。
 ※この関数の詳しい内容は下の『参考URL』をどうぞ。回答者No.2です。
 ※そのたも参考になります。
・以上。

参考URL:http://oshiete1.goo.ne.jp/qa3085731.html

★最初に
>関数を一切使わず計算式と場合分けのみで計算する方法ありますか?
 ↑
 あります。というか関数で記述することを記述すればよいだけ。
 汎用性も考えれば専用の関数か、日付操作ライブラリを自作して使いまわした方が
 良いと思いますよ。
>今考えている方法は、地道に、ある基準1970年1月1日をシリアル値で0としそこから、
>今日までの日数を1月は31、2月は28にちなどを考えてを足していき、シリアル値を算出。
>そのシリアル値に+10した値を再び年月日曜日の形に戻す方法しか無いのか...続きを読む

Qカレンダープログラム、曜日の出力について。

西暦、月、日(1900年1月1日以降対象)を入力して、曜日を求めるプログラムを考えています。
過去の質問なども参考にしたんですが、プログラムがうまく動かず困っています。
プログラム中コメントの日数を変える辺りに不備があるかと思い色々試しましたが、曜日がずれてしまいますし開始1900年1月1日も月曜なのに水曜と出力されます。
forやifの条件の記述がおかしいのでしょうか?
なにか根本的な所が欠如しているのでしょうか、回答をよろしくお願いします。
始めのgetsの所はあえてです。
#include<stdio.h>
#include<stdlib.h>

int main(void)

{
char ss[9],cop1[5],cop2[3],cop3[3];

int year,manth,day,aa,cc,i,f;

gets(ss);

ss[8] = 0;
cop1[4] = 0;
cop2[2] = 0;
cop3[2] = 0;

for(i = 0;i <= 3;i++){
cop1[i] = ss[i];
}
for(i = 0;i <= 1;i++){
cop2[i] = ss[i+4];
}
for(i = 0;i <= 1;i++){
cop3[i] = ss[i+6];
}
year = atoi(cop1);
manth = atoi(cop2);
day = atoi(cop3);

printf("%d年%d月%d日",year,manth,day);

for(f = 1900; f <= year; f++){  //閏年か平年で日数を変える
if((year%4) == 0 && year != 1900){
aa += 366;
}
else{
aa += 365;
}
}
if((year%4) == 0 && manth >= 3 ){//閏年かつ3月以降日数+1
aa += 1;
}

switch(manth){
case 1:
cc = (aa + day)%7;
break;
case 2:
cc = (aa + 31 +day)%7;
break;
case 3:
cc = (aa + 59 +day)%7;
break;
case 4:
cc = (aa + 90 +day)%7;
break;
case 5:
cc = (aa + 120 +day)%7;
break;
case 6:
cc = (aa + 151 +day)%7;
break;
case 7:
cc = (aa + 181 +day)%7;
break;
case 8:
cc = (aa + 212 +day)%7;
break;
case 9:
cc = (aa + 243 +day)%7;
break;
case 10:
cc = (aa + 273 +day)%7;
break;
case 11:
cc = (aa + 304 +day)%7;
break;
case 12:
cc = (aa + 334 +day)%7;
break;
}
switch(cc){
case 1:
printf(" (月)");
break;
case 2:
printf(" (火)");
break;
case 3:
printf(" (水)");
break;
case 4:
printf(" (木)");
break;
case 5:
printf(" (金)");
break;
case 6:
printf(" (土)");
break;
case 0:
printf(" (日)");
break;
}

return 0;

}

西暦、月、日(1900年1月1日以降対象)を入力して、曜日を求めるプログラムを考えています。
過去の質問なども参考にしたんですが、プログラムがうまく動かず困っています。
プログラム中コメントの日数を変える辺りに不備があるかと思い色々試しましたが、曜日がずれてしまいますし開始1900年1月1日も月曜なのに水曜と出力されます。
forやifの条件の記述がおかしいのでしょうか?
なにか根本的な所が欠如しているのでしょうか、回答をよろしくお願いします。
始めのgetsの所はあえてです。
#include<stdio...続きを読む

Aベストアンサー

★追記。
・質問者さんのソースを元に書き直してみました。
 参考にして下さい。

#include <stdio.h>

// break 付きのキーワード
#define CASE  break;case
#define DEFAULT  break;default

// 西暦から閏年の判定
int FuncIsLeapYear( int year )
{
 if ( !(year % 3200) ) return 0;  // 平年(3200で割れる年)
 if ( !(year % 400) ) return 1;  // 閏年(400で割れる年)
 if ( !(year % 100) ) return 0;  // 平年(100で割れる年)
 if ( !(year % 4) ) return 1;  // 閏年(4で割れる年)
 return 0;       // 平年(それ以外の年)
}

// メイン関数
int main( void )
{
 static const char *table[] = { "日", "月", "火", "水", "木", "金", "土" };
 // 宣言
 int year = 0;
 int month = 0;
 int day = 0;
 int week = 0;
 // カウンタ
 int days;
 int i;
 
 // 年月日の入力
 scanf( "%04d/%02d/%02d", &year, &month, &day );
 // 1900年以降の日数を数える
 for ( days = 0, i = 1900 ; i < year ; i++ ){
  days += (FuncIsLeapYear(i) ? 366 : 365);
 }
 // 1月1日から日数を加える
 switch ( month ){
  CASE 1: days += 0;
  CASE 2: days += 31;
  CASE 3: days += 31 + 28;
  CASE 4: days += 31 + 28 + 31;
  CASE 5: days += 31 + 28 + 31 + 30;
  CASE 6: days += 31 + 28 + 31 + 30 + 31;
  CASE 7: days += 31 + 28 + 31 + 30 + 31 + 30;
  CASE 8: days += 31 + 28 + 31 + 30 + 31 + 30 + 31;
  CASE 9: days += 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31;
  CASE 10: days += 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30;
  CASE 11: days += 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31;
  CASE 12: days += 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30;
 }
 // 3月以降で閏年なら+1日
 if ( (month >= 3) && FuncIsLeapYear(year) ){
  days++;
 }
 // 入力した日付を加える
 days += day;
 // 曜日の計算
 week = (days % 7);
 // 表示
 printf( "year = %d\n", year );
 printf( "month = %d\n", month );
 printf( "day = %d\n", day );
 printf( "week = %s\n", table[week] );
 return 0;
}
以上。

★追記。
・質問者さんのソースを元に書き直してみました。
 参考にして下さい。

#include <stdio.h>

// break 付きのキーワード
#define CASE  break;case
#define DEFAULT  break;default

// 西暦から閏年の判定
int FuncIsLeapYear( int year )
{
 if ( !(year % 3200) ) return 0;  // 平年(3200で割れる年)
 if ( !(year % 400) ) return 1;  // 閏年(400で割れる年)
 if ( !(year % 100) ) return 0;  // 平年(100で割れる年)
 if ( !(year % 4) ) return 1;  // ...続きを読む

Q万年カレンダーのC言語プログラムを教えてください

西暦年と月を入力すると、その年月のカレンダーが出てくる万年カレンダーのC言語プログラムを教えてください!お願いします!

Aベストアンサー

>ありがとうございます。さっそくプログラムを動かしてみました。でも第一週と第二週の日付がうまくつながっていないです。これはどうしてでしょうか?

ごめんなさい。
htmlの半角スペースを連続して書くと1個にされてしまう事を忘れていました。
下から7行目の
printf(" ");
の半角スペース1個は半角スペース3個にしてください。

Qうるう年を求めるプログラム

1900から2100年のうるう年を求めるプログラムが作りたいのですが
どうしたらよいでしょうか?

forを使って1900から2100までかぞえる。
4で割り切れるものは閏年
しかし100で割り切れるものは閏年でない。
400で割り切れるものは閏年である。
最後の条件の分岐から閏年のみを表示させたいのですがわかりません

Aベストアンサー

int uruu;
int year;
for(year = 1900; year <= 2100; year++) {
 if(year % 4 == 0) {
  // 4で割り切れる
  if(year % 100 == 0) {
   // 100で割り切れる
   if(year % 400 == 0) {
    // 400で割り切れる = 閏年
    uruu = 1;
   } else {
    // 400で割り切れない = 閏年では無い
    uruu= 0;
   }
  } else {
   // 100で割り切れない = 閏年
   uruu = 1;
  }
 } else {
  // 4で割り切れない = 閏年ではない
  uruu = 0;
 }

 if(uruu == 1) {
  // 閏年のときの処理
 } else {
  // 閏年ではないときの処理
 }
}

規則どおりに書くのならこうなるね。
改良の余地はあるかもしれないけど。

int uruu;
int year;
for(year = 1900; year <= 2100; year++) {
 if(year % 4 == 0) {
  // 4で割り切れる
  if(year % 100 == 0) {
   // 100で割り切れる
   if(year % 400 == 0) {
    // 400で割り切れる = 閏年
    uruu = 1;
   } else {
    // 400で割り切れない = 閏年では無い
    uruu= 0;
   }
  } else {
   // 100で割り切れない = 閏年
   uruu = 1;
  }
 } else {
  // 4で割り切れない = 閏年ではない
  uruu = 0;
...続きを読む

Q2038年問題 日付算出

2038年問題で困っています。
指定した日付から指定した日数後および前(マイナス日数)の日付を取得するCのプログラムがあります。
これまでmktime関数をして算出していましたが、2038年以降の日付を指定するとエラーになるようです。
上記関数をmktime関数を使用せず自前で作成しないといけないのですが、どう作っていいか思いつきません。どなたか参考になるようなサイト、およびサンプルプログラムがあれば教えていただきたいと思っています。
よろしくお願いします。

Aベストアンサー

★アドバイス
・一般的に次の方法を取ります。
 (1)年月日を西暦1年1月1日からの経過日数に変換
 (2)経過日数に(期間の)日数を加算/減算
 (3)西暦1年1月1日からの経過日数を年月日に変換
 このうち(3)が最も面倒な作業になります。多分。
 下に(3)だけですがサンプルを載せておきます。

サンプル:
// 閏年の判定
int isLeapYear( int year )
{
 if ( !(year % 3200) ) return 0;
 if ( !(year % 400) ) return 1;
 if ( !(year % 100) ) return 0;
 return !(year & 0x3);
}

// 経過日数から年月日を求める
int sample3( int days, int *year, int *month, int *day )
{
 static int table[ 13 ] = {
  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
 }; int Y, M, D, ddd;
 
 // [日]コピー
 D = days;
 // [年]算出
 for ( Y = 1 ; D > (ddd = (isLeapYear(Y) ? 366 : 365)) ; Y++ ){
  D -= ddd;
 }
 // [月]算出
 table[ 2 ] = ((ddd == 365) ? 28 : 29);
 for ( M = 1 ; D > table[M] ; M++ ){
  D -= table[ M ];
 }
 // セット
 *year = Y;
 *month = M;
 *day = D;
 return days % 7; // 曜日を返す(0-6)
}

その他:
・上記のソースは『経過日数を年月日に変換』する考え方のサンプルです。
 そのため計算スピードが低速で実用にはなりません。
 その代わりに正しく変換してくれます。
 答え合わせようの関数です。
・実際に利用するためには『年の算出』を除算と剰余を使って計算させます。
 考え方は
 (1)経過日数から3200年間の日数(1168776日)を幾つ引けるかを除算で求める。
 (2)(1)のあまりの経過日数から400年間の日数(146097日)を幾つ引けるかを除算で求める。
 (3)(2)のあまりの経過日数から100年間の日数(36524日)を幾つ引けるかを除算で求める。
 (4)(3)のあまりの経過日数から4年間の日数(1461日)を幾つ引けるかを除算で求める。
 (5)(4)のあまりの経過日数から1年間の日数(365日)を幾つ引けるかを除算で求める。
 (6)(1)-(5)までで求めた除算の商と年数を掛けて加算すると『年』が求まる。
 (7)(5)のあまりが年内の1月1日からの通算日数となります。これを『月』『日』に分ける。
・詳しい解説などは下の『参考URL』をどうぞ。
 そこで紹介している『経過日数から年月日と曜日を求める』なら実用的なスピードで
 計算してくれます。ただし有効範囲は3199年までです。3200年以降は1日ずつずれます。
 これは3200年以降の閏年を考慮していないためです。でも誤差1日ですが。
・以上。

参考URL:http://cl.is.kyushu-u.ac.jp/Literacy/PP/H14/adp/program/date.html

★アドバイス
・一般的に次の方法を取ります。
 (1)年月日を西暦1年1月1日からの経過日数に変換
 (2)経過日数に(期間の)日数を加算/減算
 (3)西暦1年1月1日からの経過日数を年月日に変換
 このうち(3)が最も面倒な作業になります。多分。
 下に(3)だけですがサンプルを載せておきます。

サンプル:
// 閏年の判定
int isLeapYear( int year )
{
 if ( !(year % 3200) ) return 0;
 if ( !(year % 400) ) return 1;
 if ( !(year % 100) ) return 0;
 return !(year & 0x3);
}

// 経...続きを読む

QC言語でのカレンダー作成

今、C言語でカレンダーを表示させるプログラムを作成しています

西暦と月を入力すると、その年、その月の1ヶ月分のカレンダーが表示されるプログラムを作りました

そこで、次は入力した月から3ヶ月分のカレンダーを表示させるプログラムを作成したいのですが、どなたか教えてくださいませんか?

配列を使う方法や文字列など色々な方法があると伺って、どの方法が作りやすいのかわからないんです・・・

borland c を使用しています

Aベストアンサー

こんにちは。

前提として、作成するプログラムがコンソールアプリで、コンソール画面
にカレンダーを表示する場合ですが。。。
※以下は、あくまで一例です。

■縦方向に並べて表示する場合
※こちらは、#1さんの言われるように、単純に作成済みの1ヶ月カレンダー
 表示プログラムを、3回呼ぶだけで良いと思います。

1)既に作成済みのカレンダー表示を行うプログラムを関数化します。
  例)
  void DispMonth( int nYear, int nMonth );
   int nYear  : カレンダーの年
   int nMonth : カレンダーの月

2)入力された[年]、[月]を基準にして、forループなどで、[月]及び[年]を
  変えながら、1)の関数を呼出す。

 入力された[月]を中央にして、前後3ヶ月分のカレンダーを表示する場合、
 ================================================
 int nYear, nMonth;
 int m, nYY, nMM;
 nYear = 2010;  /* ←入力[年]とします */
 nMonth = 11;   /* ←入力[月]とします */
 /* 月単位のループ処理 */
 for ( m=nMonth-1; m<=nMonth+1; ++m ) {
   nYY = nYear;
   nMM = m;
   if ( nMM < 1 ) {
     nMM += 12;
     nYY--;
   }
   else if ( nMM > 12 ) {
     nMM -= 12;
     nYY++;
   }
   DispMonth( nYY, nMM );
   /* 必要であれば、ここで空白行などを出力し間隔を調整する */
 }
 ================================================
 のような処理になると思います。

■横方向に並べて表示する場合
※こちらの場合は、少し工夫が必要になります。

1)まず、作成済みのカレンダー表示を行うプログラムを組み直して、週単位
  (第1週~第6週)で表示を行う関数にします。
  例)
  void DispWeek( int nYear, int nMonth, int nWeek );
   int nYear  : カレンダーの年
   int nMonth : カレンダーの月
   int nWeek : 週単位のインデックス(1~6)
 ※出力の際、行末には改行を付加しないようにしておきます。
 ※週インデックスが、その月の週の数を越えた場合は、空白(スペース等)
  を出力するようにしておきます。

2)入力された[年]、[月]を基準にして、forループなどで、[月]、[年]、及び、
  [週のインデックス]を変えながら、1)の関数を呼出す。

 入力された[月]を中央にして、前後3ヶ月分のカレンダーを表示する場合、
 ================================================
 int nYear, nMonth;
 int m, nYY, nMM, nWeek;
 nYear = 2010;  /* ←入力[年]とします */
 nMonth = 11;   /* ←入力[月]とします */
 /* 必要であれば、ここで年、月、及び、曜日名などの表示を行う */
 ~~~
 /* 週単位のループ処理 */
 for ( nWeek=1; nWeek<=6; ++nWeek ) {
   /* 月単位のループ処理 */
   for ( m=nMonth-1; m<=nMonth+1; ++m ) {
     nYY = nYear;
     nMM = m;
     if ( nMM < 1 ) {
       nMM += 12;
       nYY--;
     }
     else if ( nMM > 12 ) {
       nMM -= 12;
       nYY++;
     }
     DispWeek( nYY, nMM, nWeek );
     /* 必要であれば、ここでスペースなどを出力し間隔を調整する */
   }
   /* ここで改行を出力する */
 }
 ================================================
 のような処理になると思います。

※GUIアプリの場合でも、基本的に月毎に表示位置をずらすようにすれば良い
 と思います。

以上です。

こんにちは。

前提として、作成するプログラムがコンソールアプリで、コンソール画面
にカレンダーを表示する場合ですが。。。
※以下は、あくまで一例です。

■縦方向に並べて表示する場合
※こちらは、#1さんの言われるように、単純に作成済みの1ヶ月カレンダー
 表示プログラムを、3回呼ぶだけで良いと思います。

1)既に作成済みのカレンダー表示を行うプログラムを関数化します。
  例)
  void DispMonth( int nYear, int nMonth );
   int nYear  : カレンダーの年
   int nMonth : カレン...続きを読む


人気Q&Aランキング