2012-04-29 15 views
5

私はProgramming、Javaについては初めてです。私はProject EulerのWebサイトで作業して自分自身を教えようとしています。私はこの問題を完了しようとしています:http://projecteuler.net/problem=19、である:Java、1901年から2000年の1月に落ちた日曜日のカウント数

日曜日は二十 世紀の間に月の最初に落ちたどのように多くの(1901年1月1日は、2000年12月31に)?

私はそれを解決するために考えた方法は、カレンダーを表す2D配列を作成し、7までカウントして配列をループし、7までカウントするたびにそのポイントに1を加算することでした配列内にあります。結局、私は配列の最初の行を合計し、それは月の初めに何回の日曜日だったのでしょうか?

私のループに問題があります。月末になると7回カウントされてリセットされますが、それをやめる方法を理解できません。ここで

が私のコードです:

public class Problem019 { 
    public static void main (String[] args){ 

     //System.out.println(LeapYearTest(1996)); 
     int ThirtyOne = 31; 
     int Thirty = 30; 
     int FebNorm = 28; 
     int FebLeap = 29; 
     int a, b, c, Day, e = 0, f = 0; 
     int Calander[] []= new int [12] [] ; 

     Calander[0] = new int [ThirtyOne]; 
     Calander[1] = new int [FebNorm]; 
     Calander[2] = new int [ThirtyOne]; 
     Calander[3] = new int [Thirty]; 
     Calander[4] = new int [ThirtyOne]; 
     Calander[5] = new int [Thirty]; 
     Calander[6] = new int [ThirtyOne]; 
     Calander[7] = new int [ThirtyOne]; 
     Calander[8] = new int [Thirty]; 
     Calander[9] = new int [ThirtyOne]; 
     Calander[10] = new int [Thirty]; 
     Calander[11] = new int [ThirtyOne]; 

     for (a=1901;a<2001;a++){ 
      //System.out.println(a); 
      if (LeapYearTest(a)) 
      { 
       Calander[1] = new int [FebLeap]; 
      } 
      else 
      { 
       Calander[1] = new int [FebNorm]; 
      } 

      for (e=0;e<Calander.length;e++) 
      { 
       System.out.println("e: " + e); 
       f=0; 

       while (f<Calander[e].length) 
       { 

        //System.out.println(Calander[e].length); 
        Day=1; 
        while (Day<8 && f<Calander[e].length) 
        { 
         System.out.println("f: " + f + "\tDay: " + Day + "\tCalander[e][f]: " + Calander[e][f]); 
         Day++; 
         f++; 

         if (f<Calander[e].length && f!=0 && Day==7) 
         { 
         Calander[e][f]+= 1; 
         } 

        } 

       } 
      } 
      //System.out.println(a); 
     } 
     for (b=0;b<Calander.length;b++) 
     { 
      System.out.print(Calander[0][b]); 
     } 
    } 


    public static boolean LeapYearTest(int x) 
    { 
     if (x%4==0 || x%400==0){ 
      return true; 
     } 
     if (x%100==0){ 
      return false; 
     } 
     else return false; 
    } 

} 

これは、eが月で、それが印刷さ何で、fは月の日で、日が7にカウントされます。

どう
f: 25 Day: 5 Calander[e][f]: 0 
f: 26 Day: 6 Calander[e][f]: 0 
f: 27 Day: 7 Calander[e][f]: 100 
f: 28 Day: 1 Calander[e][f]: 0 
f: 29 Day: 2 Calander[e][f]: 0 
**f: 30 Day: 3 Calander[e][f]: 0** 
e: 10 
**f: 0 Day: 1 Calander[e][f]: 0** 
f: 1 Day: 2 Calander[e][f]: 0 
f: 2 Day: 3 Calander[e][f]: 0 

今月の終わりに日がリセットされないようにループを設定することはできますか?または、非常に多くのネストされたループを伴わないこの問題を解決する別の方法がありますか?

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

+3

その体を簡素化するための方法を比較し変更。あなたは繰り返し、Sun 1stの総数を増やすだけです。カウンタ0-6と並行して反復し、現在の月の日数に移動する別のカウンタを繰り返します。 –

+1

さらに、毎日繰り返すだけでなく、月の初めにある日曜日を計算する、より巧妙な方法があります。他に誰もあなたのコードを見ないならば重要ではありませんが、* Calendar *のスペルを間違えてしまいます。また、Javaのコード規約ではcamelCaseの変数名(最初の文字は小文字)を使うことをお勧めします。 – rob

