I am not sure whether my solution is justifiable (ans. 171) - Project Euler Q.19 since I am having a hard time getting my head around modular arithmetic and not really sure whether my approach to it was correct or not... I was having trouble on trying to get the equivalence of having 0 as a key rather than 1 to Monday for reference in a hash table. The question was;
1 Jan 1900 was a Monday.
Thirty days has September, April, June and November. All the rest have thirty-one, Saving February alone, which has twenty-eight, rain
or shine. And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth
century (1 Jan 1901 to 31 Dec 2000)?
So what I did was start the sum of days at 1 (reference for days in hash table) and subtract 1 after finding the sum of Sunday's, since doing it by 0 caused problems when the total sum of days were divisible by 3 and 6 (3:Wednesday, 6:Sunday). How could I have done this by using 0 as reference for Monday?
import java.util.*;
public class p19 {
public static void main(String[] args) {
Hashtable<Integer, String> days = new Hashtable<Integer, String>();
days.put(1, "Monday");
days.put(2, "Tuesday");
days.put(3, "Wednesday");
days.put(4, "Thursday");
days.put(5, "Friday");
days.put(6, "Saturday");
days.put(7, "Sunday");
Hashtable<Integer, String> months = new Hashtable<Integer, String>();
months.put(1, "January");
months.put(2, "February");
months.put(3, "March");
months.put(4, "April");
months.put(5, "May");
months.put(6, "June");
months.put(7, "July");
months.put(8, "August");
months.put(9, "September");
months.put(10, "October");
months.put(11, "November");
months.put(12, "December");
int min, max;
min = 1900;
max = 2000;
String[][] arr = new String[12 * (max - min + 1)][];
// Total days starts at 1 to make modular arithmetic easier when accounting for days
// (i.e., 1 Monday, 2 Tuesday, etc.) and since the first day, hence, 0th day on 1 Jan 1900 is Monday.
for (int year = min, index = 0, totalDays = 1; year <= max; year++) {
for (int month = 1; month <= 12; month++, index++) {
arr[index] = new String[numberOfDays(month,year)];
int sum = 1;
System.out.println(months.get(new Integer(month)) + " " + year);
for (int day = 1; day <= numberOfDays(month, year); day++, totalDays++) {
if (totalDays % 7 == 0) {
arr[index][day - 1] = days.get(new Integer((totalDays % 7 + 7) % 365));
}
else {
arr[index][day - 1] = days.get(new Integer((totalDays % 7) % 365));
}
if (sum > 7) {
System.out.println();
sum = 1;
}
System.out.print(totalDays + ":= " + arr[index][day - 1] + ", " + day + " | ");
sum++;
}
System.out.println("\n");
}
}
int count = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i][0] == "Sunday") {
count++;
}
}
// Subtract 1 from count since the total days were overstated by 1 before inititallizing array
System.out.println("Number of Sundays that fell on the first of the month from: 1/Jan/1901 - 31/Dec/2000: " + (count - 1));
}
public static int numberOfDays (int month, int year) {
int days = 0;
switch (month) {
case 7:
case 4:
case 6:
case 11:
days = 30;
break;
case 2:
if (isLeapYear(year)) {
days = 29;
}
else {
days = 28;
}
break;
default: days = 31;
break;
}
return days;
}
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
}
Your daysInMonth check is incorrect - the result must have been correct by incidence:
switch (month) {
case 4:
case 6:
case 9:
case 11:
days = 30;
break;
The rest of the program can be simplified - note that the start year has to be corrected too, dow stands for DayOfWeek:
public static void main (String[] args) {
int count = 0;
// dow = 2, since 1.1.1901 was a Thuesday (2)
for (int year = 1901, dow = 2; year <= 2000; ++year)
{
for (int month = 1; month <= 12; ++month)
{
if (dow == 0) {
// System.out.println ("Date: " + year + "-" + month);
++count;
}
dow = (dow + numberOfDays (month, year)) % 7;
}
}
System.out.println ("Number of Sundays that fell on the first of the month from: 1/Jan/1901 - 31/Dec/2000: " + count);
}
Related
I have used these two websites to measure how close my code gets.
Difference
Days
since year
zero
I use days since year zero to normalise the two entered dates, then I find the difference between those dates.
import java.util.Scanner;
public class DateDiff {
private static final int[] monthsDay = {31,28,31,30,31,30,31,31,30,31,30,31};
public static String dateChecker() {
boolean b = true;
int dateC = 0;
String date = "";
do {
Scanner scanner = new Scanner(System.in);
date = scanner.nextLine();
try {
if (date.charAt(2) == '/' && date.charAt(5) == '/') {
date = date.replace("/", "");
dateC = Integer.parseInt(date);
b = false;
} else {
System.out.println("Reenter date in the dd/mm/yyyy format");
}
} catch (Exception e) {
System.out.println("Reenter date in the dd/mm/yyyy format");
}
} while (b);
return date;
}
public static int daysForMonth(int months, int year) {
int days = 0;
for (int i = 0; i < months; i++)
if (i == 1)
days += ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)
? monthsDay[i] + 1
: monthsDay[i];
else
days += monthsDay[i];
return days;
}
public static int daysForYears(int year) {
int days = 0;
for (int i = 0; i < year; i++)
if ((i % 4 == 0 && (i % 100 != 0)) || (i % 400 == 0))
days += 366;
else
days += 365;
return days;
}
public static int daysSinceYearZero(String date) {
int day = Integer.parseInt(date.substring(0,2));
int month = Integer.parseInt(date.substring(2,4));
int year = Integer.parseInt(date.substring(4,8));
int daysMonth = daysForMonth(month-1, year);
int daysYear = daysForYears(year);
return day + daysMonth + daysYear;
}
public static void main(String[] args) {
System.out.println("Enter first date");
String date1 = dateChecker();
System.out.println("Enter second date");
String date2 = dateChecker();
int firstDate = daysSinceYearZero(date1);
int secondDate = daysSinceYearZero(date2);
System.out.println("First Date days since Year Zero: " + firstDate);
System.out.println("Second Date days since Year Zero: " + secondDate);
System.out.println("Difference: " + Math.abs(firstDate-secondDate));
}
}
My code gets close, but always seems to miss by a few days and I can't figure out why. I have confirmed the days and daysMonth are correct, but do not understand where I am going wrong in calculating the number of days since year zero using years (the daysYear variable)
Edit: No libraries are allowed to be used. Scanner is fine however as that is just for user input.
Since you didn't explain any requirements or limitations you can do it like this.
LocalDate earliest = LocalDate.parse("2012-05-17");
LocalDate latest = LocalDate.parse("2022-06-22");
System.out.println(latest.toEpochDay()-earliest.toEpochDay());
prints
3688 (exclusive of the latest date day)
However, here is one way to home grow it. I used lambdas to facilitate the process. And no loops were required in the calculation. So this runs in constant time.
First I created an IntTrinaryOperator interface.
interface IntTrinaryOperator {
public int applyAsInt(int a, int b, int c);
}
Then an array of month days was created (leap years are handled later) the first cell is ignored but required for the following operation.
int daysPerMonth[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int monthSums[] = daysPerMonth.clone();
// this creates a running sum
// looks like [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
// the last cell is not used.
Arrays.parallelPrefix(monthSums, (a, b) -> a + b);
A leap year function
Function<Integer, Boolean> isLeapYear =
a -> a % 400 == 0 || (a % 100 != 0 && a % 4 == 0);
And the defined Trinary to be used for the actual calculation.
(y-1)*365-(y-1)/100+(y-1)/4 +(y-1)/400 - computes total leap years starting from previous year.
first total days using 365 days per year
then subtract century years
then add years divisible by 400 back in.
monthSums[m-1]+d - adds days for this year
((m > 2) && isLeapYear.apply(y) ? 1 : 0) - 1 - adds one more day if after February but subtracts 1 to exclude current day (as in most ranges in Java)
IntTrinaryOperator daysToEpoch = (y, m, d) -> (y - 1) * 365
- (y - 1) / 100 + (y - 1) / 4 + (y - 1) / 400 +
+ monthSums[m - 1] + d
+ ((m > 2) && isLeapYear.apply(y) ? 1 : 0) - 1;
Testing
generate some dates. Dates are not chronological so the days could be negative, hence the Math.abs()
Random r = new Random();
for (int i = 0; i < 10; i++) {
int eYear = r.nextInt(2022) + 1;
int eMonth = r.nextInt(12) + 1;
int eDay = r.nextInt(daysPerMonth[eMonth])
+ (eMonth == 2 && isLeapYear.apply(eYear) ? 1 :
0);
int sYear = r.nextInt(2022) + 1;
int sMonth = r.nextInt(12) + 1;
int sDay = r.nextInt(daysPerMonth[sMonth])
+ (sMonth == 2 && isLeapYear.apply(sYear) ? 1 :
0);
int eDaysToEpoch =
daysToEpoch.applyAsInt(eYear, eMonth, eDay);
int sDaysToEpoch =
daysToEpoch.applyAsInt(sYear, sMonth, sDay);
System.out.printf("%02d/%02d/%04d - %02d/%02d/%04d - %,9d total days%n",
eMonth, eDay, eYear, sMonth, sDay, sYear, Math.abs(eDaysToEpoch-sDaysToEpoch));
}
And the original dates
System.out.println(daysToEpoch.applyAsInt(2022, 6, 22)-
daysToEpoch.applyAsInt(2012, 5, 17));
prints something like.
04/10/1377 - 12/03/1486 - 40,048 total days
02/12/0727 - 03/27/0196 - 193,899 total days
11/26/0457 - 12/09/0307 - 54,775 total days
02/25/0691 - 10/23/1596 - 330,785 total days
03/28/0404 - 01/16/1567 - 424,705 total days
10/18/0372 - 01/15/1316 - 344,512 total days
08/01/1374 - 01/23/1484 - 39,986 total days
03/21/0622 - 07/24/0495 - 46,260 total days
02/05/1167 - 08/05/1558 - 142,991 total days
12/02/1824 - 07/21/0976 - 309,859 total days
3688
This has been tested using the API method first shown above. With over 1M random tests there were no discrepancies.
Here is a date validation method. It checks for leap years and days against months. It also allows single digits for month and day. It does not produce detailed error messages. I continues to re-prompt until a valid date is entered. Otherwise the day, month, and year are returned in an array.
public static int[] getDate(Scanner scanner) {
String stringDate = "\\d\\d?/\\d\\d?/\\d{4}";
while (true) {
System.out.println(
"Please enter date in dd/mm/yyyy format.");
String date = scanner.nextLine();
if (date.matches(stringDate)) {
int[] dmy = Arrays.stream(date.split("/"))
.mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(dmy));
int d = dmy[0];
int m = dmy[1];
int y = dmy[2];
if (d > 0 && m > 0 && m < 13 && y > 0) {
boolean isLeap = isLeapYear.apply(y);
if (isLeap && d <= 29 && m == 2) {
return dmy;
}
if (d <= daysPerMonth[m]) {
return dmy;
}
}
}
System.out.print("Illegal date: ");
}
}
There are date libraries to do this. I am not very sure about the necessity of writing this code. In any case if you want to completely code the solution without using any libraries then here is the code to do that.
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class DateDiff {
private static final Map<Integer, Integer> monthsMap = new HashMap<>();
static {
monthsMap.put(0, 0);
monthsMap.put(1, 31);
monthsMap.put(2, 28);
monthsMap.put(3, 31);
monthsMap.put(4, 30);
monthsMap.put(5, 31);
monthsMap.put(6, 30);
monthsMap.put(7, 31);
monthsMap.put(8, 31);
monthsMap.put(9, 30);
monthsMap.put(10, 31);
monthsMap.put(11, 30);
monthsMap.put(12, 31);
}
public static String dateChecker() {
boolean incorrectDateFormat =false;
String date;
do {
Scanner scanner = new Scanner(System.in);
date = scanner.nextLine();
if (date.charAt(2) != '/' || date.charAt(5) != '/') {
System.out.println("Re-enter date in the dd/mm/yyyy format");
incorrectDateFormat =true;
} else {
incorrectDateFormat = false;
}
} while(incorrectDateFormat);
return date;
}
public static int daysSinceYearZero(String dateStr) {
int year = Integer.parseInt(dateStr.substring(dateStr.lastIndexOf("/")+1));
int totalNumberOfCompleteYearsSinceYear0 = year; //As year zero is also a complete year, so completed number of years will be equal given year in the date.
int leapYearCount = 0;
for(int i=1; i<=totalNumberOfCompleteYearsSinceYear0; i++) { //year zero is not a leap year. so starting the loop from 4 as year 4 is the first leap year.
if((i % 4 == 0 && (i % 100 != 0)) || (i % 400 == 0)) {
leapYearCount++;
}
}
int totalNumberOfDaysInCompletedYears = totalNumberOfCompleteYearsSinceYear0*365 + leapYearCount;
int monthFromGivenDate = Integer.parseInt(dateStr.substring(dateStr.indexOf("/")+1,dateStr.lastIndexOf("/")));
int completedMonth = monthFromGivenDate - 1;
int daysForCompletedMonthsOfCurrentYear = 0;
for(int i=0; i<=completedMonth;i++) {
daysForCompletedMonthsOfCurrentYear = daysForCompletedMonthsOfCurrentYear + monthsMap.get(i);
if(i==2 && year%4==0) {
daysForCompletedMonthsOfCurrentYear++;
}
}
int numberOfDaysCompletedInCurrentMonth = Integer.parseInt(dateStr.substring(0,dateStr.indexOf("/")));
int totalNumberOfDaysTillGivenDate = totalNumberOfDaysInCompletedYears + daysForCompletedMonthsOfCurrentYear + numberOfDaysCompletedInCurrentMonth;
return totalNumberOfDaysTillGivenDate;
}
public static void main(String[] args) {
System.out.println("Enter first date");
String date1 = dateChecker();
System.out.println("Enter second date");
String date2 = dateChecker();
int firstDate = daysSinceYearZero(date1);
int secondDate = daysSinceYearZero(date2);
System.out.println("First Date days since Year Zero: " + firstDate);
System.out.println("Second Date days since Year Zero: " + secondDate);
System.out.println("Difference: " + Math.abs(firstDate-secondDate));
}
}
Hello and thanks in advance for the assistance. I have a program that is supposed to print the current month calendar based on the user inputs of month and year. The program mostly work but i am having issues with formatting and the first day of the month is not starting under the proper date.
Example output:
October 2020
------------------------------
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
October 2020 will begin on a Thursday, but 1 is printed under Mon. October 4th is a Sunday, so that is where the new line should begin.
Please see attached my code. Thanks again
import java.util.*;
public class CalendarMonthDisplay {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); //Scan for user input
System.out.print("Please enter a month between 1 and 12 (e.g. 5): "); //Prompt user to enter month
int m = input.nextInt();
System.out.print("Please enter a full year (e.g. 2018): "); //Prompt user to enter year
int y = input.nextInt();
//Print calendar for the month of the year
if ( m < 1 || m > 12)
System.out.print("Wrong input! Please try again.");
else
printMonthCalendar(m, y);
}
static void printMonthCalendar (int m, int y) { //Display calendar in format above
int startDay = getStartDay(m, y);
int numDaysInMonths = getNumDaysInMonth(m, y);
printMonthHeader(m, y);
printMonthBody(startDay, numDaysInMonths);
}
static void printMonthBody (int startDay, int numDaysInMonths) { //Display the days in the calendar
int i;
for (i = 0; i <= startDay; i++)
System.out.print(" ");
for (i = 1; i <= numDaysInMonths; i++) {
if ( i < 10 )
System.out.print(" " + i );
else
System.out.print(" " + i + " ");
if ((startDay + i) % 7 == 0)
System.out.println();
}
System.out.println();
}
static void printMonthHeader (int m, int y) { //Display the header information
System.out.println("\t" + getMonthName(m) + " " + y);
System.out.println("------------------------------");
System.out.println(" Sun Mon Tue Wed Thu Fri Sat");
}
static String getMonthName (int m) {
String monthName = null;
switch (m) {
case 1: monthName = "January";
break;
case 2: monthName = "February";
break;
case 3: monthName = "March";
break;
case 4: monthName = "April";
break;
case 5: monthName = "May";
break;
case 6: monthName = "June";
break;
case 7: monthName = "July";
break;
case 8: monthName = "August";
break;
case 9: monthName = "September";
break;
case 10: monthName = "October";
break;
case 11: monthName = "November";
break;
case 12: monthName = "December";
}
return monthName;
}
static int getNumDaysInMonth (int m, int y) {
int numDaysInMonths= 0;
switch (m) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
numDaysInMonths= 31;
break;
case 4: case 6: case 9: case 11:
numDaysInMonths = 30;
break;
case 2:
if (isLeapYear(y))
numDaysInMonths = 29;
else
numDaysInMonths = 28;
break;
}
return numDaysInMonths;
}
static boolean isLeapYear (int y) {
return (y % 400 == 0) || (y % 4 == 0 && y % 100 != 0);
// return true;
// return false;
}
static int getStartDay (int m, int y) {
// Adjust month number & year to fit Zeller's numbering system
if (m < 3)
m = m + 12;
y = y - 1;
int d = 1; //Set day parameter to 1
int k = y % 100; // Calculate year within century
int j = y / 100; // Calculate century term
int h = 0; // Day number of first day in month 'm'
h = ( d + ( 13 * ( m + 1 ) / 5 ) + k + ( k / 4 ) + ( j / 4 ) + ( 5 * j ) ) % 7;
// Convert Zeller's value to ISO value (1 = Mon, ... , 7 = Sun )
int dayNum = ( ( h + 5 ) % 7 ) + 1;
return dayNum;
}
}
I recommend you do it using the modern date-time API. Learn more about the modern date-time API from Trail: Date Time.
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); // Scan for user input
System.out.print("Please enter a month between 1 and 12 (e.g. 5): "); // Prompt user to enter month
int m = input.nextInt();
System.out.print("Please enter a full year (e.g. 2018): "); // Prompt user to enter year
int y = input.nextInt();
printMonth(y, m);
}
static void printMonth(int year, int month) {
YearMonth ym = YearMonth.of(year, month);
System.out.println("Sun Mon Tue Wed Thu Fri Sat");
int counter = 1;
// Get day of week of 1st date of the month and print space for as many days as
// distant from SUN
int dayValue = LocalDate.of(year, month, 1).getDayOfWeek().getValue();
for (int i = 0; i < dayValue; i++, counter++) {
System.out.printf("%-4s", "");
}
for (int i = 1; i <= ym.getMonth().length(ym.isLeapYear()); i++, counter++) {
System.out.printf("%-4d", i);
// Break the line if the value of the counter is multiple of 7
if (counter % 7 == 0) {
System.out.println();
}
}
}
}
A sample run:
Please enter a month between 1 and 12 (e.g. 5): 9
Please enter a full year (e.g. 2018): 2020
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Note: Learn about formatted printing at Formatter.
I don’t think your getStartDay method returns the day of week number that you expect. It seems to be returning 1 for Wednesday through 7 for Tuesday. Examples:
Month Start day of week getStartDay()
------------------------------------------------
July 2020 Wednesday 1
August 2020 Saturday 4
September 2020 Tuesday 7
October 2020 Thursday 2
There is probably one or two more problems in your code, but I think you should fix this fundamental one first.
It’s a fine exercise, you’re surely learning. For production code one would never implement Zeller’s algorithm or any other algorithm for finding lengths of months and days of week. This is done much more reliably by the standard library as shown in the answer by Arvind Kumar Avinash.
I have code that takes user input as the year, the month and then the date. I have three methods, one gets the day of the week, one gets the amount of days in that month, and the other calculates whether or not that year is a leap year.
When a user enters a year a month and a date, for example "2016 3 3" I want my code to then list the months from 1-12 and next to each number the amount of days in that month. The code I have for all three methods is as follows.
class Date {
int year, month, day;
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
/**
* This method returns the day of the week as an integer: 0 and 6: Sunday
* and Saturday 1 - 5: Weekdays
*/
public int getDayOfWeek() {
int y0 = year - (14 - month) / 12;
int x = y0 + y0 / 4 - y0 / 100 + y0 / 400;
int m0 = month + 12 * ((14 - month) / 12) - 2;
int d0 = (day + x + (31 * m0) / 12) % 7;
return d0;
}
/**
* This method returns the number of days in a given month an integer
*/
public int getDaysInMonth(int month) {
int daysInMonth = (int) (28 + (Math.floor(month / 8.0) + month) % 2 + 2 % month + 2 * Math.floor(1.0 / month));
if (month == 2 && isLeapYear()) {
daysInMonth += 1;
}
return daysInMonth;
}
public boolean isLeapYear() {
boolean isLeapYear = true;
if (year % 4 != 0) {
isLeapYear = false;
}
else {
if (year % 100 != 0) {
isLeapYear = true;
}
else if (year % 400 != 0) {
isLeapYear = false;
}
else {
isLeapYear = true;
}
}
return isLeapYear;
}
I'm in my first year of computer science and am still very fresh to this, i've been staring at this code and googling for the better part of a day and can't seem to figure anything out, any help would be appreciated.
I know this is wrong but this is all I've been able to come up with so far
public void printDaysInMonth() {
int m = getDaysInMonth(month);
System.out.println("Month " + " Days");
for (int i=0; i<12; i++) {
System.out.println(m);
}
}
You're on the right track, but you're assigning your m variable outside of the for loop, and thus printing the same month every time. Instead, try assigning it and printing it inside the loop you already have:
public void printDaysInMonth() {
for (int i = 1; i <= 12; i++) {
int m = getDaysInMonth(i);
System.out.println("Month " + i + " has " + m + "days.");
}
}
Since your getDaysInMonth method already accounts for leap years, this should be enough!
In Java 8, you can use below code for easy Date manipulation:
public static void main(String[] args) {
LocalDate date1=getDate(12,12,2020);
int dayOfWeek=getDayOfWeek(date1);
boolean isLeapYear=isLeapYear(date1);
System.out.println("date: "+date1);
System.out.println("dayOfWeek: "+dayOfWeek);
System.out.println("is Leap year: "+isLeapYear);
}
// Will return LocalDate value
private static LocalDate getDate(int date,int month, int year){
return LocalDate.of(year,month,date);
}
// Will return Dayofweek
private static int getDayOfWeek(LocalDate date){
return date.getDayOfWeek().getValue();
}
// Will return true/false if year is/is not leap year
private static boolean isLeapYear(LocalDate date){
return date.isLeapYear();
}
/* If you want to print days of all months of a particular year, use below method*/
public void printLengthsOdDaysInMonthsOfYearPassed(int year){
year=2020;
for(int i=1;i<=12;i++){
LocalDate date2=LocalDate.of(year,i,1);
int length=date2.getMonth().maxLength();
System.out.println("Month: "+i+ " length: "+length);
System.out.println("is leap year: "+date2.isLeapYear());
}
}
Hello I made a software that generates HTML calendar. I followed Method 1 algorithm from wiki http://www.wikihow.com/Calculate-the-Day-of-the-Week to get exact day of the week in current day for a year. But problem is that there is a bug when it goes from year 2099 to 2100 then in year 2100 it is about one day behind.
My question is... is that algorithm correct? Should it be a day back behind in year 2100? I have no time to wait 85 years to figure this out :(
Also I tried Easter Sunday algorithm and it also generates Sunday on the day it should be Sunday by Day of the week algorithm so I am not sure who has the right, if me or the two correctly generating algorithms.
Here are next years what are buggy in my opinion:
Testing years 0 to 2200
Incorrect day from year to year: 99/100
Incorrect day from year to year: 199/200
Incorrect day from year to year: 299/300
Incorrect day from year to year: 499/500
Incorrect day from year to year: 599/600
Incorrect day from year to year: 699/700
Incorrect day from year to year: 899/900
Incorrect day from year to year: 999/1000
Incorrect day from year to year: 1099/1100
Incorrect day from year to year: 1299/1300
Incorrect day from year to year: 1399/1400
Incorrect day from year to year: 1499/1500
Incorrect day from year to year: 1699/1700
Incorrect day from year to year: 1799/1800
Incorrect day from year to year: 1899/1900
Incorrect day from year to year: 2099/2100
*EDIT
Here is algorithm to get Easter Sunday:
int c = year/100;
int n = year - 19*(int)(year/19);
int k = (c - 17)/25;
int i = c - (int)(c/4) - (int)((c - k)/3) + 19*n + 15;
i -= 30*(int)(i/30);
i -= (int)(i/28)*(1 - (int)(i/28)*(int)(29/(i + 1))*(int)((21 - n)/11));
int j = year + (int)(year/4) + i + 2 - c + (int)(c/4);
j -= 7*(int)(j/7);
int l = i - j;
int m = 3 + (int)((l + 40)/44); //Your month when is Easter Sunday
int d = l + 28 - 31*(int)(m/4); //Your day when is Easter Sunday
Here is algorithm to get day of the week in a Year Month Day
int [] CENTURY_TABLE = {0, 5, 3, 1};
int [] MONTH_TABLE = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
boolean leap = false;
int t1 = (day + MONTH_TABLE[month - 1]) % 7;
int m1 = year % 100;
int t2 = (m1 % 7) + (m1/4) + CENTURY_TABLE[((int)(year/100)) % CENTURY_TABLE.length] - (leap && (month == 1 || month == 2) ? 1 : 0);
if(t2 == -1){
t2 = 6;
}
int d = (t1 + t2) % 7; //0 - Saturday, 1 - Sunday, 2 - Monday... 6 - Friday
*FIX
Change "leap" summary in Day of Week algorithm to
leap = year % 4 == 0 && (year % 100 == 0 ? year % 400 == 0 : true) //This fixed my problem <3
I think your program is affected by leap years: https://en.wikipedia.org/wiki/Leap_year
You have a problem on all years dividable through 100 but not 400.
Edit after select as solution:
The code works without problems. I slightly changed for a test and got no error:
#Test
public void dayTest()
{
for (int y = 2099; y <=2100; y++)
{
int day = easterSundayDay(y);
int month = easterSundayMonth(y);
assertTrue("Wrong year: " + day + "."+ month + "."+ y + " is day: " + dayOfWeek(day, month, y), 1 ==dayOfWeek(day, month, y));
}
}
public int easterSundayDay(int year)
{
int c = year/100;
int n = year - 19*(int)(year/19);
int k = (c - 17)/25;
int i = c - (int)(c/4) - (int)((c - k)/3) + 19*n + 15;
i -= 30*(int)(i/30);
i -= (int)(i/28)*(1 - (int)(i/28)*(int)(29/(i + 1))*(int)((21 - n)/11));
int j = year + (int)(year/4) + i + 2 - c + (int)(c/4);
j -= 7*(int)(j/7);
int l = i - j;
int m = 3 + (int)((l + 40)/44); //Your month when is Easter Sunday
int d = l + 28 - 31*(int)(m/4);
return d;
}
public int easterSundayMonth(int year)
{
int c = year/100;
int n = year - 19*(int)(year/19);
int k = (c - 17)/25;
int i = c - (int)(c/4) - (int)((c - k)/3) + 19*n + 15;
i -= 30*(int)(i/30);
i -= (int)(i/28)*(1 - (int)(i/28)*(int)(29/(i + 1))*(int)((21 - n)/11));
int j = year + (int)(year/4) + i + 2 - c + (int)(c/4);
j -= 7*(int)(j/7);
int l = i - j;
int m = 3 + (int)((l + 40)/44); //Your month when is Easter Sunday
int d = l + 28 - 31*(int)(m/4);
return m;
}
public int dayOfWeek(int day, int month, int year)
{
int [] CENTURY_TABLE = {0, 5, 3, 1};
int [] MONTH_TABLE = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
boolean leap = false;
int t1 = (day + MONTH_TABLE[month - 1]) % 7;
int m1 = year % 100;
int t2 = (m1 % 7) + (m1/4) + CENTURY_TABLE[((int)(year/100)) % CENTURY_TABLE.length] - (leap && (month == 1 || month == 2) ? 1 : 0);
if(t2 == -1){
t2 = 6;
}
int d = (t1 + t2) % 7;
return d;
}
I'm doing Project Euler #19 and I'm wondering how my code works.
Project Euler #19
You are given the following information, but you may prefer to do some
research for yourself.
1 Jan 1900 was a Monday.
Thirty days has September,
April, June and
November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a
century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec
2000)?
My code:
static int weekday = 1;
static int sundays = 0;
static int years = 1901;
static int[] monthArray = {31, 29, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static void solve() {
for (int yr = years; yr <= 2000; yr++) {
for (int j = 0; j < 12; j++) {
if (j != 1) {
months(monthArray[j]);
} else {
if(yr % 4 == 0 && yr % 400 != 0) {
months(monthArray[1]);
} else {
months(monthArray[2]);
}
}
}
}
System.out.println(sundays);
}
public static void months(int monthLength) {
for (int i = 0; i <= monthLength; i++) {
if (weekday == 7 && i == 1) {
sundays++;
}
if (weekday == 7) {
weekday = 0;
}
weekday++;
}
}
I thought I would have to put "j++" into the leap year so the code would jump over the next month in the array. Another thing is that I wondered why I can't start the 'months' method's for loop and weekday from 1. Is this even a bit of code that counts sundays reliably from other timespans? I get the right answer for the requested timespan, but I'm not sure if it actually works. Thanks.
EDIT: I changed the code so that the for loop with 'j' iterates through the months pretty linearly with the exception that is not in the array. Still not 100% sure if it's correct, but it seems a lot more logically sound.
public static void solve() {
for (int yr = years; yr <= 2000; yr++) {
for (int j = 0; j < 12; j++) {
if (yr % 4 == 0 && yr % 400 != 0 && j == 1) {
months(29);
} else {
months(monthArray[j]);
}
}
}
System.out.println(sundays);
}
public static void months(int monthLength) {
for (int i = 0; i < monthLength; i++) {
if (weekday == 7 && i == 1) {
sundays++;
}
if (weekday == 7) {
weekday = 1;
} else {
weekday++;
}
}
}
I don't wanna give too much because this site involve personal research and being stuck is a part of the game (give a try to another challenge, come back ... you will see things more clearly)
One way to do it is like that:
counter is 0
day_counter start on a monday
for every years between 1900 and 2000:
for each months in that year:
add month's number of day modulo 7 to day_counter
if 7 divide day_counter:
increment counter
at the end, counter is your solution.
My code in JS:
var sunday=0;
for (var year = 1901; year <= 2000; year++) {
for (var month = 1; month <= 12; month++) {
if ((new Date(year, month, 1)).getDay() == 0) {
sunday++;
}
}
}
window.alert(sunday);