Constraint Program: Build employee weekly schedules using ILOG - constraint-programming

I'm new to constraint programming. Now I try to build weekly schedules to manage employee work time and to meet the labor demand. I am using ILOG CPLEX and try to fix the problem. I think this is an easy problem but I can't wrap my head around it. Here's the problem:
• We have multiple workers (i.e.47 workers), each with some constraints (let's say day min work hours, day max work hours, week min work hours, week max work hours)
• Each worker has at least one skill to do a task, with certain level
• We have some tasks (i.e. 10 tasks) every day, each day is split to 48 slots(0~47). a half hour one slot.
• A worker can only work on one task at the same slot (a half hour)
• A task at one slot has a labor demand (may be 2, means: need two workers to do this task at this slot), a min demand(may be 1), and a max demand(may be 10).
How do we assign the tasks to the workers to meet the demand and minimize the number of workers used and assign workers with higher level to do a task?
I'm just learning CP & ILOG CPLEX for two weeks. Any ideas on how to a constraint (i.e. day min work hours)?
I write some code as below:
CP cp = new CP();
//objective
INumExpr skillMatrixSum = cp.IntExpr();
//day min work hours and so on
IList<SMAIlogStaffInfo> listStaffInfo = SMAScheduleByILogStaffInfoBusiness.GetStaffInfoList(unitId, startDate, endDate, connectionString);
//skill level
IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo = SMAScheduleByILogSkillMatrixInfoBusiness.GetPersonSkillMatrixInfoList(unitId, startDate, endDate, connectionString);
//demand for every half hour
IList<SMAIlogDemandInfo> listDemandInfo = SMAScheduleByILogDemandInfoBusiness.GetDemandInfoList(unitId, startDate, endDate, connectionString);
//staff unavailability (i.e. ask for a leave)
IList<SMAIlogUnvailInfo> listUnvailInfo = SMAScheduleByILogUnvailInfoBusiness.GetUnvailInfoList(unitId, startDate, endDate, connectionString);
int nbWorkers = listStaffInfo.Select(t => t.PersonId).Distinct().Count();
IList<string> listPerson = listStaffInfo.Select(t => t.PersonId).ToList();
//possible tasks by skill
List<IIntervalVar> allTasks = new List<IIntervalVar>();
List<IIntervalVar>[] workerTasks = new List<IIntervalVar>[nbWorkers];
for (int w = 0; w < nbWorkers; w++)
{
workerTasks[w] = new List<IIntervalVar>();
}
//schedule by day from sunday to saturday
for (int weekdayIndex = 0; weekdayIndex < 7; weekdayIndex++)
{
IList<SMAIlogDemandInfo> listWeekdayDemandInfo = listDemandInfo.Where(t => t.Weekday == weekdayIndex).ToList();
IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo = listUnvailInfo.Where(t => t.Weekday == weekdayIndex).ToList();
SetScheduleByDay(cp, skillMatrixSum, allTasks, workerTasks, listWeekdayDemandInfo, listWeekdayUnvailInfo, listStaffInfo, listPersonSkillMatrixInfo);
}
//constraint: one worker can not do two tasks at the same time
for (int w = 0; w < nbWorkers; w++)
{
IIntervalSequenceVar seq = cp.IntervalSequenceVar(workerTasks[w].ToArray(), listPerson[w]);
cp.Add(cp.NoOverlap(seq));
}
//Minimize skill Matrix
cp.Add(cp.Minimize(skillMatrixSum));
public static void SetScheduleByDay(CP cp, INumExpr skillMatrixSum, List<IIntervalVar> allTasks, List<IIntervalVar>[] workerTasks,
IList<SMAIlogDemandInfo> listWeekdayDemandInfo, IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo, IList<SMAIlogStaffInfo> listStaffInfo, IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo)
{
int weekdayIndex = listWeekdayDemandInfo.Select(t => t.Weekday).FirstOrDefault();
int skillCount = listWeekdayDemandInfo.Select(t => t.SkillId).Distinct().Count(); //task count
int nbTasks = listWeekdayDemandInfo.Count(); //weekday demand
int nbWorkers = listStaffInfo.Count();//workers count
//decision variables
IIntervalVar[] tasks = new IIntervalVar[nbTasks];
IIntervalVar[,] taskMatrix = new IIntervalVar[nbTasks, nbWorkers];
ICumulFunctionExpr[] arrayResourcesWorkHours = new ICumulFunctionExpr[nbWorkers];
for (int j = 0; j < nbWorkers; j++)
{
arrayResourcesWorkHours[j] = cp.CumulFunctionExpr();
}
string taskFullName;
string skillId = "";
string personId = "";
int levelId = 0;
int demand = 0;
int slot = 0;
for (int i = 0; i < nbTasks; i++)
{
slot = listWeekdayDemandInfo[i].Slot;
demand = listWeekdayDemandInfo[i].Demand;
if (demand == 0)
{
continue;
}
skillId = listWeekdayDemandInfo[i].SkillId;
List<IIntervalVar> alttasks = new List<IIntervalVar>();
for (int w = 0; w < listStaffInfo.Count(); w++)
{
personId = listStaffInfo[w].PersonId;
levelId = listPersonSkillMatrixInfo.Where(t => t.SkillId == skillId && t.PersonId == personId).ToList().ToList().FirstOrDefault().LevelId;
//this worker can do this task
if (levelId > 0)
{
taskFullName = personId + "-" + weekdayIndex + "-" + slot + "-" + skillId;
IIntervalVar wtask = cp.IntervalVar(1, taskFullName);
wtask.SetOptional(); // SetOptional
alttasks.Add(wtask);
taskMatrix[i, w] = wtask;
workerTasks[w].Add(wtask);
allTasks.Add(wtask);
listStaffInfo[w].listWorkerTasks.Add(wtask);
skillMatrixSum = cp.Sum(skillMatrixSum, cp.Prod(levelId, cp.PresenceOf(wtask)));
}
}
cp.Add(cp.Alternative(tasks[i], alttasks.ToArray()));
}
//here is my problem. the way to generalize cumulative is wrong (DayMin and DayMax,also unavailability ).because tasks is Set Optional
for (int j = 0; j < listStaffInfo.Count; j++)
{
IList<IIntervalVar> listWorkerTasks = listStaffInfo[j].listWorkerTasks;
if (listWorkerTasks.Count == 0)
{
continue;
}
int dayMin = listStaffInfo[j].DayMin;
int dayMax = listStaffInfo[j].DayMax;
IConstraint dayMinConstraint = cp.Ge(arrayResourcesWorkHours[j], dayMin);
cp.Add(dayMinConstraint);
IConstraint dayMaxConstraint = cp.Le(arrayResourcesWorkHours[j], dayMax);
cp.Add(dayMaxConstraint);
INumToNumStepFunction availablityNumStep = cp.NumToNumStepFunction();
IList<SMAIlogUnvailInfo> listWeekdayPersonUnvailInfo = listWeekdayUnvailInfo.Where(t => t.PersonId == personId).ToList();
if (listWeekdayPersonUnvailInfo.Count > 0)
{
for (int k = 0; k < listWeekdayPersonUnvailInfo.Count; k++)
{
int startSlot = listWeekdayPersonUnvailInfo[k].StartSlot;
int endSlot = listWeekdayPersonUnvailInfo[k].EndSlot;
for (int i = 0; i < listWorkerTasks.Count; i++)
{
while (startSlot <= endSlot)
{
availablityNumStep.SetValue(startSlot, startSlot + 1, 0);
//cp.Add(cp.ForbidExtent(listWorkerTasks[i], availablityNumStep));
cp.Add(cp.ForbidStart(listWorkerTasks[i], availablityNumStep));
cp.Add(cp.ForbidEnd(listWorkerTasks[i], availablityNumStep));
startSlot++;
}
}
}
}
}
}
I have a set of input data as below:
Schedule Input Data
and also I have a example of out data as below:
Schedule Output Data

Related

Find maximum deviation of all substrings

Given a string, find the maximum deviation among all substrings. The maximum deviation is defined as the difference between the maximum frequency of a character and the minimum frequency of a character.
For example, in abcaba, a has a frequency of 3; b has a frequency of 2; c has a frequency of 1. so a has the maximum frequency, which is 3, whereas c has a minimum frequency of 1. Therefore the deviation of this string is 3 - 1 = 2. And we also need to find all other deviations for each of the substrings for abacaba, the maximum among them is the answer.
I couldn't think of a better way rather than the obvious brute force approach. Thanks in advance!
For finding all substrings you have to consider O(n2). See this post for more details. You can just optimize it by stop point where substring lengths be smaller than current maximum deviation.
maxDeviation = 0;
n = strlen(str);
for i = 0 to n
{
if(n-i < maxDeviation) break; //this is new stop point to improve
sub1 = substring(str,i,n);
sub2 = substring(str,0,n-i); // you can use if(i!=0) to avoid duplication of first sentence
a = findMaxDeviation(sub1); // This is O(n)
b = findMaxDeviation(sub2); // This is O(n)
maxDeviation = max(a,b);
}
print maxDeviation
Pay attention to this line if(n-i < maxDeviation) break; because you cannot find a deviation more than maxDeviation in a string with length of smaller than maxDeviation.
public static int getDev(Map<String, Integer> devEntries){
List<Integer> entries = devEntries.entrySet().stream()
.map(x->x.getValue())
.collect(Collectors.toList());
Comparator<Integer> collect = Comparator.naturalOrder();
Collections.sort(entries,collect.reversed());
return entries.get(0) - entries.get( entries.size()-1);
}
public static int getMaxFreqDeviation(String s, Set<Integer> deviations ) {
for (int x=0;x<s.length();x++) {
for (int g=x;g<s.length()+1;g++){
String su =s.substring(x,g);
Map<String, Integer> map = Arrays.asList(su.split(""))
.stream()
.collect(Collectors.groupingBy(v->v,Collectors.summingInt(v->1)));
if (map.entrySet().size()==1){
deviations.add(abs(0));
}else {
int devcount = getDev(map);
deviations.add(abs(devcount));
}
}
}
return deviations.stream().collect(Collectors.toList()).get(deviations.size()-1);
}
public static void main(String[] args){
String se = "abcaba";
Set<Integer> deviations = new TreeSet<>();
int ans = getMaxFreqDeviation(se,deviations);
System.out.println(ans);
}
}
I faced a similar question in a test and I used c#, although I failed during the challenge but picked it up to solve the next day. I came about something like the below.
var holdDict = new Dictionary<char, int>();
var sArray = s.ToCharArray();
var currentCharCount = 1;
//Add the first element
holdDict.Add(sArray[0],1);
for (int i = 1; i < s.Length-1; i++)
{
if (sArray[i] == sArray[i - 1])
{
currentCharCount += 1;
}
else
{
currentCharCount = 1;
}
holdDict.TryGetValue(sArray[i], out var keyValue);
if (keyValue < currentCharCount) holdDict[sArray[i]] = currentCharCount;
}
var myQueue = new PriorityQueue<string, int>();
foreach (var rec in holdDict)
{
myQueue.Enqueue($"{rec.Key}#{rec.Value}", rec.Value);
}
int highest = 0, lowest = 0, queueCount=myQueue.Count;
while (myQueue.Count > 0)
{
int currentValue = int.Parse(myQueue.Peek().Split('#')[1]);
if (myQueue.Count == queueCount) lowest = currentValue;
highest = currentValue;
myQueue.Dequeue();
}
return highest - lowest;
O(n) algo (26*26*N)
import string
def maxSubarray(s, ch1, ch2):
"""Find the largest sum of any contiguous subarray."""
"""From https://en.wikipedia.org/wiki/Maximum_subarray_problem"""
best_sum = 0
current_sum = 0
for x in s:
if x == ch1:
x = 1
elif x == ch2:
x = -1
else:
x = 0
current_sum = max(0, current_sum + x)
best_sum = max(best_sum, current_sum)
return best_sum
def findMaxDiv(s):
'''Algo from https://discuss.codechef.com/t/help-coding-strings/99427/4'''
maxDiv = 0
for ch1 in string.ascii_lowercase:
for ch2 in string.ascii_lowercase:
if ch1 == ch2:
continue
curDiv = maxSubarray(s, ch1, ch2)
if curDiv > maxDiv:
maxDiv = curDiv
return maxDiv

