four in a row logic by creating AI - algorithm

I am developing 4 in a row game. in this i am creating AI.
I have used same logic as Four in a row logic.
in my game 0 represents empty slot. 1 represents user slot and 2 for computer slot.
now i want to develop AI such that if user means '1' are three in a row then put 2 to its 4th row.
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0]
i want to put 2 on last row 4th column after '1' like [0, 1, 1, 1, 2, 0, 0] and by creating AI then what to do?

The AI could be implemented by using the Minimax algorithm which can be found on Wikipedia under the following link:
http://en.wikipedia.org/wiki/Minimax
Basically, you need a function f to decide for a state of the game whether player 1 has won (value 1) or player 2 has won (value -1) or the game has not ended yet. If the game has not ended, all moves for the current player have to be evaluated by simulating them on the board and recursively calling F again, where the respective other player is active. The value of a possible move will be the maximum of the return values of F (if player 1 is the current player) or the minimum of the return values of F (if player 2 is the current player).
At least this is the rough idea; depending on the programming language used for implementation, the evaluation logic can be implemented independently from the specific game, such that the same code can play four-in-a-row or tic-tac-toe. Furthermore, evaluation can be stopped if a move of value 1 (for player 1) or -1 (for player 2) is found, which is also termed "pruning of the search tree".

use this part of code to prevent creating situations like 0,1,1,1,0 by user in all rows, it will return number of column that computer should play with that:
for(byte i=0;i<6;i++)
{
byte min = (byte) ((i * 7) + 1);
for(int j = 0;j<=2;j++) {
if (boardValues[min+j] == 1 && boardValues[min + j + 1] == 1 && boardValues[min - 1 + j] == 0 && boardValues[min +j + 2] == 0) {
if (i == 5) return (byte) ((min + j - 1)%7);
else if(boardValues[min - 1 + j + 7] != 0 && boardValues[min +j + 2 + 7] != 0)
return (byte) ((min + j - 1)%7);
}
}
}
boardValues is difined like this in my code:
public static byte[] boardValues = new byte[42];

It's maybe too late, but if someone is still interested to know, Here is a series of articles - http://blog.gamesolver.org/ .
It explains the concepts and logic, step by step to make a connect 4 resolver AI.

Related

Dynamic programming (Solve combination of scores) [duplicate]

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'])
~

Lua Proportional Matrix Enlargement with Stretched Values

I am currently working in a game spell system and I want to know if anyone knows a simple way to enlarge a matrix and also its values, almost like an image stretch.
I am using 2D matrices to represent the spell affected areas, so the below matrix represent the starting spell effect point and its effect area.
Example:
local area = {{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{1, 1, 3, 1, 1},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0}}
Where:
3: Origin point (where the spell was cast)
1: Affected area relative to the origin point.
Taking this in consideration, I would like to develop a function to enlarge the matrix.
function matrix.enlarge(mtx, row, col) ... end
The abstraction and result of the following function taking the shown example of an area would be something like following:
local enlarged_matrix = matrix.enlarge(area, 2, 2)
matrix.print(enlarged_matrix)
--output
--local area = {{0, 0, 0, 1, 0, 0, 0},
-- {0, 0, 1, 1, 1, 0, 0},
-- {0, 1, 1, 1, 1, 1, 0},
-- {1, 1, 1, 3, 1, 1, 1},
-- {0, 1, 1, 1, 1, 1, 0},
-- {0, 0, 1, 1, 1, 0, 0},
-- {0, 0, 0, 1, 0, 0, 0}}
Several possibilities:
brute force: create new matrix, copy old into it:
function matrix.enlarge(area, horiz, vert)
local vertNow = #vert
local horizNow = #horiz
local newVert = vertNow + vert
local newHoriz = horizNow + horiz
-- create table of zeros
local newMatrix = {}
for i=1,newVert do
tt = {}
newMatrix[i] = tt
for j=1,newHoriz do
if i > vert/2 and i < vertNow + vert/2 and j > horiz/2 and j < horizNow + horiz/2 then
tt[j] = area[i][j]
else
tt[j] = 0
end
end
end
end
use formula: you have circular symmetry so just need radius, no need to store the value:
function distance(i,j)
return math.sqrt(i*i+j*j)
end
local dissip = 2 -- at a distance of "2", spell is e^(-0.5) of center
function getSpellStrength(dist) -- gaussian
return 3*math.exp(-math.pow(dist/dissip, 2))
end
val = getSpellStrength(distance(i,j))
If the actual computation of spell strength is heavy, and spread doesn't change often (say only when experience increases by a certain delta), then option 1 better. If spread changes quickly (say every time frame while spell taking effect), and spell strength as simple as gaussian, then option 2 better. For in-between cases it depends you'll have to try both. But #2 is simpler so I would favor it unless you can show that it is a performance bottleneck.
Also, the formula (option 2) is trivial to apply regardless of shape of room/area. If an enemy is at i1,j1, and caster at i2,j2, you can know immediately the spell strength at i1,j1 via distance(i1-i2,j1-j2), regardless of shape of room. You can also fairly easily combine effects of multiple spells, like a resistence spell by enemy (same distance formula).
If you really have to use matrix, and it must work for any shape, then probably this is best option:
scale the old matrix to a new matrix:
function enlarge(area, deltaX, deltaY)
sizeX = #(area[1])
sizeY = #area -- number of rows
scaleX = (sizeX + deltaX)/sizeX
scaleX = (sizeY + deltaY)/sizeY
newArea = {}
for iY=1, sizeY do
newRow = {}
newArea[iY] = newRow
fromY = round(iY/scaleY)
for jX=1, sizeX do
fromX = round(jX/scaleX)
if fromY < 1 or fromX < 1 or fromY > sizeY or fromX > sizeX then
val = 0
else
val = area[fromY][fromX]
end
newRow[jX] = val
end
end
return newArea
end
Here, you're basically creating a scaled version of the original (interpolation). WARNING: Not debugged, so you will have to massage this a bit (like there might be +-1 missing in a few places, you should declare your vars local, etc). And round() would be something like
function round(x)
return math.floor(num + 0.5)
end
But hopefully you can do the rest of the work :)