+0

@マルコ:申し訳ありませんが理解していない、あなたは精巧にできますか? – Keith

答えて

3

私はあなたの既存のコードを投げ捨て、新鮮なものから始める必要があると思います。 Project Eulerの問題を解決してコードを作成する方法を学習しようとしているので、私はあなたにコードを与えることによって楽しいことを忘れることはありません。あなたは完全な作業コードが必要だと思われるので、誤解や見落としがある可能性がある問題文の微妙な詳細のために存在していたいくつかのバグを含め、コードを修正しました。

楽しみのためだけに、この行を置き換え、これを1に初期化し、あなたが最初にDayを宣言するときに、あなたが固定したいあなたのコードで当面の問題...

を見てみましょう:

Day=1; 

if (Day > 7) { 
    Day = 1; 
} 

と月の日数を乗り越えループの内側に移動します。これで

しかし、まだ重大な問題があります。毎年2月の配列を上書きし続けます。一度だけ初期化し、その長さを29に設定する必要があります。しかし、これはまた、calendar[month].lengthに依存するループを破るという不幸な副作用があります。そのためにも考慮する必要があります。

本当に追跡する必要があるのは、月の初めに落ちた日曜日の数なので、1つの変数を格納して増やすだけです。これは、2月の配列を上書きするという前述の問題を解決します。なぜなら、2月の配列をもう使用しないからです。一方、実際に配列を使用して練習したい場合は、3次元配列を使用することができます(追加次元は年です)。しかし、ほとんどのJavaプログラマはほとんどの場合、配列の代わりにリストを使用していると推測しています。配列を使用するときは、複数の次元の配列を使用することはほとんどありません。

さらにいくつかのノート

あなたの外側のwhileループは冗長です。

LeapYearTestメソッドでは、100で割り切れるすべてのうるう年(100で割り切れるすべてを4で割り切れるので、年を100で割り切れるようにテストするifブロックを決して入力しません)の場合、trueを誤って返します。

最後に、(毎月の初日にループするのではなく)1月の毎日をループしています。

はまた、問題のある状態ことに注意し、

1月1日は月曜日でした。

しかし、あなたは日曜日が1月1日から始まる見つけることになっています。

これらのバグやその他のバグ(ループ内の状態など)を修正した後、以下にコードの完全版を含めました。モジュラス(%)演算子をさらに使い、月の他の日に日曜日の数を計算しないことで、これを短時間で実行するように簡単に最適化できることに注意してください。 )。

public class Problem019 { 
    public static void main (String[] args){ 

     final int ThirtyOne = 31; 
     final int Thirty = 30; 
     final int FebNorm = 28; 
     final int FebLeap = 29; 
     int numOfSundays = 0; 

     int calendar[][]= new int [12][]; 

     calendar[0] = new int [ThirtyOne]; 
     calendar[1] = new int [FebLeap]; 
     calendar[2] = new int [ThirtyOne]; 
     calendar[3] = new int [Thirty]; 
     calendar[4] = new int [ThirtyOne]; 
     calendar[5] = new int [Thirty]; 
     calendar[6] = new int [ThirtyOne]; 
     calendar[7] = new int [ThirtyOne]; 
     calendar[8] = new int [Thirty]; 
     calendar[9] = new int [ThirtyOne]; 
     calendar[10] = new int [Thirty]; 
     calendar[11] = new int [ThirtyOne]; 

     int dayOfWeek = 1; 
     for (int year = 1900; year < 2001; year++) { 
      for (int month = 0; month < calendar.length; month++) { 
       int dayOfMonth=0; 

       int daysInMonth; 
       if (month == 1) { 
        daysInMonth = isLeapYear(year) ? FebLeap : FebNorm; 
       } 
       else { 
        daysInMonth = calendar[month].length; 
       } 

       while (dayOfWeek < 8 && dayOfMonth < daysInMonth) { 
        System.out.println("year: " + year + "\tday: " + dayOfWeek 
          + "\tcalendar["+month+"]["+dayOfMonth+"]: " + calendar[month][dayOfMonth]); 

        if (dayOfWeek == 7 && year > 1900) { 
         calendar[month][dayOfMonth]++; 

         if (dayOfMonth == 0) { 
          numOfSundays++; 
         } 
        } 

        dayOfMonth++; 

        dayOfWeek++; 
        if (dayOfWeek > 7) { 
         dayOfWeek=1; 
        } 
       } 
      } 
     } 

     for (int month = 0; month < calendar.length; month++) { 
      System.out.println(calendar[month][0]); 
     } 

     System.out.println(numOfSundays); 
    } 