Profit Maximization based on dynamix programming

I have been trying to solve this problem :
" You have to travel to different villages to make some profit.
In each village, you gain some profit. But the catch is, from a particular village i, you can only move to a village j if and only if and the profit gain from village j is a multiple of the profit gain from village i.
You have to tell the maximum profit you can gain while traveling."
Here is the link to the full problem:
https://www.hackerearth.com/practice/algorithms/dynamic-programming/introduction-to-dynamic-programming-1/practice-problems/algorithm/avatar-and-his-quest-d939b13f/description/
I have been trying to solve this problem for quite a few hours. I know this is a variant of the longest increasing subsequence but the first thought that came to my mind was to solve it through recursion and then memoize it. Here is a part of the code to my approach. Please help me identify the mistake.
static int[] dp;
static int index;
static int solve(int[] p) {
int n = p.length;
int max = 0;
for(int i = 0;i<n; i++)
{
dp = new int[i+1];
Arrays.fill(dp,-1);
index = i;
max = Math.max(max,profit(p,i));
}
return max;
}
static int profit(int[] p, int n)
{
if(dp[n] == -1)
{
if(n == 0)
{
if(p[index] % p[n] == 0)
dp[n] = p[n];
else
dp[n] = 0;
}
else
{
int v1 = profit(p,n-1);
int v2 = 0;
if(p[index] % p[n] == 0)
v2 = p[n] + profit(p,n-1);
dp[n] = Math.max(v1,v2);
}
}
return dp[n];
}
I have used extra array to get the solution, my code is written in Java.
public static int getmaxprofit(int[] p, int n){
// p is the array that contains all the village profits
// n is the number of villages
// used one extra array msis, that would be just a copy of p initially
int i,j,max=0;
int msis[] = new int[n];
for(i=0;i<n;i++){
msis[i]=p[i];
}
// while iteraring through p, I will check in backward and find all the villages that can be added based on criteria such previous element must be smaller and current element is multiple of previous.
for(i=1;i<n;i++){
for(j=0;j<i;j++){
if(p[i]>p[j] && p[i]%p[j]==0 && msis[i] < msis[j]+p[i]){
msis[i] = msis[j]+p[i];
}
}
}
for(i=0;i<n;i++){
if(max < msis[i]){
max = msis[i];
}
}
return max;
}