Finding a substring, with some additional conditions

I'm given a string which looks like this:
1011010100
And my task is to find the length of a substring which number of nulls is always <= number of ones. And this should always happen while 'scanning' substring from right to left and from left to right. So in this example, the answer would be:
10110101 => 8
I know that the complexity should be either O(n) or O(n log n), because length can reach up to 10^6
Any ideas?
The O(n) solution is quite simple actually, by building the "height array", representing the number of 1's relative to number of 0's. So a height of 2 means there are 2 more 1's than 0's. The we iterate over the height array once performing some maximality checking with some conditions.
Crucial Observation
Note that a subarray fulfilling the conditions must have its height minimum at the beginning, and maximum at the end (as relative to the subarray, not the whole array).
The height array for the sample in the question can be plotted like this, with marked answer:
v
/\/\/\
/\/ \
^
Proof:
Suppose the height is not minimum at the beginning, that means there is a point inside the subarray where the height is lower than the beginning. At this point, the number of 0 should be larger than the number of 1. Contradiction.
Suppose the height is not maximum at the end, that means there is a point in the subarray where the height is larger than the end, say at index j. Then at index j to the end there are more 0 than 1 (since the height decreases), and so when we "scan" the subarray from right to left we will find more 0 than 1 at index j. Contradiction.
Algorithm
Now the problem can be interpreted as finding the longest subarray which ends with the highest height in the subarray, while keeping the minimum to not exceed the height at the beginning. This is very similar to maximum subarray problem like mentioned by klrmlr ("contiguous subsequence of an array" is better said as "subarray"). And the idea is not keeping an O(n) state, but rather keeping the "maximum so far" and "maximum at this point".
Following that algorithm, below is the pseudocode, traversing the array once:
Procedure Balance_Left_Right
Record the lowest and highest point so far
If the height at this point is lower than the lowest point so far, then change the starting point to the index after this point
If the height at this point is higher or equal to the highest point so far, then this is a valid subarray, record the length (and start and end indices, if you like)
However we will soon see a problem (as pointed by Adam Jackson through personal communication) for this test case: 1100101, visualized as follows:
/\
/ \/\/
The correct answer is 3 (the last 101), but the above algorithm will get 2 (the first 11). This is because our answer is apparently hidden behind a "high mountain" (i.e., the lowest point in the answer is not lower than the mountain, and the highest point in the answer is not higher than the mountain).
And so we need to ensure that when we run the Procedure Balance_Left_Right (above), there is no "high mountain" hiding the answer. And so the solution is to traverse the array once from the right, try to partition the array into multiple sections where in each section, this property holds: "the number of 1's is always >= the number of 0's, as traversed from the right", and also for each section, it can't be extended to the left anymore.
Then, in each section, when traversed from the left, will have the maximum height at the end of the section, and this is the maximum. And it can be proven that with this property, the method balance_left_right will find the correct answer for this section. So, we just call our balance_left_right method on each section, and then take the maximum answer among those.
Now, you may ask, why it's sufficient to run Balance_Left_Right on each section? This is because the answer requires the property to hold from the left and from the right, and so it must lies inside one of the sections, since each of the section satisfies half of the property.
The algorithm is still O(n) because we only visit each element twice, once from the right, and once from the left.
The last test case will be partitioned as follows:
/|\ |
/ | \|/\/
** ***
where only the sections marked with asterisk (*) are taken.
So the new algorithm is as follows:
Procedure Max_Balance_Left_Right
Partition the input where the number of 1 >= number of 0 from the right (Using Balance_Left from the right, or can call it Balance_right)
Run Balance_Left_Right on each partition
Take the maximum
Here's the code in Python:
def balance_left_right(arr):
lower = 0
upper = -2**32
lower_idx = 0 # Optional
upper_idx = -1 # Optional
result = (0,0,0)
height = 0
length = 0
for idx, num in enumerate(arr):
length += 1
height += 1 if num==1 else -1
if height<lower:
lower = height # Reset the lowest
upper = height # Reset the highest
lower_idx = idx+1 # Optional, record the starting point
length = 0 # Reset the answer
if height>=upper:
upper = height
upper_idx = idx # Optional, record the end point
if length > result[0]: # Take maximum length
result = (length, lower_idx, upper_idx)
return result
def max_balance_left_right(arr):
all_partitions = []
start = 0
end = len(arr)
right_partitions = balance_left(reversed(arr[start:end]))
for right_start, right_end in right_partitions:
all_partitions.append((end-right_end, end-right_start))
result = (0,0,0)
for start, end in all_partitions:
candidate = balance_left_right(arr[start:end])
if result[0] < candidate[0]:
result = (candidate[0], candidate[1]+start, candidate[2]+start)
return result
def balance_left(arr):
lower = 0
start_idx = 0
end_idx = -1
height = 0
result = []
for idx, num in enumerate(arr):
height += 1 if num==1 else -1
if height < lower:
if end_idx != -1:
result.append((start_idx,end_idx))
lower = height
start_idx = idx+1
end_idx = -1
else:
end_idx = idx+1
if end_idx != -1:
result.append((start_idx, end_idx))
return result
test_cases = [
[1,0,1,1,0,1,0,1,0,0],
[0,0,1,0,1,0,0,1,0,1,0,0,1,1,0,1,0,1,0,0,1],
[1,1,1,0,0,0,1,0,0,1,1,0,1,1,0,1,1,0],
[1,1,0,0,1,0,1,0,1,1,0,0,1,0,0],
[1,1,0,0,1,0,1],
[1,1,1,1,1,0,0,0,1,0,1,0,1,1,0,0,1,0,0,1,0,1,1]
]
for test_case in test_cases:
print 'Balance left right:'
print test_case
print balance_left_right(test_case)
print 'Max balance left right:'
print test_case
print max_balance_left_right(test_case)
print
which will print:
Balance left right:
[1, 0, 1, 1, 0, 1, 0, 1, 0, 0]
(8, 0, 7)
Max balance left right:
[1, 0, 1, 1, 0, 1, 0, 1, 0, 0]
(8, 0, 7)
Balance left right:
[0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]
(6, 12, 17)
Max balance left right:
[0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]
(6, 12, 17)
Balance left right:
[1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0]
(8, 9, 16)
Max balance left right:
[1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0]
(8, 9, 16)
Balance left right:
[1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0]
(10, 0, 9)
Max balance left right:
[1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0]
(10, 0, 9)
Balance left right:
[1, 1, 0, 0, 1, 0, 1]
(2, 0, 1)
Max balance left right:
[1, 1, 0, 0, 1, 0, 1]
(3, 4, 6)
Balance left right:
[1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1]
(5, 0, 4)
Max balance left right:
[1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1]
(6, 8, 13)
For your eyes enjoyment, the height array for the test cases:
First:
v
/\/\/\
/\/ \
^
Second:
\
\/\/\ v
\/\/\ /\/\/\
\/ \/
^
Third:
v
/\ /\
/ \ /\/
/ \/\ /\/
\/
^
Fourth:
v
/\ /\
/ \/\/\/ \/\
^ \
Fifth:
/\ v
/ \/\/
^
Sixth:
/\ v
/ \ /\
/ \/\/\/ \/\ /
/ ^ \/\/
/
Clarification Regarding the Question
As some of the readers are confused on what exactly OP wants, although it's already stated clearly in the question, let me explain the question by some examples.
First, the task from the question:
And my task is to find the length of a substring which number of nulls is always <= number of ones. And this should always happen while 'scanning' substring from right to left and from left to right
This refers to something like "Catalan Number Ballot Problem" or "Available Change Problem". In the Wiki you can check the "monotonic path" problem, where you can map "move right" as "1" and "move up" as "0".
The problem is to find a subarray of the original array, such that, when the subarray is traversed from left-to-right and right-to-left, this property holds:
The number of 0's seen so far should not exceed the number of 1's seen so far.
For example, the string 1010 holds the property from left-to-right, because if we scan the array from left-to-right, there will always be more 1's than 0's. But the property doesn't hold from right-to-left, since the first character encountered from the right is 0, and so at the beginning we have more 0's (there is one) than 1's (there is none).
For example given by OP, we see that the answer for the string 1011010100 is the first eight characters, namely: 10110101. Why?
Ok, so when we traverse the subarray from left to right, we see that there is always more 1's than 0's. Let's check the number of 1's and 0's as we traverse the array from left-to-right:
1: num(0) = 0, num(1) = 1
0: num(0) = 1, num(1) = 1
1: num(0) = 1, num(1) = 2
1: num(0) = 1, num(1) = 3
0: num(0) = 2, num(1) = 3
1: num(0) = 2, num(1) = 4
0: num(0) = 3, num(1) = 4
1: num(0) = 3, num(1) = 5
We can see that at any point of time the number of 0's is always less than or equal to the number of 1's. That's why the property holds from left-to-right. And the same check can be done from right-to-left.
So why isn't 1011010100 and answer?
Let's see when we traverse the string right-to-left:
0: num(0) = 1, num(1) = 0
0: num(0) = 2, num(1) = 0
1: num(0) = 2, num(1) = 1
...
I didn't put the full traversal because the property has already been violated since the first step, since we have num(0) > num(1). That's why the string 1011010100 doesn't satisfy the constraints of the problem.
You can see also that my "height array" is actually the difference between the number of 1's and the number of 0's, namely: num(1) - num(0). So in order to have the property, we must have the [relative] height positive. That, can be visualized by having the height not less than the initial height.
Here goes my algorithm:
Start from right side:
1. if you find 0 increment the value of count
2. if you find 1 decrement the count
Store these values in an array i.e. v[].
e.g.
a[] = {1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1}
v[] = {0, 1, 0,-1, 0, 1, 0, 1, 2, 1, 2, 1, 0, -1}
Now the problem reduces to find indexes from V i.e. i, j such that v[i] < v[j] and i<j.
proof:
if you see here i=0 and j=11 is the possible answer and values are v[i]=0, v[j]=1.
This means that till j we have one 0 extra in the string and as the v[i]=0 that means from i to j window size the extra 0 is cancelled by putting extra 1. Hence the answer.
Hope it helps. please let me know if you have doubt. Thanks.
(Almost correct, i.e. subtly wrong) linear time solution
with two recodings of the problem (one removed later...), and a sliding window.
Encoding A
You can compress the input to yield the number of subsequent zeros or ones:
+1 -1 +2 -1 +1 -1 +1 -2
This yields encoding A and needs O(n) time.
Encoding B
Now, in encoding A, whenever you encounter two consecutive numbers that sum up to > 0, you compress further. In encoding B, the number in parentheses denotes the length of the substring:
+2(4) -1 +1 -1 +1 -2 ==> +2(6) -1 +1 -2 ==> +2(8) -2
This requires O(n), too. Here, we have the solution immediately: A string of length 8 with two more 1's than 0's. Let's try a more complicated instance (given in encoding A):
+5 -8 +4
Here, the transformation to encoding B doesn't help:
+5(5) -8 +4(4)
Consider also the following instance (encoding B):
+5(9) -6 +4(4) -6 +5(7) -6 +4(6) -6 +5(9)
This sequence will be used to demonstrate the...
Sliding window
First, determine the best solution that starts at the left:
+5 -6 +4 -6 +5 > 0 ==> 9+6+4+6+7=32
Now, extend this to find the best solution that starts at the third position (+4(4)):
+4 -6 +5 -6 +4 > 0 ==> 4+6+7+6+6=29
This solution is not better than the first we have found. Move on:
+5 -6 +4 -6 +5 > 0 ==> 7+6+6+6+9=34
This is the best solution. The algorithm can be implemented in O(n), since head and tail move only forward.
The brief description above doesn't cover all subtleties (negative number at the left in encoding B, head and tail meet, ...). Also, perhaps the recodings are unnecessary and the sliding window can be implemented directly on the 0-1 representation. However, I was able to fully understand the problem only after recoding it.
Getting rid of encoding B
Actually as kindly noted by Millie Smith, "encoding B" might be lossy, meaning that it might lead to inferior solutions in certain (yet to be identified) corner cases. But the sliding window algorithm works just as well on encoding A, so it might be even necessary to skip the conversion to encoding B. (Too lazy to rewrite the explanation of the algorithm...)