    public static boolean isLeapYear(int year){ 
     if (year % 400 == 0) { 
      return true; 
     } 
     else if (year % 100 == 0) { 
      return false; 
     } 
     else if (year % 4 == 0){ 
      return true; 
     } 
     else { 
      return false; 
     } 
    } 
} 

これもまた、かなり改善される可能性があります。たとえば、年と月を繰り返しループし、Javaの組み込みカレンダーAPIまたはサードパーティのAPIを使用して、月の最初の日が日曜日であるかどうかを確認できますが、おそらく問題を解決する最もクールな方法です自分でDoomsday Algorithmを実装することです。これにより、java.util.Calendarを使用せずに、任意の日付の曜日を簡単に計算できます。

Doomsday Algorithmを実装したら、必ずしも毎月すべてのループを繰り返す必要はないので、さらに最適化を行うことができます。たとえば、isSunday(MAR,1,year) == (! isLeapYear(year)) && isSunday(FEB,1,year)です。

+0

ありがとうございます、しかし、私はまだずらした2次元配列、各列のカウントをリセットせずに7までカウントすると、 – Keith

+0

実際にコードにいくつかの問題があります。私はいくつかの問題を指摘するために私の答えを更新しました。 – rob

+0

ありがとう、私は分かった。 しかし、私はあなたのコードを使用すると、dayoftheMonth = 0なら配列に1を加えません:if(dayOfMonth 1900) { カレンダー[月] [dayOfMonth] + = 1; } – Keith

4

私は、1901年から2001年にかけて年を増やす外側のループと、Jan - > Decをチェックし、その月の最初の日曜日だったかどうかを確認する内部ループを持つほうがはるかに迅速です。

合計100回の反復、10行のコード、上端。

編集:これを拡張します。

2つの方法で問題を解決できます。すべての日曜日を見て、1か月の初めかどうかを確認するか、すべての月の最初の日を見て、その日曜日かどうかを確認します。

未テストコード:

Calendar calendar = Calendar.getInstance(); 
int count = 0; 
for(int i=1901;i<2000;i++){ 
    for(int j=1;i<12;j++){ 
     calendar.set(Calendar.YEAR, i); 
     calendar.set(Calendar.MONTH,j); 
     calendar.set(Calendar.DAY,1); 
     if(calendar.get(Calendar.DAY_OF_WEEK).equals(Calendar.SUNDAY)){ 
      count++; 
     } 
    } 
} 

のSystem.out.println(カウント)。

+0

申し訳ありませんが、私はあなたが意味することを理解していません。内側ループは正確に何をしていますか?その月の初めが日曜日だったかどうか私はどうすればわかりますか?もっと詳しく説明できますか?乾杯。 – Keith

+0

私は自分の答えを編集して展開します。 – PaulJWilliams

+0

@Keith:Javaの組み込みカレンダーAPIを使用して、DayOfWeekを計算することができます。プロジェクトオイラーで不正行為とはみなされないと仮定します。 – rob

1

私の提案です。それはグレゴリオ暦を使って日付を特定し、それが日曜日であればそれを識別します。

import java.util.Date; 
import java.util.GregorianCalendar; 

public class SundayOfXX { 

    public static void main(String [] argv) { 
     int counter = 0; 
     for (int year = 1901, last_year = 2000; year <= last_year ; year++) { 
      for (int month = 1, last_month = 12; month <= last_month ; month++) { 
       Date d = new GregorianCalendar(year,month-1,1).getTime(); // GregorianCalendar use 0 for January 
       if (d.getDay() == 0) { // sunday is day number 0 
        counter++; 
        System.out.println(String.valueOf(counter) + " " + d); 
       } 
      } 
     } 
     System.out.println("Total sunday in XX century: "+counter); 
    } 
} 

このソリューションは完全にテストされています。それは20世紀に1日の1日である171の日曜日を見つける。

+0

