It was one of my interview question, and I could not think of the good way to get number N. (plus, I did not understand the American football scoring system as well)
6 points for the touchdown
1 point for the extra point (kicked)
2 points for a safety or a conversion (extra try after a touchdown)
3 points for a field goal
What would be an efficient algorithm to get all combinations of point-accumulations necessary to get a certain score N?
Assuming here you are looking for a way to get number of possibilities and not the actual possibilities.
First let's find a recursive function:
f(n) = (f(n-6) >= 0? f(n-6) : 0) + (f(n-1) >= 0 ? f(n-1) : 0) + (f(n-2) >= 0 ? f(n-2) : 0) + (f(n-3) >= 0 ? f(n-3) : 0)
base: f(0) = 1 and f(n) = -infinity [n<0]
The idea behind it is: You can always get to 0, by a no scoring game. If you can get to f(n-6), you can also get to f(n), and so on for each possibility.
Using the above formula one can easily create a recursive solution.
Note that you can even use dynamic programming with it, initialize a table with [-5,n], init f[0] = 0 and f[-1] = f[-2] = f[-3] = f[-4] = f[-5] = -infinity and iterate over indexes [1,n] to achieve the number of possibilities based on the the recursive formula above.
EDIT:
I just realized that a simplified version of the above formula could be:
f(n) = f(n-6) + f(n-1) + f(n-2) + f(n-3)
and base will be: f(0) = 1, f(n) = 0 [n<0]
The two formulas will yield exactly the same result.
This is identical to the coin change problem, apart from the specific numbers used. See this question for a variety of answers.
You could use dynamic programming loop from 1 to n, here is some pseudo code:
results[1] = 1
for i from 1 to n :
results[i+1] += results[i]
results[i+2] += results[i]
results[i+3] += results[i]
results[i+6] += results[i]
this way complexity is O(N), instead of exponential complexity if you compute recursively by subtracting from the final score... like computing a Fibonacci series.
I hope my explanation is understandable enough..
I know this question is old, but all of the solutions I see help calculate the number of scoring permutations rather than the number of scoring combinations. (So I think either something like this should be an answer or the question title should be changed.)
Some code such as the following (which could then be converted into a dp) will calculate the number of possible combinations of different scores:
int getScoreCombinationCount(int score, int scoreVals[], int scoreValIndex) {
if (scoreValIndex < 0)
return 0;
if (score == 0)
return 1;
if (score < 0)
return 0;
return getScoreCombinationCount(score - scoreVals[scoreValIndex], scoreVals, scoreValIndex) +
getScoreCombinationCount(score, scoreVals, scoreValIndex - 1);
}
This solution, implemented based on a solution in the book Elements of Programming Interviews seems to be correct for counting the number of 'combinations' (no duplicate sets) for a set of score points.
For example, if points = {7, 3, 2}, there are 2 combinations for a total score of 7:
{7} and {3, 2, 2}.
public static int ScoreCombinationCount(int total, int[] points)
{
int[] combinations = new int[total + 1];
combinations[0] = 1;
for (var i = 0; i < points.Length; i++)
{
int point = points[i];
for (var j = point; j <= total; j++)
{
combinations[j] += combinations[j - point];
}
}
return combinations[total];
}
I am not sure I understand the logic though. Can someone explain?
The answer to this question depends on whether or not you allow the total number of combinations to include duplicate unordered combinations.
For example, in American football, you can score 2, 3, or 7 points (yes, I know you can miss the extra point on a touchdown, but let's ignore 1 point).
Then if your target N is 5, then you can reach it with {2, 3} or {3, 2}. If you count that as two combinations, then the Dynamic Programming solution by #amit will work. However, if you count those two combinations as one combination, then the iterative solution by #Maximus will work.
Below is some Java code, where findWays() corresponds to counting all possible combinations, including duplicates, and findUniqueWays() corresponds to counting only unique combinations.
// Counts the number of non-unique ways to reach N.
// Note that this algorithm counts {1,2} separately from {2,1}
// Applies a recurrence relationship. For example, with values={1,2}:
// cache[i] = cache[i-1] + cache[i-2]
public static long findWays(int N, int[] values) {
long cache[] = new long[N+1];
cache[0] = 1;
for (int i = 1; i <= N; i++) {
cache[i] = 0;
for (int value : values) {
if (value <= i)
cache[i] += cache[i-value];
}
}
return cache[N];
}
// Counts the number of unique ways to reach N.
// Note that this counts truly unique combinations: {1,2} is the same as {2,1}
public static long findUniqueWays(int N, int[] values) {
long [] cache = new long[N+1];
cache[0] = 1;
for (int i = 0; i < values.length; i++) {
int value = values[i];
for (int j = value; j <= N; j++) {
cache[j] += cache[j-value];
}
}
return cache[N];
}
Below is a test case where the possible points are {2,3,7}.
private static void testFindUniqueWaysFootball() {
int[] points = new int[]{2, 3, 7}; // Ways of scoring points.
int[] NValues = new int[]{5, 7, 10}; // Total score.
long result = -1;
for (int N : NValues) {
System.out.printf("\nN = %d points\n", N);
result = findWays(N, points);
System.out.printf("findWays() result = %d\n", result);
result = findUniqueWays(N, points);
System.out.printf("findUniqueWays() result = %d\n", result);
}
}
The output is:
N = 5 points
findWays() result = 2
findUniqueWays() result = 1
N = 7 points
findWays() result = 4
findUniqueWays() result = 2
N = 10 points
findWays() result = 9
findUniqueWays() result = 3
The results above show that to reach N=7 points, then there 4 non-unique ways to do so (those ways are {7}, {2,2,3}, {2,3,2}, {3,2,2}). However, there are only 2 unique ways (those ways are {7} and {2,2,3}). However, .
Below is a python program to find all combinations ignoring the combination order (e.g. 2,3,6 and 3,2,6 are considered one combination). This is a dynamic programming solution with order(n) time. Scores are 2,3,6,7.
We traverse from row score 2 to row score 7 (4 rows). Row score 2 contains the count if we only consider score 2 in calculating the number of combinations. Row score 3 produces each column by taking the count in row score 2 for the same final score plus the previous 3 count in its own row (current position minus 3). Row score 6 uses row score 3, which contains counts for both 2,3 and adds in the previous 6 count (current position minus 6). Row score 7 uses row score 6, which contains counts for row scores 2,3,6 plus the previous 7 count.
For example, numbers[1][12] = numbers[0][12] + numbers[1][9] (9 = 12-3) which results in 3 = 1 + 2; numbers[3][12] = numbers[2][12] + numbers[3][9] (9 = 12-3) which results in 7 = 6 + 1;
def cntMoney(num):
mSz = len(scores)
numbers = [[0]*(1+num) for _ in range(mSz)]
for mI in range(mSz): numbers[mI][0] = 1
for mI,m in enumerate(scores):
for i in range(1,num+1):
numbers[mI][i] = numbers[mI][i-m] if i >= m else 0
if mI != 0: numbers[mI][i] += numbers[mI-1][i]
print('m,numbers',m,numbers[mI])
return numbers[mSz-1][num]
scores = [2,3,6,7]
num = 12
print('score,combinations',num,cntMoney(num))
output:
('m,numbers', 2, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
('m,numbers', 3, [1, 0, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 3])
('m,numbers', 6, [1, 0, 1, 1, 1, 1, 3, 1, 3, 3, 3, 3, 6])
('m,numbers', 7, [1, 0, 1, 1, 1, 1, 3, 2, 3, 4, 4, 4, 7])
('score,combinations', 12, 7)
Below is a python program to find all ordered combinations (e.g. 2,3,6 and 3,2,6 are considered two combinations). This is a dynamic programming solution with order(n) time. We build up from the start, adding the combinations calculated from previous score numbers, for each of the scores (2,3,6,7).
'vals[i] += vals[i-s]' means the current value equals the addition of the combinations from the previous values for the given scores. For example, for column vals[12] = the addition of scores 2,3,6,7: 26 = 12+9+3+2 (i-s = 10,9,6,5).
def allSeq(num):
vals = [0]*(num+1)
vals[0] = 1
for i in range(num+1):
for s in scores:
if i-s >= 0: vals[i] += vals[i-s]
print(vals)
return vals[num]
scores = [2,3,6,7]
num = 12
print('num,seqsToNum',num,allSeq(num))
Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsToNum', 12, 26)
Attached is a program that prints the sequences for each score up to the given final score.
def allSeq(num):
seqs = [[] for _ in range(num+1)]
vals = [0]*(num+1)
vals[0] = 1
for i in range(num+1):
for sI,s in enumerate(scores):
if i-s >= 0:
vals[i] += vals[i-s]
if i == s: seqs[i].append(str(s))
else:
for x in seqs[i-s]:
seqs[i].append(x + '-' + str(s))
print(vals)
for sI,seq in enumerate(seqs):
print('num,seqsSz,listOfSeqs',sI,len(seq),seq)
return vals[num],seqs[num]
scores = [2,3,6,7]
num = 12
combos,seqs = allSeq(num)
Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsSz,listOfSeqs', 0, 0, [])
('num,seqsSz,listOfSeqs', 1, 0, [])
('num,seqsSz,listOfSeqs', 2, 1, ['2'])
('num,seqsSz,listOfSeqs', 3, 1, ['3'])
('num,seqsSz,listOfSeqs', 4, 1, ['2-2'])
('num,seqsSz,listOfSeqs', 5, 2, ['3-2', '2-3'])
('num,seqsSz,listOfSeqs', 6, 3, ['2-2-2', '3-3', '6'])
('num,seqsSz,listOfSeqs', 7, 4, ['3-2-2', '2-3-2', '2-2-3', '7'])
('num,seqsSz,listOfSeqs', 8, 6, ['2-2-2-2', '3-3-2', '6-2', '3-2-3', '2-3-3', '2-6'])
('num,seqsSz,listOfSeqs', 9, 9, ['3-2-2-2', '2-3-2-2', '2-2-3-2', '7-2', '2-2-2-3', '3-3-3', '6-3', '3-6', '2-7'])
('num,seqsSz,listOfSeqs', 10, 12, ['2-2-2-2-2', '3-3-2-2', '6-2-2', '3-2-3-2', '2-3-3-2', '2-6-2', '3-2-2-3', '2-3-2-3', '2-2-3-3', '7-3', '2-2-6', '3-7'])
('num,seqsSz,listOfSeqs', 11, 18, ['3-2-2-2-2', '2-3-2-2-2', '2-2-3-2-2', '7-2-2', '2-2-2-3-2', '3-3-3-2', '6-3-2', '3-6-2', '2-7-2', '2-2-2-2-3', '3-3-2-3', '6-2-3', '3-2-3-3', '2-3-3-3', '2-6-3', '3-2-6', '2-3-6', '2-2-7'])
('num,seqsSz,listOfSeqs', 12, 26, ['2-2-2-2-2-2', '3-3-2-2-2', '6-2-2-2', '3-2-3-2-2', '2-3-3-2-2', '2-6-2-2', '3-2-2-3-2', '2-3-2-3-2', '2-2-3-3-2', '7-3-2', '2-2-6-2', '3-7-2', '3-2-2-2-3', '2-3-2-2-3', '2-2-3-2-3', '7-2-3', '2-2-2-3-3', '3-3-3-3', '6-3-3', '3-6-3', '2-7-3', '2-2-2-6', '3-3-6', '6-6', '3-2-7', '2-3-7'])
~
I'm trying to find an elegant way to lexically sort sequences of integers with LINQ. In other words, If I have these sequences of ints
7, 10, 12, 14, 15
10, 12, 15
10
7, 15
14, 15
I would hope to have them come out sorted like this
7, 10, 12, 14, 15
7, 15
10
10, 12, 15
14, 15
It is the same basic idea of sorting strings by their characters except I want to sort a sequence of ints instead. I don't want to sort alphabetically, but I do want the sequences lexically sorted.
The kind of sort you want is known as a lexical sort:
Given two partially ordered sets A and B, the lexicographical order on
the Cartesian product A × B is defined as (a,b) ≤ (a′,b′) if and only
if a < a′ or (a = a′ and b ≤ b′).
.Net gives you the tools to specify what kind of comparison you want when you want to sort. There are two structures to control this: IComparer and the Comparison<T> delegate. You can pass either one of these to List.Sort. Example:
var lists = new List<Int32[]> {
new [] { 7, 10, 12, 14, 15 },
new [] { 7, 15 },
new [] { 7, 15 },
new [] { 10 },
new [] { 10, 12, 15 },
new [] { 14, 15 } };
lists.Sort((a, b) => {
var result = a.Zip(b, Tuple.Create)
.Select(t => t.Item1.CompareTo(t.Item2))
.FirstOrDefault(c => c != 0);
return result == 0 && !a.Any() ? -1 : result; // Empty list minimum
});
(Download for LinqPad)
This passes a Comparison<Int32[]> delegate which zips the comparands, allowing element by element comparison and stops comparing when the first non-zero integer comparison is detected. If no unequal elements are found, it returns the default for Int32 which is 0, meaning the lists are lexicographically equal.
(Note I added another element to your set of lists to show that equal lists of integers sort correctly.)
Bonus chatter:
I thought this method would be faster than allocating strings and using OrderBy but after profiling there's no appreciable difference in speed. I tried to make it faster using a struct instead of Tuple, which did help a little, and probably saved GC allocation and memory usage, but I didn't measure memory usage performance. If performance is a concern, you'd probably end up eschewing the tidy Linq approach and write the Comparer using a loop.
You could convert the numbers to strings, pad them all to the same length and join the together, then order that, and finally split them up again.
var intLists = new List<List<int>>
{
new List<int> { 7, 10, 12, 14, 15 },
new List<int> { 10, 12, 15 },
new List<int> { 10 },
new List<int> { 7, 15 },
new List<int> { 14, 15 },
};
var orderedLists = intLists
.Select(l => string.Join("", l.Select (x => x.ToString().PadLeft(10))))
.OrderBy(l => l)
.Select(l => l.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select (x => int.Parse(x)));
Reference object: { 1, 5, 6, 9, 10, 11 }
Other objects:
A { 2, 4, 5, 6, 8, 10, 11 }
B { 5, 7, 9, 10 }
C { 2, 5, 6, 7, 9, 12 }
D { 1, 3, 4, 5, 6, 8, 9, 10 }
E { 6, 8 }
F { 1, 2, 3, 4, 7, 8, 9, 13, 15 }
... { ... }
Difficulty: It should be faster than O(n*m)
Result should be:
Array
(
[D] => 5
[A] => 4
[C] => 3
[B] => 3
[F] => 2
[E] => 1
)
Slow solution:
ref = array(1, 5, 6, 9, 10, 11);
foreach (A, B, C, D,.. AS row)
{
foreach (row AS col)
{
if ( exist(col, ref) )
{
result[row] += 1;
}
}
}
sort (result)
.. this is a solution, but its far to slow.
Is there another way like patter recognition, hopefully in O(log n)?
It is possible to save each object in an other notation, like for example:
ref = "15691011"
A = "2456811"
But I don't know if this helps.
If you have all data in your objects sorted, you can do this routine faster, by comparing not single values in the row, but whole row step by step.
foreach (A, B, C, D,.. AS row)
{
for (i = 0, j = 0; i < row.length && j < ref.length)
{
if (row[i] < ref[j]) i++;
elseif (row[i] > ref[j]) j++;
else {
result[row] += 1;
i++; j++;
}
}
}
In this case you pass you reference only once for each row, but this algorithm need all your data to be already sorted.
You could start with the largest sequence (it has the largest change to have many references).
When you find - for example - 4 refs, you can safely skip all sequences with less then 4 elements.
Another early exit is to abort checking a sequence, when the current sequence cannot surpass the current max. for example: Your current max is 6 elements. You are processing a list of size 7, but the first two elements are no reference. The highest reachable for this list is 5, which is lower than 6, abort the sequence.
Problem in both cases is that you can not construct a complete array of results.
Assumptions:
There are m lists apart from the reference object.
The lists are sorted initially.
There are no repetition of elements in any array.
Scan all the arrays and find out the maximum element in all the lists. You only need to check the last element in each list. Call it MAX.
For each of the m + 1 lists, make a corresponding Boolean array with MAX elements and initialize their values to zero.
Scan all the arrays and make the corresponding indices of arrays 1.
For example, the corresponding array for the example reference object { 1, 5, 6, 9, 10, 11 } shall look like:
{1,0,0,0,1,1,0,0,1,1,1,0,0,...}
Now for every pair-wise combination, you can just check the corresponding indices and increment the count if both are 1.
The above algorithm can be done in linear time complexity with regards to the total number of elements in the data.
You should use other techniques used in search engines. For each number, you have a list of object contained this number in sorted order. In your case
1 -> {D, F}
5 -> {A, B, C, D}
6 -> {A, C, D, E}
9 -> {B, C, D, F}
10 -> {A, B, D}
11 -> {A}
Merging this list you can count how your object is similar to objects in list
A -> 4
B -> 3
C -> 2
D -> 5
E -> 1
F -> 2
After sorting, you get needed result. If you need only top k elements, you should use a priority queue.
question origin
Given an unsorted array of size n containing objects with ids of 0 … n-1, sort the array in place and in linear time. Assume that the objects contain large members such as binary data, so instantiating new copies of the objects is prohibitively expensive.
void linearSort(int* input, const int n) {
for (int i = 0; i < n; i++) {
while (input[i] != i) {
// swap
int swapPoint = input[i];
input[i] = input[swapPoint];
input[swapPoint] = swapPoint;
}
}
}
Is this linear? Does this sort work with any kind of array of ints? If so, why do we need quicksort anymore?
Despite the while loop inside the for, this sort is linear O(n). If the while loop occurs multiple times for a given i then for the i values that match swapPoint there will not execute the while loop at all.
This implementation will only work for arrays of ints where there are no duplicates and the values are sequential from 0 to n-1, which is why Quicksort still is relevant being O(n log n) because it works with non-sequential values.
This can be easily tested by making the worst case:
input = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
and then using the following code:
int whileCount = 0;
for (int i = 0; i < n; i++)
{
while (input[i] != i)
{
whileCount++;
// swap
int swapPoint = input[i];
input[i] = input[swapPoint];
input[swapPoint] = swapPoint;
}
Console.WriteLine("for: {0}, while: {1}", i, whileCount);
}
The output will be as follows:
for: 0, while: 9
for: 1, while: 9
for: 2, while: 9
for: 3, while: 9
for: 4, while: 9
for: 5, while: 9
for: 6, while: 9
for: 7, while: 9
for: 8, while: 9
for: 9, while: 9
so you see even in the worst case where you have the while loop run n-1 times in the first iteration of the for loop, you still only get n-1 iterations of the while loop for the entire process.
Further examples with random data:
{7, 1, 2, 4, 3, 5, 0, 6, 8, 9} => 2 on i=0, 1 on i=3 and nothing more. (total 3 while loop runs)
{7, 8, 2, 1, 0, 3, 4, 5, 6, 9} => 7 on i=0 and nothing more (total 7 while loop runs)
{9, 8, 7, 4, 3, 1, 0, 2, 5, 6} => 2 on i=0, 2 on i=1, 1 on i=2, 1 on i=3 (total 6 while loop runs)
Each you put input[i] to the position swapPoint, which is exactly where it needs to go. So in the following steps those elements are already at the right place and the total time of exchange won't exceed the size n.