Modifying a matrix in groovy with multiple threads

I have a multi-threading problem in which I should multiply 2 random matrices. The problem is that after I finish the execution the matrix is empty although if I print the element that is inserted into the matrix it is displayed right. The matrices to be multiplied are not empty.
import java.util.Random
p1 = 500
p2 = 500
threads = 4
def giveTasks(int workers, int tasks) {
int[] taskArray = new int[workers + 1]
taskArray[0] = 0
for (i = 1; i <= workers; i++) {
taskArray[i] = taskArray[i - 1] + tasks / workers + Math.max(tasks % workers - i + 1, 0)
}
return taskArray
}
class Matrix {
public int[][] table
public Matrix(int p1, int p2) {
table = new int[p1][p2]
}
public Matrix(int[][] matrix) {
table = matrix
}
}
def createMatrix(int lines, int columns) {
int[][] matrix = new int[lines][columns]
Random rn = new Random()
for (i = 0; i < lines; i++)
for (j = 0; j < columns; j++)
matrix[i][j] = rn.nextInt(100)
return matrix
}
Matrix matrix1 = new Matrix(createMatrix(p1, p2))
Matrix matrix2 = new Matrix(createMatrix(p2, p1))
Matrix matrix = new Matrix(p1, p2)
int[] taskArray = giveTasks(threads, p1)
def thread
int tn = 0
for (int i = 1; i < threads + 1; i++) {
start = taskArray[i - 1]
stop = taskArray[i]
thread = Thread.start {
for (int job = start; job < stop; job++) { //line for matrix1
int sum = 0
for (int j = 0; j < p1; j++) {
for (int k = 0; k < p1; k++)
sum += matrix1.table[job][k] + matrix2.table[k][j]
matrix.table[job][j] = sum
}
}
tn += 1
println "Thread " + tn + "finished"
}
}
thread.join()
print matrix.table
There is one major thing wrongly understood by you in the code you have shown us - you overwrite thread variable inside for-loop and after you spawn all 4 threads you wait only for the last one to finish execution.
Instead you should store a list of all spawned threads and you would have to join them all in the end of the script. Something like:
def queue = []
int tn = 0
for (int i = 1; i < threads + 1; i++) {
start = taskArray[i - 1]
stop = taskArray[i]
def thread = Thread.start {
for (int job = start; job < stop; job++) { //line for matrix1
int sum = 0
for (int j = 0; j < p1; j++) {
for (int k = 0; k < p1; k++)
sum += matrix1.table[job][k] + matrix2.table[k][j]
matrix.table[job][j] = sum
}
}
tn += 1
println "Thread " + tn + "finished"
}
queue << thread
}
queue*.join()
matrix.table.each { println it }
You can see that in the end of the script it does:
queue*.join()
It uses Groovy's spread operator to call join() method on all elements collected in the list. And we add every spawned thread to the queue list using left shift operator:
queue << thread
This is an equivalent of queue.add(thread).
I have run your program with p1=16 and p2=16 with those changes applied and I got an output like:
Thread 3finished
Thread 4finished
Thread 1finished
Thread 2finished
[1470, 2794, 4343, 5924, 7388, 9015, 10533, 12064, 13713, 15672, 17354, 18916, 20524, 22086, 23370, 24982]
[1464, 2782, 4325, 5900, 7358, 8979, 10491, 12016, 13659, 15612, 17288, 18844, 20446, 22002, 23280, 24886]
[1629, 3112, 4820, 6560, 8183, 9969, 11646, 13336, 15144, 17262, 19103, 20824, 22591, 24312, 25755, 27526]
[1466, 2786, 4331, 5908, 7368, 8991, 10505, 12032, 13677, 15632, 17310, 18868, 20472, 22030, 23310, 24918]
[1487, 2828, 4394, 5992, 7473, 9117, 10652, 12200, 13866, 15842, 17541, 19120, 20745, 22324, 23625, 25254]
[1570, 2994, 4643, 6324, 7888, 9615, 11233, 12864, 14613, 16672, 18454, 20116, 21824, 23486, 24870, 26582]
[1345, 2544, 3968, 5424, 6763, 8265, 9658, 11064, 12588, 14422, 15979, 17416, 18899, 20336, 21495, 22982]
[1622, 3098, 4799, 6532, 8148, 9927, 11597, 13280, 15081, 17192, 19026, 20740, 22500, 24214, 25650, 27414]
[1557, 2968, 4604, 6272, 7823, 9537, 11142, 12760, 14496, 16542, 18311, 19960, 21655, 23304, 24675, 26374]
[1477, 2808, 4364, 5952, 7423, 9057, 10582, 12120, 13776, 15742, 17431, 19000, 20615, 22184, 23475, 25094]
[1447, 2748, 4274, 5832, 7273, 8877, 10372, 11880, 13506, 15442, 17101, 18640, 20225, 21764, 23025, 24614]
[1473, 2800, 4352, 5936, 7403, 9033, 10554, 12088, 13740, 15702, 17387, 18952, 20563, 22128, 23415, 25030]
[1727, 3308, 5114, 6952, 8673, 10557, 12332, 14120, 16026, 18242, 20181, 22000, 23865, 25684, 27225, 29094]
[1483, 2820, 4382, 5976, 7453, 9093, 10624, 12168, 13830, 15802, 17497, 19072, 20693, 22268, 23565, 25190]
[1575, 3004, 4658, 6344, 7913, 9645, 11268, 12904, 14658, 16722, 18509, 20176, 21889, 23556, 24945, 26662]
[1474, 2802, 4355, 5940, 7408, 9039, 10561, 12096, 13749, 15712, 17398, 18964, 20576, 22142, 23430, 25046]
Hope it helps.