「171 * 7 = 1197」、1197年は4年未満であるため、20世紀は4年しかありませんでしたか? –

+0

目に見えても、問題の定義を読んだことはありません。それは1901年から2000年の1か月の第1日である171日です。しかし、私はあなたの意見を取ります。私はより明示的に私の投稿を編集しました... – Nicocube

1

これを試してみてください:これはあなたのコードをクリーンアップし、簡素化され

import java.util.Calendar; 
public class Problem019 { 

    public static void main (String[] args){ 

     Calendar calendar = Calendar.getInstance(); 
     int countFirstSunday = 0; 
     for(int year = 1901; year <= 2000 ; year++) { 
      for(int month = 0; month <= 11; month++) { 
       calendar.set(year, month, 1); 
       if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) { 
        countFirstSunday++; 
       } 
      } 
     } 
     System.out.println("Sundays as the first of month: " + countFirstSunday); 
    } 

} 
0

public static void main(String[] args) { 
    final int[] mLens = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 
    int dow = 0; // initialize to day of week on Jan 1, 1901 
    int suns = 0; 
    for (int y = 1901; y < 2001; y++) 
    for (int m = 0; m < mLens.length; m++) { 
     if (dow++ % 7 == 0) suns++; 
     final int mLen = mLens[m] + leapAdd(y, m); 
     for (int d = 1; d < mLen; d++) dow++; 
    } 
    System.out.println(suns); 
} 

static int leapAdd(int y, int m) { 
    if (m != 1) return 0; 
    if (y % 4 == 0 || y % 400 == 0) return 1; 
    return y % 100 == 0 ? 0 : 1; 
} 

ここ
public static void main(String[] args) { 
    final int thirtyOne = 31, thirty = 30; 
    final int calendar[][] = new int[12][]; 
    final int[] febLeap = new int[29]; 
    final int[] febNorm = new int[28]; 
    calendar[0] = new int[thirtyOne]; 
    calendar[2] = new int[thirtyOne]; 
    calendar[3] = new int[thirty]; 
    calendar[4] = new int[thirtyOne]; 
    calendar[5] = new int[thirty]; 
    calendar[6] = new int[thirtyOne]; 
    calendar[7] = new int[thirtyOne]; 
    calendar[8] = new int[thirty]; 
    calendar[9] = new int[thirtyOne]; 
    calendar[10] = new int[thirty]; 
    calendar[11] = new int[thirtyOne]; 
    int dow = 0; // set to day of week for Jan 1 1901 
    for (int y = 1901; y < 2001; y++) { 
    calendar[1] = leapYearTest(y)? febLeap : febNorm; 
    for (int m = 0; m < calendar.length; m++) 
     for (int d = 0; d < calendar[m].length; d++) 
     if (dow++ % 7 == 0) calendar[m][d]++; 
    } 
    int sumSundays = calendar[0][0] + febLeap[0] + febNorm[0]; 
    for (int i = 2; i < calendar.length; i++) sumSundays += calendar[i][0]; 
    System.out.println("Number of Sundays is " + sumSundays); 
} 

public static boolean leapYearTest(int x) { 
    if (x % 4 == 0 || x % 400 == 0) 
    return true; 
    return x % 100 != 0; 
} 

は、私はあなたが配列を必要としない言ったとき、私は何を意味するかです

しかし、すぐにあなたは、月の中を走るその内側のループには意味がないことに気付きます。それは、まさにモジュロ7です。だから、内側のループは私が(擬似コード)のようにそれを行うだろう

for (int m = 0; m < mLens.length; m++) { 
     if (dow == 0) suns++; 
     final int mLen = mLens[m] + leapAdd(y, m); 
     dow = (dow + mLen) % 7; 
    } 
0

を言う必要があります。一つは、はるかに少ない2D配列を任意の配列を必要としない

class MyDate { ... } // support adding a number of days and comparing with another MyDate 
MyDate end = new MyDate(31. Dec 2000) 
MyDate start = new MyDate(first sunday in 20th century) 
int count = start.mday == 1 ? 1 : 0; 
start.add(7); 
while (start < end) (
    if (start.mday == 1) count++; 
    start.add(7); 
} 

注意。 (単純な定数配列はokです使用して、MyDateに該当のaddメソッドでは、しかし、月の長さを取得するには。)

0

はここ2つのソリューションです:

