2012-04-29 65 views
5

我是編程和Java的新手,我試圖通過Project Euler網站來教自己。我試圖完成這個問題:http://projecteuler.net/problem=19,那就是:爪哇,從1901年到2000年這個月的第一個月的星期日計數

多少週日二十世紀 (1901年1月1日至31月2000)期間,在第一個月的下跌?

我想解決這個問題的方法是製作一個二維數組,代表一個calander,並通過計數到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 

如何我可以設置循環,以便Day在本月末不重置?還是有另一種方法來解決這個問題,不涉及這麼多的嵌套循環?

謝謝!

+3

所有這些陣列是矯枉過正。你只需要迭代和增加Sun 1st的總數。你用一個計數器0-6和另一個計數器並行迭代,計算當前月份的天數。 – 2012-04-29 07:50:20

+1

另外,除了每天循環之外,還有更多聰明的方法來計算本月第一個月的星期日。另外 - 如果沒有其他人會看到您的代碼,但您拼錯* Calendar *,並且Java編碼約定建議使用camelCase變量名稱(第一個字母爲小寫),那麼這不重要。 – rob 2012-04-29 08:14:11

+0

@Marko:對不起,我不明白,你能詳細說一下嗎? – Keith 2012-04-29 08:22:13

回答

3

我認爲你需要拋棄現有的代碼並重新開始。 由於您試圖通過解決Project Euler問題來學習如何編寫代碼,因此我不會通過給您代碼來破壞您的樂趣。看起來你確實需要完整的工作代碼,所以我已經修復了你的代碼,包括一些由於問題陳述中可能誤解或忽略的細節而出現的一些錯誤。

只是爲了好玩,讓我們看看你的代碼眼前的問題,你想固定...

當您最初聲明Day,初始化爲1。然後替換此行:

Day=1; 

與此:

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

和移動它是越過一個月的天循環內。

但仍然存在嚴重問題。你每年都會覆蓋你的Feb數組。你只應該初始化它一次,並將其長度設置爲29.但是這也有打破任何取決於calendar[month].length的循環的不良副作用,所以你也必須考慮到這一點。

你真正需要跟蹤的是每月的第一個星期日的數量,所以你只需要存儲和增加一個變量。這解決了覆蓋Feb數組的問題,因爲您不再使用它(或任何其他月份的數組)。另一方面,如果你真的只想練習使用數組,你可以使用一個三維數組(其中的額外維度是年)。但我想冒險猜測,大多數Java程序員大多數時間使用List而不是數組,而當他們使用數組時,他們幾乎不會使用具有多個維度的數組。

多幾個音符

你的外循環while是多餘的。

LeapYearTest方法不正確地爲所有閏年100整除返回true(由100整除所有年份也被4整除,所以你永遠不會進入,如果塊測試年由100整除)。

最後,你正在循環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的內置Calendar API或第三方API來檢查該月的第一天是否是星期天,但也許是解決問題的最酷方式是自己實施Doomsday Algorithm。這將允許您輕鬆計算任何給定日期的星期幾,而無需使用java.util.Calendar。

一旦你已經實現了末日算法,你不必每次都循環所有的月份,所以你可以做更多的優化。例如,isSunday(MAR,1,year) == (! isLeapYear(year)) && isSunday(FEB,1,year)

+0

謝謝,但我仍然想知道如何循環交錯的二維數組,通過計數到7,而不重置每列的計數? – Keith 2012-04-29 09:23:24

+0

你的代碼實際上有幾個問題。我已經更新了我的答案,指出了一些問題。 – rob 2012-04-29 11:01:28

+0

謝謝,我的理解。我改變了if語句爲:if(dayOfMonth 1900)如果我使用你的代碼,它不會將數組1加到數組中,如果dayoftheMonth = {calendar} [dayOfMonth] + = 1; } – Keith 2012-04-29 12:03:49

4

爲了讓從1901年到2001年增加一年的外循環以及檢查1月 - > 12月的內循環,然後查看該月的第一個月是否爲星期天,會快得多嗎?

總共100 * 12次迭代,10行代碼,頂部。

編輯:在此擴大。

您可以通過兩種方式解決問題 - 查看所有星期日,看他們是否在一個月的第一天,或者查看所有月份的第一天,看看它是否是星期天。

未經測試的代碼:

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 2012-04-29 08:17:53

+0

我會編輯我的答案以擴展。 – PaulJWilliams 2012-04-29 08:19:57

+0

@Keith:您可以使用Java內置的Calendar API來計算DayOfWeek ...假定這不被認爲是在Euler項目上作弊。 – rob 2012-04-29 08:19:59

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世紀是一個月的第一天的171個星期日。

+0

'171 * 7 = 1197'和1197不到4年,20世紀也只有4年? – 2013-07-18 13:23:43

+0

顯然你還沒有讀過問題定義。它是從1901年到2000年的一個月中的第一個171天。但我接受你的觀點,並且我編輯了我的帖子以更明確地表示...... – Nicocube 2013-07-18 14:20:15

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 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; 
} 

這裏就是我的意思,當我說你不需要數組:

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; 
} 

但是你馬上意識到,在內部循環遍歷幾個月的時候,沒有任何意義,當時它只是模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

我願意做它像這樣(僞):

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); 
} 

注意,一個不需要任何陣列,更不用說二維數組。 (爲了得到該月的長度,但是,MyDate中的附加方法,使用一個簡單的恆定陣列是確定。)

0

這裏是2級的解決方案:

1)使用Calendar - 它更簡單,但它不是那麼有效 - 135毫秒

​​

注意:

  • DAY_OF_MONTHDATE是等同的。
  • 我用Calendar.JANUARY因爲第一個月是0而不是1,即使第一天/日期是1

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); 
    } 

} 
相關問題