Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
Suppose a Bank-Customer pays RD installments at every last day of the month.
So there must be 2 installments between the dates 12th October, 2013 and 10th December, 2013.
How can I find out how many installments the customer paid during this period of tine?
Should I use NodaTime library?
Ok. Here is my effort:
public sealed class DateDifference
{
int years;
public int Years
{
get { return years; }
}
int months;
public int Months
{
get { return months; }
}
int days;
public int Days
{
get { return days; }
}
public override string ToString()
{
return string.Format("[DateDifference Years={0}, Months={1}, Days={2}]", years, months, days);
}
public DateDifference(DateTime earlier, DateTime later)
{
if (later < earlier)
throw new ArgumentException("later is earlier than 'earlier'.");
bool isleapday = (earlier.Month == 2 && earlier.Day == 29);
DateTime tmp = isleapday ? new DateTime(earlier.Year, 2, 28) : earlier;
while (true)
{
try
{
tmp = tmp.AddYears(1);
if (isleapday && DateTime.IsLeapYear(tmp.Year))
tmp = new DateTime(tmp.Year, 2, 29);
}
catch (ArgumentOutOfRangeException)
{
break;
}
if (tmp <= later)
{
years++;
earlier = tmp;
}
else
{
break;
}
}
// Add months
tmp = earlier;
while (true)
{
try
{
tmp = tmp.AddMonths(1);
if (isleapday && tmp.Day != 29 && tmp.Month != 2)
tmp = new DateTime(tmp.Year, tmp.Month, 29);
}
catch (ArgumentOutOfRangeException)
{
break;
}
if (tmp <= later)
{
months++;
earlier = tmp;
}
else
{
break;
}
}
tmp = earlier;
while (true)
{
try
{
tmp = tmp.AddDays(1);
}
catch (ArgumentOutOfRangeException)
{
break;
}
if (tmp <= later)
{
days++;
earlier = tmp;
}
else
{
break;
}
}
}
DateDifference dateDifference = new DateDifference(startDateTextBox.Value, endDateTextBox.Value);
this.noOfInstallmentsTextBox.Text = ((int)++dateDifference.Months).ToString();
So I'll start off by saying I'm not prioritizing speed here. Datetime issues are tricky. Writing methods that you can be sure will work in all sorts of corner cases all over the world is hard. This approach is designed to work despite all edge cases and for that to be clear to the reader. It does not attempt to make cleaver optimizations because the tend to not work in odd edge cases, which are simply too common to ignore in the datetime world.
So first off we'll start with a simple helper method to get all of the days between two dates:
public static IEnumerable<DateTime> Days(DateTime start, DateTime end)
{
DateTime current = start;
while (current < end)
{
yield return current;
current = current.AddDays(1);
}
}
(If you want something more general purpose here you might get the next date after start, if you want all returned values to be "midnight". You might also swap start and end if they are in the reverse order, or throw an exception, etc. Also consider the exact semantics that you want; I have the bounds inclusive on start and exclusive on end, you may want it entirely inclusive, entirely exclusive, etc.)
We'll also create a method to determine if a date is the last day of the month.
public static bool IsLastDayOfMonth(DateTime date)
{
return date.AddDays(1).Month != date.Month;
}
We can actually define the last day of the month as being the only date for which its month is different from the following day's month.
Now when we combine these together we have an implementation that is very simple and clear to the reader:
public static int InstallmentCount(DateTime start, DateTime end)
{
return Days(start, end)
.Where(day => IsLastDayOfMonth(day))
.Count();
}
I hope it will help you.
int foo(DateTime start, DateTime end)
{
int count = end.Month - start.Month;
if (count < 0)
count += 12;
count += 12 * (end.Year - start.Year);
return count;
}
Related
I am having trouble understanding the relationship between variables and arguments passed to a method. The program below is supposed to take three integers from the main method (M, D, Y) and use various methods to validate if it is a valid date. This includes ensuring the year is between 1900 and 2100, as well as making sure the month is 1-12, and the day is within that month's range of days (including Feb 29th on leap years). If the date from the main method is not valid, the program should say so and print the default date of 1/1/1900. The code below always prints the default no matter what arguments are provided. I believe that this is because there is an issue with how I am using either the variables M, D, Y or the variables Month, Day, Year. This program is for an assignment in which I have to use all methods and constructors in the code below. I am unsure how to have the arguments M, D, Y get turned into the variables Month, Day, and Year, so they can be printed by the ShowDate method which was provided for me.
class Date
{
private int Month;
private int Day;
private int Year;
// Sets date to 1/1/1900
public Date()
{
Month = 1;
Day = 1;
Year = 1900;
}
public Date(int M, int D, int Y)
{
SetDate(M, D, Y);
}
public Boolean SetDate(int M, int D, int Y)
{
if (ValidateDate(M, D, Y))
{
Month = M;
Day = D;
Year = Y;
return true;
}
else
{
Console.WriteLine("Invalide date");
SetDefaultDate();
return false;
}
}
private void SetDefaultDate()
{
Month = 1;
Day = 1;
Year = 1900;
}
// Determines if date is valid.
public Boolean ValidateDate(int M, int D, int Y)
{
ValidateMonth();
ValidateDay();
ValidateYear();
if (ValidateMonth() && ValidateDay() && ValidateYear())
{
ShowDate();
return true;
}
else
{
return false;
}
}
// Determines if month is valid.
public Boolean ValidateMonth()
{
if (Month >= 1 && Month <= 12)
{
return true;
}
else
{
return false;
}
}
// Determines if year is valid.
public Boolean ValidateYear()
{
if(Year >= 1900 && Year <= 2100)
{
return true;
}
else
{
return false;
}
}
// Determines if day is valid
public Boolean ValidateDay()
{
IsLeapYear();
if(Month == 1 || Month == 3 || Month == 5 || Month == 7 || Month == 8 || Month == 10 || Month == 12)
{
if (Day >= 1 && Day <= 31)
{
return true;
}
else
{
return false;
}
}
else if (Month == 4 || Month == 6 || Month == 9 || Month == 11)
{
if (Day >= 1 && Day <= 30)
{
return true;
}
else
{
return false;
}
}
else if (Month == 2 && IsLeapYear())
{
if (Day >= 1 && Day <= 29)
{
return true;
}
else
{
return false;
}
}
else if (Month == 2 && !IsLeapYear())
{
if (Day >= 1 && Day <= 28)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
// Determine if year is a leap year
public Boolean IsLeapYear()
{
if ((Year % 4 == 0 && Year % 100 != 0) || (Year % 400 == 0))
{
return true;
}
else
{
return false;
}
}
// Print date to screen in format M/D/Y
public void DisplayDate()
{
Console.WriteLine(ShowDate());
}
public String ShowDate()
{
StringBuilder myStringBuilder = new StringBuilder();
myStringBuilder.AppendFormat("{0} / {1} / {2}", Month, Day, Year);
Console.WriteLine("{0}", myStringBuilder);
return (myStringBuilder.ToString());
}
static void Main(string[] args)
{
Date NewDate = new Date();
NewDate.SetDate(11,11,2011);
Console.ReadLine();
}
}
You never assign M, D or Y to your Month, Day and Year fields, so you are checking against your default values, which will all be zero by default. You could assign M, D and Y to their corresponding intended variables, but then you wouldn't be validating the input, just validating your fields. Instead you could have your methods accept parameters and check what you pass to it:
public Boolean ValidateMonth(int month)
{
if (month >= 1 && month <= 12)
{
return true;
}
else
{
return false;
}
}
And then when you call it
ValidateMonth(M);
And then you can do the same thing for the other two methods.
Also in your ValidateDate() method you have three useless calls to ValidateMonth(), ValidateDay() and ValidateYear(). You call each of these methods twice. (Once in the beginning, and then again in the if statement.) You can remove these:
public Boolean ValidateDate(int M, int D, int Y)
{
//Remove these:
//ValidateMonth();
//ValidateDay();
//ValidateYear();
if (ValidateMonth() && ValidateDay() && ValidateYear())
{
ShowDate();
return true;
}
else
{
return false;
}
}
Your constructor should initialize the class with the values given. Currently your default constructor initializes M, D, and Y but the constructor that takes arguments does not.
You can address this by changing the constructors to be more like this:
public Date() : this(1,1,1900)
{
}
public Date(int M, int D, int Y)
{
Month = M;
Day = D;
Year = Y;
}
Once the class is initialized, it's just a question of exposing a property or method to validate the values that are already held in the class. You shouldn't need to pass the month, day, and year into a method again, since they're already set. So the validate method might look like this:
public bool IsValid
{
get
{
return ValidateDay() && ValidateMonth() && ValidateYear();
}
}
And in your main program:
Date newDate = new Date(11,11,2011);
if (newDate.IsValid)
{
Console.WriteLine("Date is valid.");
}
else
{
Console.WriteLine("Date is not valid.");
}
Struggling to find the reason why the same code behave differently. The answer provided does indeed run, but when I type the same code from the solution manual, I got an ArgumentOutOfRangeException. I just cannot see where my problem lies.
Code from solution manual, class Date:
// Exercise 10.6 Solution: Date.cs
// Date class declaration.
using System;
public class Date
{
private int month; // 1-12
private int day; // 1-31 based on month
private int year; // >0
private static readonly int[] DAYSPERMONTH =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// constructor: use property Month to confirm proper value for month;
// use property Day to confirm proper value for day
public Date(int theDay, int theMonth, int theYear )
{
Month = theMonth; // validate month
Year = theYear; // validate year
Day = theDay; // validate day
Console.WriteLine( "Date object constructor for date {0}", this );
} // end Date constructor
// property that gets and sets the year
public int Year
{
get
{
return year;
} // end get
private set
{
year = CheckYear( value );
} // end set
} // end property year
// property that gets and sets the month
public int Month
{
get
{
return month;
} // end get
private set // make writing inaccessible outside the class
{
month = CheckMonth( value );
} // end set
} // end property Month
// property that gets and sets the day
public int Day
{
get
{
return day;
} // end get
private set // make writing inaccessible outside the class
{
day = CheckDay( value );
} // end set
} // end property Day
// increment the day and check if doing so will change the month
public void NextDay()
{
if ( !endOfMonth() )
++Day;
else
{
Day = 1;
NextMonth();
}
} // end method NextDay
// increment the month and check if doing so will change the year
public void NextMonth()
{
if ( Month < 12 )
++Month;
else
{
Month = 1;
++Year;
}
} // end method NextMonth
// return a string of the form month/day/year
public override string ToString()
{
return string.Format($"{Day:D2}/{Month:D2}/{Year}");
} // end method ToString
// utility method to confirm proper year value
private int CheckYear( int testYear )
{
if ( testYear > 0 ) // validate year
return testYear;
else // year is invalid
throw new ArgumentOutOfRangeException(
"year", testYear, "year must greater than 0" );
} // end method CheckYear
// utility method to confirm proper month value
private int CheckMonth( int testMonth )
{
if ( testMonth > 0 && testMonth <= 12 ) // validate month
return testMonth;
else // month is invalid
throw new ArgumentOutOfRangeException(
"month", testMonth, "month must be 1-12" );
} // end method CheckMonth
// utility method to confirm proper day value based on month and year
private int CheckDay( int testDay )
{
// Check if day in range for month
if ( testDay > 0 && testDay <= DAYSPERMONTH[ Month ] )
return testDay;
// Check for leap year
if ( testDay == 29 && leapYear() )
return testDay;
throw new ArgumentOutOfRangeException(
"day", testDay, "day out of range for current month/year" );
} // end method CheckDay
// check for end of month
private bool endOfMonth()
{
if ( leapYear() && Month == 2 && Day == 29 )
return true;
else
return Day == DAYSPERMONTH[ Month ];
} // end method endOfMonth
private bool leapYear()
{
return Month == 2 && ( Year % 400 == 0 ||
( Year % 4 == 0 && Year % 100 != 0 ) );
} // end method leapYear
} // end class Date
Code from solution manual, class DateTest:
// Exercise 10.6 Solution: DateTest
// Application tests Date class with year validation,
// NextDay and NextMonth methods.
using System;
public class DateTest
{
// method Main begins execution of C# application
public static void Main( string[] args )
{
Console.WriteLine( "Checking increment" );
Date testDate = new Date( 18, 9, 1980 );
// test incrementing of day, month and year
for ( int counter = 0; counter < 40; counter++ )
{
testDate.NextDay();
Console.WriteLine( "Incremented Date: {0}",
testDate.ToString() );
}
Console.Read();// end for
} // end Main
} // end class DateTest
My own copied code from Solution manual, class Date:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Ex_10._06
{
public class Date
{
private int day;
private int month;
private int year;
private static readonly int[] DAYSPERMONTH = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public Date(int theDay, int theMonth, int theYear)
{
Day = theDay;
Month = theMonth;
Year = theYear;
WriteLine($"Date object constructor for date {this}");
}
public int Year
{
get
{
return year;
}
private set
{
year = CheckYear(value);
}
}
public int Month
{
get
{
return month;
}
private set
{
month = CheckMonth(value);
}
}
public int Day
{
get
{
return day;
}
private set
{
day = CheckDay(value);
}
}
public void NextDay()
{
if (!endOfMonth())
++Day;
else
{
Day = 1;
NextMonth();
}
}
public void NextMonth()
{
if (Month < 12)
++Month;
else
{
Month = 1;
++Year;
}
}
public override string ToString()
{
return string.Format("{0}/{1}/{2}", Day, Month, Year);
}
private int CheckYear(int testYear)
{
if (testYear >= 0)
return testYear;
else
throw new ArgumentOutOfRangeException("Year", testYear, "Year must be equal or greater than 0");
}
private int CheckMonth(int testMonth)
{
if (testMonth > 0 && testMonth <= 12)
return testMonth;
else
throw new ArgumentOutOfRangeException("Month", testMonth, "Month must be 1-12");
}
private int CheckDay(int testDay)
{
if (testDay > 0 && testDay <= DAYSPERMONTH[Month])
return testDay;
if (testDay == 29 && leapYear())
return testDay;
throw new ArgumentOutOfRangeException("Day", testDay, "Day out of range for current month/year");
}
private bool endOfMonth()
{
if (leapYear() && Month == 2 && Day == 29)
return true;
else
return Day == DAYSPERMONTH[Month];
}
private bool leapYear()
{
return Month == 2 && (Year % 400 == 0 || (Year % 4 == 0 && Year % 100 != 0));
}
}
}
My own code copied from solution manual, class DateTest:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Ex_10._06
{
public class DateTest
{
public static void Main(string[] args)
{
WriteLine("Checking increment");
Date d1 = new Date(18, 9, 1979);
for (int i = 0; i < 100; i++)
{
d1.NextDay();
WriteLine($"Incremented Date: {d1.ToString()}");
}
Read();
}
}
}
True to be told, I am still learning. Maybe there is something I have overlooked, but I have even copied the style how to indent, still it produces an ArgumentOutOfRangeException, while the original code does run. Thanks in advance for your time.
See image call stack:
Call Stack
The error you are facing is because you switched the order in which properties are given their values in the constructor. When you declare a new date, you do CheckDay before anything else, which works out whether the day is valid in the given month, but it is done before the month is specified so that's your exception.
In the original code
Month = theMonth; // validate month
Year = theYear; // validate year
Day = theDay; // validate day
In the your code
Day = theDay;
Month = theMonth;
Year = theYear;
This causes an error because a month needs to be defined before the date, as it checks the array using the current month as an index.
Specifically, in CheckDay(), this code is run
if (testDay > 0 && testDay <= DAYSPERMONTH[Month])
return testDay;
Which, when Month = null evaluates to this
if (testDay > 0 && testDay <= DAYSPERMONTH[null])
I am trying this problem using dynamic programming
Problem:
Given a meeting room and a list of intervals (represent the meeting), for e.g.:
interval 1: 1.00-2.00
interval 2: 2.00-4.00
interval 3: 14.00-16.00
...
etc.
Question:
How to schedule the meeting to maximize the room utilization, and NO meeting should overlap with each other?
Attempted solution
Below is my initial attempt in C# (knowing it is a modified Knapsack problem with constraints). However I had difficulty in getting the result correctly.
bool ContainsOverlapped(List<Interval> list)
{
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++)
{
for (int j = i + 1; j < sortedList.Count; j++)
{
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public bool Optimize(List<Interval> intervals, int limit, List<Interval> itemSoFar){
if (intervals == null || intervals.Count == 0)
return true; //no more choice
if (Sum(itemSoFar) > limit) //over limit
return false;
var arrInterval = intervals.ToArray();
//try all choices
for (int i = 0; i < arrInterval.Length; i++){
List<Interval> remaining = new List<Interval>();
for (int j = i + 1; j < arrInterval.Length; j++) {
remaining.Add(arrInterval[j]);
}
var partialChoice = new List<Interval>();
partialChoice.AddRange(itemSoFar);
partialChoice.Add(arrInterval[i]);
//should not schedule overlap
if (ContainsOverlapped(partialChoice))
partialChoice.Remove(arrInterval[i]);
if (Optimize(remaining, limit, partialChoice))
return true;
else
partialChoice.Remove(arrInterval[i]); //undo
}
//try all solution
return false;
}
public class Interval
{
public bool IsOverlap(Interval other)
{
return (other.Start < this.Start && this.Start < other.End) || //other < this
(this.Start < other.Start && other.End < this.End) || // this covers other
(other.Start < this.Start && this.End < other.End) || // other covers this
(this.Start < other.Start && other.Start < this.End); //this < other
}
public override bool Equals(object obj){
var i = (Interval)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Interval(int start, int end){
Start = start;
End = end;
}
public int Duration{
get{
return End - Start;
}
}
}
Edit 1
Room utilization = amount of time the room is occupied. Sorry for confusion.
Edit 2
for simplicity: the duration of each interval is integer, and the start/end time start at whole hour (1,2,3..24)
I'm not sure how you are relating this to a knapsack problem. To me it seems more of a vertex cover problem.
First sort the intervals as per their start times and form a graph representation in the form of adjacency matrix or list.
The vertices shall be the interval numbers. There shall be an edge between two vertices if the corresponding intervals overlap with each other. Also, each vertex shall be associated with a value equal to the interval's duration.
The problem then becomes choosing the independent vertices in such a way that the total value is maximum.
This can be done through dynamic programming. The recurrence relation for each vertex shall be as follows:
V[i] = max{ V[j] | j < i and i->j is an edge,
V[k] + value[i] | k < i and there is no edge between i and k }
Base Case V[1] = value[1]
Note:
The vertices should be numbered in increasing order of their start times. Then if there are three vertices:
i < j < k, and if there is no edge between vertex i and vertex j, then there cannot be any edge between vertex i and vertex k.
Good approach is to create class that can easily handle for you.
First I create helper class for easily storing intervals
public class FromToDateTime
{
private DateTime _start;
public DateTime Start
{
get
{
return _start;
}
set
{
_start = value;
}
}
private DateTime _end;
public DateTime End
{
get
{
return _end;
}
set
{
_end = value;
}
}
public FromToDateTime(DateTime start, DateTime end)
{
Start = start;
End = end;
}
}
And then here is class Room, where all intervals are and which has method "addInterval", which returns true, if interval is ok and was added and false, if it does not.
btw : I got a checking condition for overlapping here : Algorithm to detect overlapping periods
public class Room
{
private List<FromToDateTime> _intervals;
public List<FromToDateTime> Intervals
{
get
{
return _intervals;
}
set
{
_intervals = value;
}
}
public Room()
{
Intervals = new List<FromToDateTime>();
}
public bool addInterval(FromToDateTime newInterval)
{
foreach (FromToDateTime interval in Intervals)
{
if (newInterval.Start < interval.End && interval.Start < newInterval.End)
{
return false;
}
}
Intervals.Add(newInterval);
return true;
}
}
While the more general problem (if you have multiple number of meeting rooms) is indeed NP-Hard, and is known as the interval scheduling problem.
Optimal solution for 1-d problem with one classroom:
For the 1-d problem, choosing the (still valid) earliest deadline first solves the problem optimally.
Proof: by induction, the base clause is the void clause - the algorithm optimally solves a problem with zero meetings.
The induction hypothesis is the algorithm solves the problem optimally for any number of k tasks.
The step: Given a problem with n meetings, hose the earliest deadline, and remove all invalid meetings after choosing it. Let the chosen earliest deadline task be T.
You will get a new problem of smaller size, and by invoking the algorithm on the reminder, you will get the optimal solution for them (induction hypothesis).
Now, note that given that optimal solution, you can add at most one of the discarded tasks, since you can either add T, or another discarded task - but all of them overlaps T - otherwise they wouldn't have been discarded), thus, you can add at most one from all discarded tasks, same as the suggested algorithm.
Conclusion: For 1 meeting room, this algorithm is optimal.
QED
high level pseudo code of the solution:
findOptimal(list<tasks>):
res = [] //empty list
sort(list) //according to deadline/meeting end
while (list.IsEmpty() == false):
res = res.append(list.first())
end = list.first().endTime()
//remove all overlaps with the chosen meeting
while (list.first().startTine() < end):
list.removeFirst()
return res
Clarification: This answer assumes "Room Utilization" means maximize number of meetings placed in the room.
Thanks all, here is my solution based on this Princeton note on dynamic programming.
Algorithm:
Sort all events by end time.
For each event, find p[n] - the latest event (by end time) which does not overlap with it.
Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
}
The complete source-code:
namespace CommonProblems.Algorithm.DynamicProgramming {
public class Scheduler {
#region init & test
public List<Event> _events { get; set; }
public List<Event> Init() {
if (_events == null) {
_events = new List<Event>();
_events.Add(new Event(8, 11));
_events.Add(new Event(6, 10));
_events.Add(new Event(5, 9));
_events.Add(new Event(3, 8));
_events.Add(new Event(4, 7));
_events.Add(new Event(0, 6));
_events.Add(new Event(3, 5));
_events.Add(new Event(1, 4));
}
return _events;
}
public void DemoOptimize() {
this.Init();
this.DynamicOptimize(this._events);
}
#endregion
#region Dynamic Programming
public void DynamicOptimize(List<Event> events) {
events.Add(new Event(0, 0));
events = events.SortByEndTime();
int[] eventIndexes = getCompatibleEvent(events);
int[] utilization = getBestUtilization(events, eventIndexes);
List<Event> schedule = getOptimizeSchedule(events, events.Count - 1, utilization, eventIndexes);
foreach (var e in schedule) {
Console.WriteLine("Event: [{0}- {1}]", e.Start, e.End);
}
}
/*
Algo to get optimization value:
1) Sort all events by end time, give each of the an index.
2) For each event, find p[n] - the latest event (by end time) which does not overlap with it.
3) Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
display opt();
}
*/
int[] getBestUtilization(List<Event> sortedEvents, int[] compatibleEvents) {
int[] optimal = new int[sortedEvents.Count];
int n = optimal.Length;
optimal[0] = 0;
for (int j = 1; j < n; j++) {
var thisEvent = sortedEvents[j];
//pick between 2 choices:
optimal[j] = Math.Max(thisEvent.Duration + optimal[compatibleEvents[j]], //Include this event
optimal[j - 1]); //Not include
}
return optimal;
}
/*
Show the optimized events:
sortedEvents: events sorted by end time.
index: event index to start with.
optimal: optimal[n] = the optimized schedule at n-th event.
compatibleEvents: compatibleEvents[n] = the latest event before n-th
*/
List<Event> getOptimizeSchedule(List<Event> sortedEvents, int index, int[] optimal, int[] compatibleEvents) {
List<Event> output = new List<Event>();
if (index == 0) {
//base case: no more event
return output;
}
//it's better to choose this event
else if (sortedEvents[index].Duration + optimal[compatibleEvents[index]] >= optimal[index]) {
output.Add(sortedEvents[index]);
//recursive go back
output.AddRange(getOptimizeSchedule(sortedEvents, compatibleEvents[index], optimal, compatibleEvents));
return output;
}
//it's better NOT choose this event
else {
output.AddRange(getOptimizeSchedule(sortedEvents, index - 1, optimal, compatibleEvents));
return output;
}
}
//compatibleEvents[n] = the latest event which do not overlap with n-th.
int[] getCompatibleEvent(List<Event> sortedEvents) {
int[] compatibleEvents = new int[sortedEvents.Count];
for (int i = 0; i < sortedEvents.Count; i++) {
for (int j = 0; j <= i; j++) {
if (!sortedEvents[j].IsOverlap(sortedEvents[i])) {
compatibleEvents[i] = j;
}
}
}
return compatibleEvents;
}
#endregion
}
public class Event {
public int EventId { get; set; }
public bool IsOverlap(Event other) {
return !(this.End <= other.Start ||
this.Start >= other.End);
}
public override bool Equals(object obj) {
var i = (Event)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Event(int start, int end) {
Start = start;
End = end;
}
public int Duration {
get {
return End - Start;
}
}
}
public static class ListExtension {
public static bool ContainsOverlapped(this List<Event> list) {
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++) {
for (int j = i + 1; j < sortedList.Count; j++) {
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public static List<Event> SortByEndTime(this List<Event> events) {
if (events == null) return new List<Event>();
return events.OrderBy(x => x.End).ToList();
}
}
}
This is the code I have at the moment:
String getDayRequested;
public void setDay(String getDayFromForm1)
{
getDayRequested = getDayFromForm1;
{
if (getDayRequested.Contains("today"))
{
getDayRequested = DateTime.Today.DayOfWeek.ToString();
}
else if (getDayRequested.Contains("tomorrow"))
{
getDayRequested = DateTime.Today.AddDays(1).DayOfWeek.ToString();
}
}
This checks my TextBox.Text string from Form1, and checks to see if the text "today" or "tomorrow" is in it.
Can anyone help me in the right direction of how to check the string for information asked about upcoming days; ie: "What will be the date this saturday", and add the appropriate number of days depending on what the day is when asked.
UPDATE
Using the code in the accepted answer, I used the following in my above else if statement to complete what I was after:
else if (getDayRequested.Contains("monday"))
{
getDayRequested = GetFutureDay(DateTime.Now, DayOfWeek.Monday).ToString("dd");
}
This handy little method will return a future day of the week.
public DateTime GetFutureDay(DateTime start, DayOfWeek day)
{
int daysToAdd = (day - start.DayOfWeek + 7) % 7;
return start.AddDays(daysToAdd);
}
It would be called like:
var day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), getDayFromForm1);
var getDayRequested = GetFutureDay(DateTime.Now, day);
Consider the following snippet of code...
DateTime date;
public void setDay(String day)
{
DayOfWeek futureDay = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), day);
int futureDayValue = (int)futureDay;
int currentDayValue = (int)DateTime.Now.DayOfWeek;
int dayDiff = futureDayValue - currentDayValue;
if (dayDiff > 0)
{
date = DateTime.Now.AddDays(dayDiff);
}
else
{
date = DateTime.Now.AddDays(dayDiff + 7);
}
}
Good Luck!
I have the following:
public class Interval
{
DateTime Start;
DateTime End;
}
I have a List<Interval> object containing multiple intervals. I am trying to achieve the following (I used numbers to make it easy to understand):
[(1, 5), (2, 4), (3, 6)] ---> [(1,6)]
[(1, 3), (2, 4), (5, 8)] ---> [(1, 4), (5,8)]
I currently do this in Python as follows:
def merge(times):
saved = list(times[0])
for st, en in sorted([sorted(t) for t in times]):
if st <= saved[1]:
saved[1] = max(saved[1], en)
else:
yield tuple(saved)
saved[0] = st
saved[1] = en
yield tuple(saved)
but am trying to achieve the same in C# (LINQ would be best but optional). Any suggestions on how to do this efficiently?
Here's a version using yield return - I find it easier to read than doing an Aggregate query, although it's still lazy evaluated. This assumes you've ordered the list already, if not, just add that step.
IEnumerable<Interval> MergeOverlappingIntervals(IEnumerable<Interval> intervals)
{
var accumulator = intervals.First();
intervals = intervals.Skip(1);
foreach(var interval in intervals)
{
if ( interval.Start <= accumulator.End )
{
accumulator = Combine(accumulator, interval);
}
else
{
yield return accumulator;
accumulator = interval;
}
}
yield return accumulator;
}
Interval Combine(Interval start, Interval end)
{
return new Interval
{
Start = start.Start,
End = Max(start.End, end.End),
};
}
private static DateTime Max(DateTime left, DateTime right)
{
return (left > right) ? left : right;
}
I was beset by "Not Created Here" syndrome tonight, so here's mine. Using an Enumerator directly saved me a couple lines of code, made it clearer (IMO), and handled the case with no records. I suppose it might run a smidge faster as well if you care about that...
public IEnumerable<Tuple<DateTime, DateTime>> Merge(IEnumerable<Tuple<DateTime, DateTime>> ranges)
{
DateTime extentStart, extentEnd;
using (var enumerator = ranges.OrderBy(r => r.Item1).GetEnumerator()) {
bool recordsRemain = enumerator.MoveNext();
while (recordsRemain)
{
extentStart = enumerator.Current.Item1;
extentEnd = enumerator.Current.Item2;
while ((recordsRemain = enumerator.MoveNext()) && enumerator.Current.Item1 < extentEnd)
{
if (enumerator.Current.Item2 > extentEnd)
{
extentEnd = enumerator.Current.Item2;
}
}
yield return Tuple.Create(extentStart, extentEnd);
}
}
}
In my own implementation, I use a TimeRange type to store each Tuple<DateTime, DateTime>, as other here do. I didn't include that here simply to stay focused / on-topic.
This may not be the prettiest solution, but it may work as well
public static List<Interval> Merge(List<Interval> intervals)
{
var mergedIntervals = new List<Interval>();
var orderedIntervals = intervals.OrderBy<Interval, DateTime>(x => x.Start).ToList<Interval>();
DateTime start = orderedIntervals.First().Start;
DateTime end = orderedIntervals.First().End;
Interval currentInterval;
for (int i = 1; i < orderedIntervals.Count; i++)
{
currentInterval = orderedIntervals[i];
if (currentInterval.Start < end)
{
end = currentInterval.End;
}
else
{
mergedIntervals.Add(new Interval()
{
Start = start,
End = end
});
start = currentInterval.Start;
end = currentInterval.End;
}
}
mergedIntervals.Add(new Interval()
{
Start = start,
End = end
});
return mergedIntervals;
}
Any feedback will be appreciated.
Regards
This kind of merging would typically be considered as a fold in functional languages. The LINQ equivalent is Aggregate.
IEnumerable<Interval<T>> Merge<T>(IEnumerable<Interval<T>> intervals)
where T : IComparable<T>
{
//error check parameters
var ret = new List<Interval<T>>(intervals);
int lastCount
do
{
lastCount = ret.Count;
ret = ret.Aggregate(new List<Interval<T>>(),
(agg, cur) =>
{
for (int i = 0; i < agg.Count; i++)
{
var a = agg[i];
if (a.Contains(cur.Start))
{
if (a.End.CompareTo(cur.End) <= 0)
{
agg[i] = new Interval<T>(a.Start, cur.End);
}
return agg;
}
else if (a.Contains(cur.End))
{
if (a.Start.CompareTo(cur.Start) >= 0)
{
agg[i] = new Interval<T>(cur.Start, a.End);
}
return agg;
}
}
agg.Add(cur);
return agg;
});
} while (ret.Count != lastCount);
return ret;
}
I made the Interval class generic (Interval<T> where T : IComparable<T>), added a bool Contains(T value) method, and made it immutable, but you should not need to change it much if you want to use the class definition as you have it now.
I used TimeRange as a container storing the ranges:
public class TimeRange
{
public TimeRange(DateTime s, DateTime e) { start = s; end = e; }
public DateTime start;
public DateTime end;
}
It divides the problem in combining two time ranges. Therefor, the current time range (work) is matched with the time ranges previously merged. If one of the previously added time ranges is outdated, it is dropped and the new time range (combined from work and the matching time range) is used.
The cases I figured out for two ranges () and [] are as follows:
[] ()
([])
[(])
[()]
([)]
()[]
public static IEnumerable<TimeRange> Merge(IEnumerable<TimeRange> timeRanges)
{
List<TimeRange> mergedData = new List<TimeRange>();
foreach (var work in timeRanges)
{
Debug.Assert(work.start <= work.end, "start date has to be smaller or equal to end date to be a valid TimeRange");
var tr = new TimeRange(work.start, work.end);
int idx = -1;
for (int i = 0; i < mergedData.Count; i++)
{
if (tr.start < mergedData[i].start)
{
if (tr.end < mergedData[i].start)
continue;
if (tr.end < mergedData[i].end)
tr.end = mergedData[i].end;
}
else if (tr.start < mergedData[i].end)
{
tr.start = mergedData[i].start;
if (tr.end < mergedData[i].end)
tr.end = mergedData[i].end;
}
else
continue;
idx = i;
mergedData.RemoveAt(i);
i--;
}
if (idx < 0)
idx = mergedData.Count;
mergedData.Insert(idx, tr);
}
return mergedData;
}