1)Calendarを使用して - それは、よりシンプルで、

  • 135ミリ

    ​​

    ていることに注意してください - それはそれほど効率的ではありませんDAY_OF_MONTHおよびDATEは同等です。

  • 最初の日が1であっても、最初の月が0であり、1ではないため、Calendar.JANUARYを使用しました。

2)自身Dateクラスを使用する - それが唯一の1.65ミリ取りのみ:日曜日(addDays(7))を介して

public class P19 { 

    public static void main(String[] args) { 
     // 1 Jan 1900 - Monday 
     // 1900 is not leap => it has 365 days 
     // 365 % 7 = 1 => 1 Jan 1901 - Tuesday => 6 Jan 1901 - Sunday 

     int yearStart = 1901, yearEnd = 2000; 
     int monthStart = 1, monthEnd = 12; 
     int dayStart = 6, dayEnd = 31; 
     Date dateStart = new Date(yearStart, monthStart, dayStart); 
     Date dateStop = new Date(yearEnd, monthEnd, dayEnd); 

     int result = 0; 
     while (Date.compareDates(dateStart, dateStop) < 0) { 
      if (dateStart.day == 1) { 
       result++; 
      } 
      dateStart.addDays(7); 
     } 
     System.out.println(result); 
    } 

} 

class Date { 
    int year; 
    int month; 
    int day; 

    Date(int year, int month, int day) { 
     this.year = year; 
     this.month = month; 
     this.day = day; 
    } 

    public void addDays(int days) { 
     int numberOfDaysForMonth = getTotalMonthDays(month, year); 
     day += days; 
     if (day >= numberOfDaysForMonth) { 
      day -= numberOfDaysForMonth; 
      month++; 
      if (month > 12) { 
       month = 1; 
       year++; 
      } 
     } 

    } 

    public static int compareDates(Date d1, Date d2) { 
     if (d1.year == d2.year && d1.month == d2.month && d1.day == d2.day) { 
      return 0; 
     } 
     if (d1.year < d2.year) { 
      return -1; 
     } 
     if (d1.year == d2.year && d1.month < d2.month) { 
      return -1; 
     } 
     if (d1.year == d2.year && d1.month == d2.month && d1.day < d2.day) { 
      return -1; 
     } 
     return 1; 
    } 

    private int getTotalMonthDays(int m, int y) { 
     if (m == 2 && isLeapYear(y)) { 
      return 29; 
     } 
     if (m == 2) { 
      return 28; 
     } 
     if (m == 4 || m == 6 || m == 9 || m == 11) { 
      return 30; 
     } 
     return 31; 
    } 

    private boolean isLeapYear(int y) { 
     if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) { 
      return true; 
     } 
     return false; 
    } 

} 

この実装の繰り返し処理を。

いくつかの可能な改善:

  • 増加ステップ(例えば、:1ため、我々は任意の日スキップせず28の代わり7を追加することができます)
  • インクルードがbooleanを返すようにし、すべてのそれらの配列が行き過ぎている
0
public class CountingSundays { 

    public static void main(String[] args) { 

     int lastDayOfPreviousMonth = 6; //31 Dec 1899 is Sunday as 1 Jan 1900 is Monday 

     int countOfSundayOnFirstOfMonth = 0; 

     for (int year = 1900; year <= 2000; year++) { 
      for (int month = 1; month <= 12; month++) { 
       int dayOnFirstOfThisMonth = (lastDayOfPreviousMonth + 1) % 7; 
       if (year > 1900 && dayOnFirstOfThisMonth == 6) 
        countOfSundayOnFirstOfMonth++; 
       switch (month) { 
       case 1: // Jan 
       case 3: // Mar 
       case 5: // May 
       case 7: // Jul 
       case 8: // Aug 
       case 10: // Oct 
       case 12: // Dec 
        lastDayOfPreviousMonth = (lastDayOfPreviousMonth + 3) % 7; 
        break; 
       case 4: // Apr 
       case 6: // Jun 
       case 9: // Sep 
       case 11: // Nov 
        lastDayOfPreviousMonth = (lastDayOfPreviousMonth + 2) % 7; 
        break; 
       case 2: // Feb 
        if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) 
         lastDayOfPreviousMonth = (lastDayOfPreviousMonth + 1) % 7; 
       } 
      } 
     } 


     System.out.println(countOfSundayOnFirstOfMonth); 
    } 

} 
関連する問題