How to count children in a tree

I have a list of numbers:
[1, 2, 3, 4, 5, 6, 7]
I'm interested in finding an algorithm that can sum the total children in this list if the list where a tree:
1
/ \
2 3
/ \ / \
4 5 6 7
I'm looking for an algorithm that would give:
[6, 2, 2, 0, 0, 0, 0]
A = 6
B = 2
C = 2
D = 0
E = 0
F = 0
G = 0
Each node (except the leaves) has two children. The only exception is if the list if even:
1
/ \
2 3
/ \ /
4 5 6
I would like to avoid building a tree and then counting the number of children at each node. There must be a simple mathematical way to count the number of children from a list?
1-indexed the array.
Then for node with index i, the left son is with the index 2*i, and right is the 2*i+1.
Then go through the array from the end, for the node now:
if index of his (left or right)son is out of bound of array, then he has no (left or right)son.
If not, then you can know the number of the children of his son(we go through the array from then end).The result = number of children of now's son + number of now's son.
For example:
[1, 2, 3, 4, 5, 6, 7]
A is the result array.
1.A=[0, 0, 0, 0, 0, 0, 0],now(now is a index) = 7(1-indexed) since 7*2>7, a[7]=0
2.A=[0, 0, 0, 0, 0, 0, 0],now = 6,since 6*2>7, a[6]=0
3.A=[0, 0, 0, 0, 0, 0, 0],now = 5,since 5*2>7, a[5]=0
4.A=[0, 0, 0, 0, 0, 0, 0],now = 4,since 4*2>7, a[4]=0
5.A=[0, 0, 2, 0, 0, 0, 0],now = 3,since 3*2<7 and 3*2+1<7, a[3]=2+a[6]+a[7]=2
6.A=[0, 2, 2, 0, 0, 0, 0],now = 2,since 2*2<7 and 2*2+1<7, a[2]=2+a[4]+a[5]=2
7.A=[6, 2, 2, 0, 0, 0, 0],now = 1,since 1*2<7 and 1*2+1<7, a[1]=2+a[2]+a[3]=6
For the case where the tree is balanced (i.e. the number of elements in the input list is odd), this can be calculated with:
n = length of elements in input list
Then for element i in the output list:
d = depth of element in tree = floor(log2(i+1))+1
Then the number of children below that element in the tree is:
n_children = n - ((2^d)-1) / 2^(d-1)
So for your example of [1,2,3,4,5,6,7]:
n = 7
For array position 0 (i.e. node 1):
d = depth = floor(log2(1))+1 = 1
n_children = (7 - ((2^1)-1)) / 2^(1-1)
= (7 - 1) / 2^0
= 6 / 1
= 6
Then for then array position 1, (i.e. node 2):
d = depth = floor(log2(2))+1 = 2
n_children = (7 - ((2^2)-1)) / 2^(2-1)
= (7 - 3) / 2
= 2
Carrying on with this gives [6, 2, 2, 0, 0, 0, 0] for i=0 to i=6.
Python code for this would look like:
import math
def n_children(list_size, i):
depth = math.floor(math.log(i+1,2)) + 1
return (list_size - ((2**depth)-1)) / 2**(depth-1)
print [n_children(7, i) for i in range(7)]
This outputs [6.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0].
It'd need some modification to deal with even numbered input arrays though (easiest way might be to round array size up to nearest odd number, then subtract 1 from any odd numbered values of i, or similar).
Interpret the first array as a heap, in which the children of node n are at 2*n+1 and 2*n+2, then recursively travel the tree:
def children(t, n):
if 2 * n + 1 >= t:
return 0
elif 2 * n + 2 >= t:
return 1
else:
return 2 + children(t, 2 * n + 1) + children(t, 2 * n + 2)
size = 7
childcounts = [ children(size, i) for i in range(size) ]
print(childcounts)
This will print:
[6, 2, 2, 0, 0, 0, 0]
Like we do in heap,
children[i] = sum of children of all its child + number of child
Like for 0th element, a[0] = number of children of its left child + number of children of its right child + number of its child
so a[0] = 2 + 2 + 2
for(int i=n-1;i>=0;i--) {
if(i*2+2 < n)
a[i]+=a[i*2+2]+1;
if(i*2+1 < n)
a[i]+=a[i*2+1]+1;
}

Algorithm to get all combinations of (American) football point-accumulations necessary to get a certain score N

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'])
~

Resources