Algorithm for finding continuous repeated sequences

I'm looking for an algorithm that finds short tandem repeats in a genome sequence.
Basically, given a really long string which can only consist of the 4 characters 'ATCG', I need to find short repeats between 2-5 characters long that are next to each other.
ex:
TACATGAGATCATGATGATGATGATGGAGCTGTGAGATC
would give ATGATGATG or ATG repeated 3 times
The algorithm needs to scale up to a string of 1 million characters so I'm trying to get as close to linear runtime as possible.
My current algorithm:
Since the repeats can be 2-5 characters long, I check the string character by character and see if the Nth character is the same as the N+Xth character, with X being 2 through 5. With a counter for each X that counts sequential matches and resets at a mismatch, we know if there is a repeat when X = the counter. The subsequent repeats can then be checked manually.
You are looking at each character which gives you O(n), since you compare on each character the next (maximum) five characters this gives you a constant c:
var data = get_input();
var compare = { `A`, `T`, `G`, `A`, `T` } // or whatever
var MAX_LOOKAHEAD = compare.length
var n
var c
for(n = data_array.length; n < size; i++) { // Has runtime O(n)
for(c = 0; c < MAX_LOOKAHEAD; c++) { // Maximum O(c)
if( compare[c] != data[i+c] ) {
break;
} else {
report( "found match at position " + i )
}
}
}
It is easy to see that this runs O(n*c) times. Since c is very small it can be ignored - and I think one can not get rid of that constant - which results in a total runtime of O(n).
The good news:
You can speed this up with parallelization. E.g. you could split this up in k intervals and let multiple threads do the job for you by giving them appropriate start and end indices. This could give you a linear speedup.
If you do that make sure that you treat the intersections as special cases since you could miss a match if your intervals split a match in two.
E.g. n = 50000:
Partition for 4 threads: (n/10000) - 1 = 4. The 5th thread won't have a lot to do since it just handles the intersections which is why we don't need to consider its (in our case tiny) overhead.
1 10000 20000 40000 50000
|-------------------|-------------------|-------------------|-------------------|
| <- thread 1 -> | <- thread 2 -> | <- thread 3 -> | <- thread 4 -> |
|---| |---| |---|
|___________________|___________________|
|
thread 5
And this is how it could look like:
var data;
var compare = { `A`, `T`, `G`, `A`, `T` };
var MAX_LOOKAHEAD = compare.length;
thread_function(args[]) {
var from = args[0];
var to = args[1];
for(n = from ; n < to ; i++) {
for(c = 0; c < MAX_LOOKAHEAD; c++) {
if( compare[c] != data[i+c] ) {
break;
} else {
report( "found match at position " + i )
}
}
}
}
main() {
var data_size = 50000;
var thread_count = 4;
var interval_size = data_size / ( thread_count + 1) ;
var tid[]
// This loop starts the threads for us:
for( var i = 0; i < thread_count; i++ ) {
var args = { interval_size * i, (interval_size * i) + interval_size };
tid.add( create_thread( thread_function, args ) );
}
// And this handles the intersections:
for( var i = 1; i < thread_count - 1; i++ ) {
var args = { interval_size * i, (interval_size * i) + interval_size };
from = (interval_size * i) - compare.length + 1;
to = (interval_size * i) + compare.length - 1;
for(j = from; j < to ; j++) {
for(k = 0; k < MAX_LOOKAHEAD; k++) {
if( compare[k] != data[j+k] ) {
break;
} else {
report( "found match at position " + j )
}
}
}
}
wait_for_multiple_threads( tid );
}

