我是编程和Java的新手,我正在尝试通过Project Euler网站进行自学.我试图完成这个问题:http://projecteuler.net/problem=19,这是:
在二十世纪的第一个月(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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是它打印的内容,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
Run Code Online (Sandbox Code Playgroud)
如何设置循环以使Day在月末不重置?或者是否有另一种解决此问题的方法,不涉及如此多的嵌套循环?
谢谢!
我认为你需要扔掉现有的代码并重新开始。由于您正在尝试通过解决欧拉项目问题来学习如何编码,因此我不会因为向您提供代码而破坏您的乐趣。看来您确实想要完整的工作代码,所以我修复了您的代码,包括由于您可能误解或忽视的问题陈述中的一些微妙细节而出现的一些错误。
只是为了好玩,让我们看一下您想要修复的代码的直接问题......
当您最初声明 时Day,将其初始化为 1。然后替换此行:
Day=1;
Run Code Online (Sandbox Code Playgroud)
有了这个:
if (Day > 7) {
Day = 1;
}
Run Code Online (Sandbox Code Playgroud)
并将其移动到该月各天的循环内。
但仍然存在一个严重的问题。您每年都会不断覆盖您的二月数组。您应该只初始化它一次,并将其长度设置为 29。但这也有一个不幸的副作用,即破坏任何依赖于 的循环calendar[month].length,因此您也必须考虑到这一点。
您真正需要跟踪的是每月第一天的星期日数量,因此您只需要存储和增加一个变量即可。这解决了上述覆盖 Feb 数组的问题,因为您将不再使用它(或任何其他月份的数组)。另一方面,如果您确实只想练习使用数组,则可以使用 3 维数组(其中附加维度是年份)。但我大胆猜测,大多数 Java 程序员大多数时候都使用列表而不是数组,而且当他们确实使用数组时,他们几乎不会使用多维数组。
还有一些注意事项
你的外while循环是多余的。
对于所有能被 100 整除的闰年,您的LeapYearTest方法将错误地返回 true(所有能被 100 整除的年份也能被 4 整除,因此您永远不会输入测试能被 100 整除的年份的 if 块)。
最后,您将循环一月的每一天(而不是循环每个月的第一天)。
另请注意,问题指出,
1900 年1 月 1 日是星期一。
但您应该找到从 1 Jan 1901开始的星期日。
修复这些错误和其他错误(例如循环中的条件)后,我在下面包含了代码的完整工作版本。请注意,您可以通过更多地使用模数 (%) 运算符并且不计算该月其他日子的星期日数量(因为您最终还是将它们丢弃),可以轻松地优化它以在一小部分时间内运行)。
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
同样,这还可以得到很大的改进。例如,您可以简单地循环年份和月份,并使用 Java 内置的 Calendar API 或第三方 API 来检查该月的第一天是否是星期日,但这也许是解决问题的最酷的方法就是自己实现末日算法。这将允许您轻松计算任何给定日期的星期几,而无需使用 java.util.Calendar。
一旦实现了世界末日算法,您不必每次都循环所有月份,因此您可以进行更多优化。例如,isSunday(MAR,1,year)== (! isLeapYear(year)) && isSunday(FEB,1,year)。