Find the index of a specific combination without generating all ncr combinations

I am trying to find the index of a specific combination without generating the actual list of all possible combinations. For ex: 2 number combinations from 1 to 5 produces, 1,2;1,3,1,4,1,5;2,3,2,4,2,5..so..on. Each combination has its own index starting with zero,if my guess is right. I want to find that index without generating the all possible combination for a given combination. I am writing in C# but my code generates all possible combinations on fly. This would be expensive if n and r are like 80 and 9 and i even can't enumerate the actual range. Is there any possible way to find the index without producing the actual combination for that particular index
public int GetIndex(T[] combination)
{
int index = (from i in Enumerable.Range(0, 9)
where AreEquivalentArray(GetCombination(i), combination)
select i).SingleOrDefault();
return index;
}
I found the answer to my own question in simple terms. It is very simple but seems to be effective in my situation.The choose method is brought from other site though which generates the combinations count for n items chosen r:
public long GetIndex(T[] combinations)
{
long sum = Choose(items.Count(),atATime);
for (int i = 0; i < combinations.Count(); i++)
{
sum = sum - Choose(items.ToList().IndexOf(items.Max())+1 - (items.ToList().IndexOf(combinations[i])+1), atATime - i);
}
return sum-1;
}
private long Choose(int n, int k)
{
long result = 0;
int delta;
int max;
if (n < 0 || k < 0)
{
throw new ArgumentOutOfRangeException("Invalid negative parameter in Choose()");
}
if (n < k)
{
result = 0;
}
else if (n == k)
{
result = 1;
}
else
{
if (k < n - k)
{
delta = n - k;
max = k;
}
else
{
delta = k;
max = n - k;
}
result = delta + 1;
for (int i = 2; i <= max; i++)
{
checked
{
result = (result * (delta + i)) / i;
}
}
}
return result;
}

Resources