I am trying to write a program that simplifies mathematical expressions.
I have already written a parser that converts a string to a binary tree.
For example (1+2)*x will become
*
/ \
+ x
/ \
1 2
The idea I had of simplifying such trees is as follows:
You store a set of trees and their simplified version
For example
* +
/ \ / \
a + and * *
/ \ / \ / \
b c a b a c
(Where a,b,c can be any subtree)
Then, If I find a subtree that matches one of the stored trees, I will
replace it with its simplified version.
If necessary I will repeat the process until the tree is fully simplified.
The problem with this approach is that it can't "combine like terms" in some cases.
For example, if I try to store the tree:
+ *
/ \ and / \
x x 2 x
Then when I try to simplify the expression x+y+x, with the following tree:
+
/ \
x +
/ \
y x
It will not be simplified to 2x+y, because the subtree
+
/ \
x x
Is not contained in the tree, thus the tree will not be simplified.
I tried writing an explicit algorithm that combine like terms but there are too many
cases to consider.
Can anyone please help me find a solution to this problem?
Here is one of the basic ideas which is used in computer algebra systems.
For operators like Plus (+) and Times (*) you can define attributes like Flat (associativity) and Orderless (commutativity). Also don't define Plus and Times as "binary" operators but as "multiple-argument" operators.
So an input like:
Plus(x,Plus(y,x))
in the first step can be transformed (flattened) because of the Flat attribute to
Plus(x,y,x)
in the next step it can be transformed (sorted) because of the Orderless attribute to
Plus(x,x,y)
In your "evaluation" step you can now go through the arguments and "simplify" the expression to:
Plus(Times(2,x),y)
This approach has the advantage that expressions which are "structural equal" are stored in the same "canonical form" and could for example easier compared for "object equality" in the used programming language.
We may consider polynomials
we get the '+'-reducer and '*'-reducer for two polynomes in X
Now in the tree, instead of considering either a scalar or x as node, we may consider an "irreducible" polynomial.
Then we apply the '*'-reducer if the node operator is * or '+'-reducer otherwise which both transform two irreducibles polynome as an new irreducible one.
e.g where P_a, P_b two polynomes and
P_a = {
x0: 1 // term of degree 0 idem 1
x1: 2 // 2x
x3: 4 // 4x^3
}
and P_b = {x1: 3}
we get for sum: P_a + P_b = {x0: 1, x1: 5, x3: 4}
(so tree ['+', P_a, P_b] simplifies as {x: 0, x1: 5, x3: 4})
we get for multiplication: P_a * P_b = {x1: 3, x2: 6, x3: 12}
At the end of the day, we get an irreducible polynome in X.
We can write back that polynome as a binary tree (which is thus a simplified tree):
for each monome (in X^i), write its associated binary tree (which only contains * operator)
e.g: 5x^3 => ['*', ['*', ['*', x, x], x], 5]
then sum them
e.g: 1 + x + x^2 => ['+', 1, ['*', x, 1], ['*', x, x]
Same idea (idem implementing '+'-reducer/'*'-reducer) can be applied with an expression having polynomes in X, Y or Z, or whatever (so in your case, x, y)
below an example of implementation (you may uncomment and pass the tests using nodejs)
// a, b are polynomes of form {monomialKey: scalar, monomialKey2, scalar2, ...}
// a monomial key is e.g x1y2z2
const add = (a, b) => {
const out = Object.assign({}, a)
Object.entries(b).forEach(([monomialKey, scalar]) => {
out[monomialKey] = (out[monomialKey] || 0) + scalar
if (out[monomialKey] === 0) {
delete out[monomialKey]
}
})
return out
}
// transforms x1y2z2 to {x: 1, y: 2, z: 2}
const parseKey = s => s.match(/[a-z]+\d+/g).reduce((o, kv) => {
const [,varname,deg] = kv.match(/([a-z]+)(\d+)/)
o[varname] = parseInt(deg)
return o
}, {})
const writeKey = o => Object.entries(o).reduce((s, [varname, deg]) => s + varname+deg, '')
// simplify monomial, e.g x1y3*x1 => x2y3
const timesMonomialKey = (iA, iB) => {
const a = parseKey(iA)
const b = parseKey(iB)
const out = {}
;[a,b].forEach(x => Object.entries(x).forEach(([varname, deg]) => {
if (deg === 0) return
out[varname] = (out[varname] || 0) + deg
}))
if (Object.keys(out).length === 0) return writeKey({ x: 0 })
return writeKey(out)
}
// a, b both polynomes
const times = (a, b) => {
const out = {}
Object.entries(a).forEach(([monimalKeyA, sA]) => {
Object.entries(b).forEach(([monimalKeyB, sB]) => {
const key = timesMonomialKey(monimalKeyA, monimalKeyB)
out[key] = (out[key] || 0) + sA * sB
if (out[key] === 0) {
delete out[key]
}
})
})
return out
}
const reduceTree = t => { // of the form [operator, left, right] or val
if (!Array.isArray(t)) {
return typeof(t) === 'string'
? { [writeKey({ [t]: 1 })]: 1 } // x => {x1: 1}
: { [writeKey({ x: 0 })]: t } // 5 => {x0: 5}
}
const [op, leftTree, rightTree] = t
const left = reduceTree(leftTree)
const right = reduceTree(rightTree)
return op === '+' ? add(left, right) : times(left, right)
}
const writePolynomial = o => {
const writeMonomial = ([key, s]) => {
const a = parseKey(key)
const factors = Object.entries(a).flatMap(([varname, deg]) => {
return Array.from({length: deg}).fill(varname)
}).concat(s !== 1 ? s : [])
return factors.reduce((t, next) => ['*', t, next])
}
if (Object.keys(o).length === 0) return 0
return Object.entries(o).map(writeMonomial).reduce((t, next) => ['+', t, next])
}
console.log(writePolynomial(reduceTree(['+', ['+', 'x', 'y'], 'x'])))
//const assert = require('assert')
//assert.deepEqual(parseKey('x0y2z3'), { x: 0, y: 2, z: 3 })
//assert.deepEqual(writeKey({ x: 0, y: 2, z: 3 }), 'x0y2z3')
//assert.deepEqual(timesMonomialKey('x1y2', 'x3z1'), 'x4y2z1')
//assert.deepEqual(timesMonomialKey('x0y0', 'z0'), 'x0')
//assert.deepEqual(timesMonomialKey('x0y0', 'z0x1'), 'x1')
//assert.deepEqual(add({x0: 3, x1: 2}, {x0: 4, x3: 5}), {x0: 7, x1: 2, x3: 5})
//assert.deepEqual(add({x0: 3, y1: 2}, {x0: 4, y2: 5}), {x0: 7, y1: 2, y2: 5})
//assert.deepEqual(add({x0: 1}, {x0: -1}), {})
//assert.deepEqual(times({x0: 3, x1: 2}, {x0: 4, x1: 5}), {x0: 12, x1: 23, x2: 10})
//assert.deepEqual(times(
// {x1y0: 3, x1y1: 2},
// {x1y0: 4, x1y1: 5}),
// {x2: 12, x2y1: 23, x2y2: 10}
//)
//assert.deepEqual(reduceTree('x'), {x1: 1})
//assert.deepEqual(reduceTree(['*', 2, 'x']), {x1: 2})
//assert.deepEqual(reduceTree(['+', 2, 'x']), {x0: 2, x1: 1})
//assert.deepEqual(reduceTree(['+', 'x', ['+', 'y', 'x']]), {x1: 2, y1: 1})
//assert.deepEqual(writePolynomial({ x1y1:1, x1y2: 2}), ['+', ['*', 'x', 'y'], ['*', ['*', ['*', 'x', 'y'], 'y'], 2]])
//assert.deepEqual(writePolynomial(reduceTree(['*', ['*', 'x', 'y'], 0])), 0)
//assert.deepEqual(writePolynomial(reduceTree(['+', ['*', ['*', 'x', 'y'], 0], 2])), 2)
//
//// finally your example :)
//assert.deepEqual(writePolynomial(reduceTree(['+', ['+', 'x', 'y'], 'x'])), ['+', ['*', 'x', 2], 'y'])
Related
I have an array of arrays and a matching array. Each array has unique id values.
MatchingArray = [1,2,3,4,5,6]
A1 = [1, 4, 6]
A2 = [2,3,5]
A3 = [1,5]
A4 = [4]
A5 = [1, 6]
Need to find "optimal matchings". An optimal matching is an array of subsets from A1-A5 with minimal length, which should have a maximum possible intersection with MatchingArray.
For this example there are 2 possible matchings with a maximum intersection: M1 = [[2,3,5], [1, 4, 6]] and M2 = [[1,5], [4], [1, 6]]. But M1.length < M2.length, so the algorithm should output M1.
You could use sets (or hashes, whatever the language calls them) to optimise the time efficiency.
Convert the target array to a set, and then subtract the selected source from it (i.e. removing common values). Keep doing this recursively until the target set is empty. Keep track of the best result (using the fewest source arrays as possible). Backtrack if the number of source arrays being used gets past the length of the best solution already found at that moment.
Here is the code in Python:
def find_optimal_coverage(target, sources):
max_size = len(target)
best = None
def recurse(target, sources, selected):
nonlocal max_size, best
if len(target) == 0:
best = selected
max_size = len(best) - 1
return True
if len(selected) == max_size:
return None
for i, source in enumerate(sources):
result = recurse(target - set(source), sources[i+1:],
selected + [list(source)])
if result:
return True
target = set(target) # convert to set for faster lookup
# limit the source lists to elements that occur in the target
sources = list(map(target.intersection, sources))
# limit target to elements that occur in at least one source
target = set.union(*sources)
# sort sources by decreasing length to maximise probability of
# finding optimal solution sooner
sources.sort(key = len, reverse = True)
if recurse(target, sources, []):
return best
result = find_optimal_coverage(
[1, 2, 3, 4, 5, 6, 8],
[
[1, 4, 6, 7],
[2, 3, 5],
[1, 5],
[4],
[1, 6]
]
)
print(result)
See it run on repl.it
In JavaScript:
function subtractArray(s, arr) {
return arr.reduce( (s, v) => (s.delete(v), s), new Set(s) );
}
function findOptimalCoverage(target, sources) {
var maxSize = target.size;
var best = null;
function recurse(target, sources, selected) {
if (target.size == 0) {
best = selected;
maxSize = best.length - 1;
return true;
}
if (selected.length == maxSize) return;
return sources.some( (source, i) =>
recurse(subtractArray(target, source), sources.slice(i+1),
selected.concat([source]))
);
}
target = new Set(target) // convert to set for faster lookup
// limit the source arrays to elements that occur in the target
sources = sources.map( source => source.filter(target.has.bind(target)));
// limit target to elements that occur in at least one source
target = new Set([].concat(...sources));
// sort sources by decreasing length to maximise probability of
// finding optimal solution sooner
sources.sort( (a,b) => b.length - a.length );
if (recurse(target, sources, [])) return best;
}
var result = findOptimalCoverage(
[1, 2, 3, 4, 5, 6, 8],
[
[1, 4, 6, 7],
[2, 3, 5],
[1, 5],
[4],
[1, 6]
]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Implemented algorithm in javascript:
var matchingArray = [1, 2, 3, 4, 5, 6];
var A1 = [1, 4, 6],
A2 = [2, 3, 5],
A3 = [1, 5],
A4 = [4],
A5 = [1, 6];
var M = [A1, A2, A3, A4, A5];
function compareArrays(M, machingArray) {
var intersections = []
M.forEach(function(A) {
var partOfItersections;
if (A.length > 0) {
var intersectionsCount = getIntersectionCount(A, machingArray);
partOfItersections = intersectionsCount / A.length;
} else {
partOfItersections = 0
}
intersections.push({
length: A.length,
partOfItersections: partOfItersections
});
});
//alert(JSON.stringify(intersections));
var maxLength = 0,
maxPartOfItersections = 0,
optimalArrays = [];
intersections.forEach(function(arrayData, index) {
var currentArr = M[index];
var currentArrLength = currentArr.length;
if (maxPartOfItersections < arrayData.partOfItersections) {
setCurrentOptimalArr(arrayData.partOfItersections, currentArr);
} else if (maxPartOfItersections === arrayData.partOfItersections) {
if (maxLength < currentArrLength) {
setCurrentOptimalArr(arrayData.partOfItersections, currentArr);
} else if (maxLength === currentArrLength) {
optimalArrays.push(currentArr);
}
}
});
//alert(JSON.stringify(optimalArrays));
return optimalArrays;
function setCurrentOptimalArr(intersectionsCount, currentArr) {
optimalArrays = [currentArr];
maxLength = currentArr.length;
maxPartOfItersections = intersectionsCount;
}
function getIntersectionCount(A, machingArray) {
var intersectionCount = 0;
A.forEach(function(elem) {
if (machingArray.indexOf(elem) != -1) {
intersectionCount++;
}
});
return intersectionCount;
}
}
alert(JSON.stringify(compareArrays(M, matchingArray)));
Count intersection of arrays separately.
Return arrays which contain more part of intersections.
Code updated
I have an API for reading multi-dimensional arrays, requiring to pass a vector of ranges to read sub-rectangles (or hypercubes) from the backing array. I want to read this array "linearly", all elements in some given order with arbitrary chunk sizes. Thus, the task is with an off and a len, to translate the elements covered by this range into the smallest possible set of hyper-cubes, i.e. the smallest number of read commands issued in the API.
For example, we can calculate index vectors for the set of dimensions giving a linear index:
def calcIndices(off: Int, shape: Vector[Int]): Vector[Int] = {
val modsDivs = shape zip shape.scanRight(1)(_ * _).tail
modsDivs.map { case (mod, div) =>
(off / div) % mod
}
}
Let's say the shape is this, representing an array with rank 4 and 120 elements in total:
val sz = Vector(2, 3, 4, 5)
val num = sz.product // 120
A utility to print these index vectors for a range of linear offsets:
def printIndices(off: Int, len: Int): Unit =
(off until (off + len)).map(calcIndices(_, sz))
.map(_.mkString("[", ", ", "]")).foreach(println)
We can generate all those vectors:
printIndices(0, num)
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
[0, 1, 0, 0]
...
[1, 2, 1, 4]
[1, 2, 2, 0]
[1, 2, 2, 1]
[1, 2, 2, 2]
[1, 2, 2, 3]
[1, 2, 2, 4]
[1, 2, 3, 0]
[1, 2, 3, 1]
[1, 2, 3, 2]
[1, 2, 3, 3]
[1, 2, 3, 4]
Let's look at an example chunk that should be read,
the first six elements:
val off1 = 0
val len1 = 6
printIndices(off1, len1)
I will already partition the output by hand into hypercubes:
// first hypercube or read
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
// second hypercube or read
[0, 0, 1, 0]
So the task is to define a method
def partition(shape: Vector[Int], off: Int, len: Int): List[Vector[Range]]
which outputs the correct list and uses the smallest possible list size.
So for off1 and len1, we have the expected result:
val res1 = List(
Vector(0 to 0, 0 to 0, 0 to 0, 0 to 4),
Vector(0 to 0, 0 to 0, 1 to 1, 0 to 0)
)
assert(res1.map(_.map(_.size).product).sum == len1)
A second example, elements at indices 6 until 22, with manual partitioning giving three hypercubes or read commands:
val off2 = 6
val len2 = 16
printIndices(off2, len2)
// first hypercube or read
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
// second hypercube or read
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
// third hypercube or read
[0, 1, 0, 0]
[0, 1, 0, 1]
expected result:
val res2 = List(
Vector(0 to 0, 0 to 0, 1 to 1, 1 to 4),
Vector(0 to 0, 0 to 0, 2 to 3, 0 to 4),
Vector(0 to 0, 1 to 1, 0 to 0, 0 to 1)
)
assert(res2.map(_.map(_.size).product).sum == len2)
Note that for val off3 = 6; val len3 = 21, we would need four readings.
The idea of the following algorithm is as follows:
a point-of-interest (poi) is the left-most position
at which two index representations differ
(for example for [0, 0, 0, 1] and [0, 1, 0, 0] the poi is 1)
we recursively sub-divide the original (start, stop) linear index range
we use motions in two directions, first by keeping the start constant
and decreasing the stop through a special "ceil" operation on the start,
later by keeping the stop constant and increasing the start through
a special "floor" operation on the stop
for each sub range, we calculate the poi of the boundaries, and
we calculate "trunc" which is ceil or floor operation described above
if this trunc value is identical to its input, we add the entire region
and return
otherwise we recurse
the special "ceil" operation takes the previous start value and
increases the element at the poi index and zeroes the subsequent elements;
e.g. for [0, 0, 1, 1] and poi = 2, the ceil would be [0, 0, 2, 0]
the special "floor" operation takes the previous stop value and
zeroes the elements after the poi index;
e.g. for [0, 0, 1, 1], and poi = 2, the floor would be [0, 0, 1, 0]
Here is my implementation. First, a few utility functions:
def calcIndices(off: Int, shape: Vector[Int]): Vector[Int] = {
val modsDivs = (shape, shape.scanRight(1)(_ * _).tail, shape.indices).zipped
modsDivs.map { case (mod, div, idx) =>
val x = off / div
if (idx == 0) x else x % mod
}
}
def calcPOI(a: Vector[Int], b: Vector[Int], min: Int): Int = {
val res = (a.drop(min) zip b.drop(min)).indexWhere { case (ai,bi) => ai != bi }
if (res < 0) a.size else res + min
}
def zipToRange(a: Vector[Int], b: Vector[Int]): Vector[Range] =
(a, b).zipped.map { (ai, bi) =>
require (ai <= bi)
ai to bi
}
def calcOff(a: Vector[Int], shape: Vector[Int]): Int = {
val divs = shape.scanRight(1)(_ * _).tail
(a, divs).zipped.map(_ * _).sum
}
def indexTrunc(a: Vector[Int], poi: Int, inc: Boolean): Vector[Int] =
a.zipWithIndex.map { case (ai, i) =>
if (i < poi) ai
else if (i > poi) 0
else if (inc) ai + 1
else ai
}
Then the actual algorithm:
def partition(shape: Vector[Int], off: Int, len: Int): List[Vector[Range]] = {
val rankM = shape.size - 1
def loop(start: Int, stop: Int, poiMin: Int, dir: Boolean,
res0: List[Vector[Range]]): List[Vector[Range]] =
if (start == stop) res0 else {
val last = stop - 1
val s0 = calcIndices(start, shape)
val s1 = calcIndices(stop , shape)
val s1m = calcIndices(last , shape)
val poi = calcPOI(s0, s1m, poiMin)
val ti = if (dir) s0 else s1
val to = if (dir) s1 else s0
val st = if (poi >= rankM) to else indexTrunc(ti, poi, inc = dir)
val trunc = calcOff(st, shape)
val split = trunc != (if (dir) stop else start)
if (split) {
if (dir) {
val res1 = loop(start, trunc, poiMin = poi+1, dir = true , res0 = res0)
loop (trunc, stop , poiMin = 0 , dir = false, res0 = res1)
} else {
val s1tm = calcIndices(trunc - 1, shape)
val res1 = zipToRange(s0, s1tm) :: res0
loop (trunc, stop , poiMin = poi+1, dir = false, res0 = res1)
}
} else {
zipToRange(s0, s1m) :: res0
}
}
loop(off, off + len, poiMin = 0, dir = true, res0 = Nil).reverse
}
Examples:
val sz = Vector(2, 3, 4, 5)
partition(sz, 0, 6)
// result:
List(
Vector(0 to 0, 0 to 0, 0 to 0, 0 to 4), // first hypercube
Vector(0 to 0, 0 to 0, 1 to 1, 0 to 0) // second hypercube
)
partition(sz, 6, 21)
// result:
List(
Vector(0 to 0, 0 to 0, 1 to 1, 1 to 4), // first read
Vector(0 to 0, 0 to 0, 2 to 3, 0 to 4), // second read
Vector(0 to 0, 1 to 1, 0 to 0, 0 to 4), // third read
Vector(0 to 0, 1 to 1, 1 to 1, 0 to 1) // fourth read
)
The maximum number of reads, if I'm not mistaken, would be 2 * rank.
merge:
- &LEFT { x: 1, y: 1, r: 1 }
- &BIG { x: 2, y: 2, r: 2 }
- &SMALL { x: 3, y: 3, r: 3}
- # Override
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: big/left/small
I get the output:
{
merge:
[
{ x: 1, y: 1, r: 1 },
{ x: 2, y: 2, r: 2 },
{ x: 3, y: 3, r: 3 },
{ x: 1, y: 2, r: 2, label: 'big/left/small' }
]
}
But the results do not meet my expectation, the last one in the merge object I hope it be
{ x: 1, y: 3, r: 3, label: 'big/left/small' }.
How can I do with the YAML syntax ?
You cannot do this with YAML syntax, and your expectations are unfounded on multiple levels.
An anchored element (whether a sequence element or not) doesn't magically disappear when it is used in merge alias or any other alias nor on the basis of it being an anchor
A toplevel mapping key (merge) doesn't magically disappear because its value is a sequence scalar that contains an element with a merge indicator
The Merge Key Language-Independent Type documentation doesn't indicate such a deletion and neither does the YAML specification. The anchors (and aliases) are not normally preserved in the representation in the language you use for loading your YAML, as per the YAML specs. Therefore it is normally not possible to find the anchored elements and delete them after loading.
A generic solution would be to have top another toplevel key default key that "defines" the anchors and work only with the value associated with the merge key:
import ruamel.yaml
yaml_str = """\
default:
- &LEFT { x: 1, y: 1, r: 1 }
- &BIG { x: 2, y: 2, r: 2 }
- &SMALL { x: 3, y: 3, r: 3}
merge:
# Override
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: big/left/small
"""
data = ruamel.yaml.load(yaml_str)['merge']
print(data)
gives:
{'x': 1, 'r': 2, 'y': 2, 'label': 'big/left/small'}
(the order of the keys in your output is of course random)
I had an interview were I was asked a seemingly simple algorithm question: "Write an algorithm to return me all possible winning combinations for tic tac toe." I still can't figure out an efficient way to handle this. Is there a standard algorithm or common that should be applied to similar questions like this that I'm not aware of?
This is one of those problems that's actually simple enough for brute force and, while you could use combinatorics, graph theory, or many other complex tools to solve it, I'd actually be impressed by applicants that recognise the fact there's an easier way (at least for this problem).
There are only 39, or 19,683 possible combinations of placing x, o or <blank> in the grid, and not all of those are valid.
First, a valid game position is one where the difference between x and o counts is no more than one, since they have to alternate moves.
In addition, it's impossible to have a state where both sides have three in a row, so they can be discounted as well. If both have three in a row, then one of them would have won in the previous move.
There's actually another limitation in that it's impossible for one side to have won in two different ways without a common cell (again, they would have won in a previous move), meaning that:
XXX
OOO
XXX
cannot be achieved, while:
XXX
OOX
OOX
can be. But we can actually ignore that since there's no way to win two ways without a common cell without having already violated the "maximum difference of one" rule, since you need six cells for that, with the opponent only having three.
So I would simply use brute force and, for each position where the difference is zero or one between the counts, check the eight winning possibilities for both sides. Assuming only one of them has a win, that's a legal, winning game.
Below is a proof of concept in Python, but first the output of time when run on the process sending output to /dev/null to show how fast it is:
real 0m0.169s
user 0m0.109s
sys 0m0.030s
The code:
def won(c, n):
if c[0] == n and c[1] == n and c[2] == n: return 1
if c[3] == n and c[4] == n and c[5] == n: return 1
if c[6] == n and c[7] == n and c[8] == n: return 1
if c[0] == n and c[3] == n and c[6] == n: return 1
if c[1] == n and c[4] == n and c[7] == n: return 1
if c[2] == n and c[5] == n and c[8] == n: return 1
if c[0] == n and c[4] == n and c[8] == n: return 1
if c[2] == n and c[4] == n and c[6] == n: return 1
return 0
pc = [' ', 'x', 'o']
c = [0] * 9
for c[0] in range (3):
for c[1] in range (3):
for c[2] in range (3):
for c[3] in range (3):
for c[4] in range (3):
for c[5] in range (3):
for c[6] in range (3):
for c[7] in range (3):
for c[8] in range (3):
countx = sum([1 for x in c if x == 1])
county = sum([1 for x in c if x == 2])
if abs(countx-county) < 2:
if won(c,1) + won(c,2) == 1:
print " %s | %s | %s" % (pc[c[0]],pc[c[1]],pc[c[2]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[3]],pc[c[4]],pc[c[5]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[6]],pc[c[7]],pc[c[8]])
print
As one commenter has pointed out, there is one more restriction. The winner for a given board cannot have less cells than the loser since that means the loser just moved, despite the fact the winner had already won on the last move.
I won't change the code to take that into account but it would be a simple matter of checking who has the most cells (the last person that moved) and ensuring the winning line belonged to them.
Another way could be to start with each of the eight winning positions,
xxx ---
--- xxx
--- --- ... etc.,
and recursively fill in all legal combinations (start with inserting 2 o's, then add an x for each o ; avoid o winning positions):
xxx xxx xxx
oo- oox oox
--- o-- oox ... etc.,
Today I had an interview with Apple and I had the same question. I couldn't think well at that moment. Later one on, before going to a meeting I wrote the function for the combinations in 15 minutes, and when I came back from the meeting I wrote the validation function again in 15 minutes. I get nervous at interviews, Apple not trusts my resume, they only trust what they see in the interview, I don't blame them, many companies are the same, I just say that something in this hiring process doesn't look quite smart.
Anyways, here is my solution in Swift 4, there are 8 lines of code for the combinations function and 17 lines of code to check a valid board.
Cheers!!!
// Not used yet: 0
// Used with x : 1
// Used with 0 : 2
// 8 lines code to get the next combination
func increment ( _ list: inout [Int], _ base: Int ) -> Bool {
for digit in 0..<list.count {
list[digit] += 1
if list[digit] < base { return true }
list[digit] = 0
}
return false
}
let incrementTicTacToe = { increment(&$0, 3) }
let win0_ = [0,1,2] // [1,1,1,0,0,0,0,0,0]
let win1_ = [3,4,5] // [0,0,0,1,1,1,0,0,0]
let win2_ = [6,7,8] // [0,0,0,0,0,0,1,1,1]
let win_0 = [0,3,6] // [1,0,0,1,0,0,1,0,0]
let win_1 = [1,4,7] // [0,1,0,0,1,0,0,1,0]
let win_2 = [2,5,8] // [0,0,1,0,0,1,0,0,1]
let win00 = [0,4,8] // [1,0,0,0,1,0,0,0,1]
let win11 = [2,4,6] // [0,0,1,0,1,0,1,0,0]
let winList = [ win0_, win1_, win2_, win_0, win_1, win_2, win00, win11]
// 16 lines to check a valid board, wihtout countin lines of comment.
func winCombination (_ tictactoe: [Int]) -> Bool {
var count = 0
for win in winList {
if tictactoe[win[0]] == tictactoe[win[1]],
tictactoe[win[1]] == tictactoe[win[2]],
tictactoe[win[2]] != 0 {
// If the combination exist increment count by 1.
count += 1
}
if count == 2 {
return false
}
}
var indexes = Array(repeating:0, count:3)
for num in tictactoe { indexes[num] += 1 }
// '0' and 'X' must be used the same times or with a diference of one.
// Must one and only one valid combination
return abs(indexes[1] - indexes[2]) <= 1 && count == 1
}
// Test
var listToIncrement = Array(repeating:0, count:9)
var combinationsCount = 1
var winCount = 0
while incrementTicTacToe(&listToIncrement) {
if winCombination(listToIncrement) == true {
winCount += 1
}
combinationsCount += 1
}
print("There is \(combinationsCount) combinations including possible and impossible ones.")
print("There is \(winCount) combinations for wining positions.")
/*
There are 19683 combinations including possible and impossible ones.
There are 2032 combinations for winning positions.
*/
listToIncrement = Array(repeating:0, count:9)
var listOfIncremented = ""
for _ in 0..<1000 { // Win combinations for the first 1000 combinations
_ = incrementTicTacToe(&listToIncrement)
if winCombination(listToIncrement) == true {
listOfIncremented += ", \(listToIncrement)"
}
}
print("List of combinations: \(listOfIncremented)")
/*
List of combinations: , [2, 2, 2, 1, 1, 0, 0, 0, 0], [1, 1, 1, 2, 2, 0, 0, 0, 0],
[2, 2, 2, 1, 0, 1, 0, 0, 0], [2, 2, 2, 0, 1, 1, 0, 0, 0], [2, 2, 0, 1, 1, 1, 0, 0, 0],
[2, 0, 2, 1, 1, 1, 0, 0, 0], [0, 2, 2, 1, 1, 1, 0, 0, 0], [1, 1, 1, 2, 0, 2, 0, 0, 0],
[1, 1, 1, 0, 2, 2, 0, 0, 0], [1, 1, 0, 2, 2, 2, 0, 0, 0], [1, 0, 1, 2, 2, 2, 0, 0, 0],
[0, 1, 1, 2, 2, 2, 0, 0, 0], [1, 2, 2, 1, 0, 0, 1, 0, 0], [2, 2, 2, 1, 0, 0, 1, 0, 0],
[2, 2, 1, 0, 1, 0, 1, 0, 0], [2, 2, 2, 0, 1, 0, 1, 0, 0], [2, 2, 2, 1, 1, 0, 1, 0, 0],
[2, 0, 1, 2, 1, 0, 1, 0, 0], [0, 2, 1, 2, 1, 0, 1, 0, 0], [2, 2, 1, 2, 1, 0, 1, 0, 0],
[1, 2, 0, 1, 2, 0, 1, 0, 0], [1, 0, 2, 1, 2, 0, 1, 0, 0], [1, 2, 2, 1, 2, 0, 1, 0, 0],
[2, 2, 2, 0, 0, 1, 1, 0, 0]
*/
This is a java equivalent code sample
package testit;
public class TicTacToe {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 0 1 2
// 3 4 5
// 6 7 8
char[] pc = {' ' ,'o', 'x' };
char[] c = new char[9];
// initialize c
for (int i = 0; i < 9; i++)
c[i] = pc[0];
for (int i = 0; i < 3; i++) {
c[0] = pc[i];
for (int j = 0; j < 3; j++) {
c[1] = pc[j];
for (int k = 0; k < 3; k++) {
c[2] = pc[k];
for (int l = 0; l < 3; l++) {
c[3] = pc[l];
for (int m = 0; m < 3; m++) {
c[4] = pc[m];
for (int n = 0; n < 3; n++) {
c[5] = pc[n];
for (int o = 0; o < 3; o++) {
c[6] = pc[o];
for (int p = 0; p < 3; p++) {
c[7] = pc[p];
for (int q = 0; q < 3; q++) {
c[8] = pc[q];
int countx = 0;
int county = 0;
for(int r = 0 ; r<9 ; r++){
if(c[r] == 'x'){
countx = countx + 1;
}
else if(c[r] == 'o'){
county = county + 1;
}
}
if(Math.abs(countx - county) < 2){
if(won(c, pc[2])+won(c, pc[1]) == 1 ){
System.out.println(c[0] + " " + c[1] + " " + c[2]);
System.out.println(c[3] + " " + c[4] + " " + c[5]);
System.out.println(c[6] + " " + c[7] + " " + c[8]);
System.out.println("*******************************************");
}
}
}
}
}
}
}
}
}
}
}
}
public static int won(char[] c, char n) {
if ((c[0] == n) && (c[1] == n) && (c[2] == n))
return 1;
else if ((c[3] == n) && (c[4] == n) && (c[5] == n))
return 1;
else if ((c[6] == n) && (c[7] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[3] == n) && (c[6] == n))
return 1;
else if ((c[1] == n) && (c[4] == n) && (c[7] == n))
return 1;
else if ((c[2] == n) && (c[5] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[4] == n) && (c[8] == n))
return 1;
else if ((c[2] == n) && (c[4] == n) && (c[6] == n))
return 1;
else
return 0;
}
}
`
Below Solution generates all possible combinations using recursion
It has eliminated impossible combinations and returned 888 Combinations
Below is a working code Possible winning combinations of the TIC TAC TOE game
const players = ['X', 'O'];
let gameBoard = Array.from({ length: 9 });
const winningCombination = [
[ 0, 1, 2 ],
[ 3, 4, 5 ],
[ 6, 7, 8 ],
[ 0, 3, 6 ],
[ 1, 4, 7 ],
[ 2, 5, 8 ],
[ 0, 4, 8 ],
[ 2, 4, 6 ],
];
const isWinningCombination = (board)=> {
if((Math.abs(board.filter(a => a === players[0]).length -
board.filter(a => a === players[1]).length)) > 1) {
return false
}
let winningComb = 0;
players.forEach( player => {
winningCombination.forEach( combinations => {
if (combinations.every(combination => board[combination] === player )) {
winningComb++;
}
});
});
return winningComb === 1;
}
const getCombinations = (board) => {
let currentBoard = [...board];
const firstEmptySquare = board.indexOf(undefined)
if (firstEmptySquare === -1) {
return isWinningCombination(board) ? [board] : [];
} else {
return [...players, ''].reduce((prev, next) => {
currentBoard[firstEmptySquare] = next;
if(next !== '' && board.filter(a => a === next).length > (gameBoard.length / players.length)) {
return [...prev]
}
return [board, ...prev, ...getCombinations(currentBoard)]
}, [])
}
}
const startApp = () => {
let combination = getCombinations(gameBoard).filter(board =>
board.every(item => !(item === undefined)) && isWinningCombination(board)
)
printCombination(combination)
}
const printCombination = (combination)=> {
const ulElement = document.querySelector('.combinations');
combination.forEach(comb => {
let node = document.createElement("li");
let nodePre = document.createElement("pre");
let textnode = document.createTextNode(JSON.stringify(comb));
nodePre.appendChild(textnode);
node.appendChild(nodePre);
ulElement.appendChild(node);
})
}
startApp();
This discovers all possible combinations for tic tac toe (255,168) -- written in JavaScript using recursion. It is not optimized, but gets you what you need.
const [EMPTY, O, X] = [0, 4, 1]
let count = 0
let coordinate = [
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY
]
function reducer(arr, sumOne, sumTwo = null) {
let func = arr.reduce((sum, a) => sum + a, 0)
if((func === sumOne) || (func === sumTwo)) return true
}
function checkResult() {
let [a1, a2, a3, b1, b2, b3, c1, c2, c3] = coordinate
if(reducer([a1,a2,a3], 3, 12)) return true
if(reducer([a1,b2,c3], 3, 12)) return true
if(reducer([b1,b2,b3], 3, 12)) return true
if(reducer([c1,c2,c3], 3, 12)) return true
if(reducer([a3,b2,c1], 3, 12)) return true
if(reducer([a1,b1,c1], 3, 12)) return true
if(reducer([a2,b2,c2], 3, 12)) return true
if(reducer([a3,b3,c3], 3, 12)) return true
if(reducer([a1,a2,a3,b1,b2,b3,c1,c2,c3], 21)) return true
return false
}
function nextPiece() {
let [countX, countO] = [0, 0]
for(let i = 0; i < coordinate.length; i++) {
if(coordinate[i] === X) countX++
if(coordinate[i] === O) countO++
}
return countX === countO ? X : O
}
function countGames() {
if (checkResult()) {
count++
}else {
for (let i = 0; i < 9; i++) {
if (coordinate[i] === EMPTY) {
coordinate[i] = nextPiece()
countGames()
coordinate[i] = EMPTY
}
}
}
}
countGames()
console.log(count)
I separated out the checkResult returns in case you want to output various win conditions.
Could be solved with brute force but keep in mind the corner cases like player2 can't move when player1 has won and vice versa. Also remember Difference between moves of player1 and player can't be greater than 1 and less than 0.
I have written code for validating whether provided combination is valid or not, might soon post on github.
I am writing an application which subdivides an N-dimensional axis aligned bounding box into smaller N-dimensional bounding boxes, I need an algorithm which will do this.
For example:
in 1 dimension a "bounding box" is simply a length
e.g. { Min=0, Max=100 }
which would be subdivided into
{Min=0, Max=50} and {Min=50, Max=100}
in 2 dimensions a "bounding box" is a square
e.g. {Min=[0,0], Max=[100,100]}
would be divided into
{Min=[0,0], Max=[50,50]}
{Min=[0,50], Max=[50,100]}
{Min=[50,0], Max=[100,50]}
{Min=[50,50], Max=[100,100]}
And so on, all I need is a description of an algorithm for doing this, language doesn't particularly matter, since once I know how to do it I can translate it into the language of choice (C# in this case)
EDIT:: In response to questions in comments:
subdivisions must always be equal (as
in the examples)
boundaries are
floating points, so divisibility by two isn't a problem
Break it into two problems: iterating over the grid of "Min" points, and constructing a small box for a Min point.
For your second case, {[0,0], [100,100]}, deltaX=50 and deltaY=50. The grid is
[0, 0]
[0, 50]
[50, 0]
[50, 50]
and it is trivial to construct the second column from the first:
[ 0, 0] [ 50, 50]
[ 0, 50] [ 50, 100]
[50, 0] [100, 50]
[50, 50] [100, 100]
Here's a three-dimensional case {[0,0,0], [100,100,60]}, delta = [50, 50, 30]
[ 0, 0, 0] [ 50, 50, 30]
[ 0, 0, 30] [ 50, 50, 60]
[ 0, 50, 0] [ 50, 100, 30]
[ 0, 50, 30] [ 50, 100, 60]
[50, 0, 0] [100, 50, 30]
[50, 0, 30] [100, 50, 60]
[50, 50, 0] [100, 100, 30]
[50, 50, 30] [100, 100, 60]
A function that splits the box in all dimensions (in Python):
def halfboxes(box):
result = [[]]
for (a, b) in box:
result = [r + [(a, (a+b)/2)] for r in result] + \
[r + [((a+b)/2, b)] for r in result]
return result
h = halfboxes([(0,100), (20, 100)])
# Results in h =
# [[(0, 50), (20, 60)], [(50, 100), (20, 60)],
# [(0, 50), (60, 100)], [(50, 100), (60,100)]]
If this is's a good solution also depends your performance requirements. It makes a lot copies of arrays, which is not really efficient. But it might well be good enough for your use case.
Edit:
A more efficient version, that doesn't copy any arrays:
def halfboxes(box):
# total number of resulting arrays
resultscount = 2**len(box)
# allocate |resultscount| arrays
results = [[] for i in range(resultscount)]
spread = 1
for (a,b) in box:
low = (a, (a+b)/2)
high = ((a+b)/2, b)
for i in range(resultscount):
# "magic" to append the high/low parts to the correct array
if i % (spread*2) < spread:
results[i].append(low)
else:
results[i].append(high)
spread *= 2
return results
Here no arrays are copied and some calculations on the index are used to decide where the new boundaries should be added.
calculate the first box:
dimension n: Min[0, 0, 0, .., 0] -- Max[delta1/2, delta2/2, ..., deltan/2]
your big box will be subdivised to 2n small boxes -> calculate 2n transalations to apply to the 1st box (including a translation of [0, 0, 0, .., 0])
(of course the code below is not optimized-organized...)
using System;
using System.Collections.Generic;
namespace WindowsFormsApplication1
{
public class Class1
{
public static List<Box> GetSmallBoxes(Box bigBox)
{
int translationCoef;
List<Box> boxes = new List<Box>();
Box box;
for (int k = 0; k < Math.Pow(2, bigBox.Dimension); k++)
{
box = new Box(bigBox.Dimension);
for (int d = 0; d < bigBox.Dimension; d++)
{
translationCoef = ((int)(k / Math.Pow(2, bigBox.Dimension - d - 1)) % 2) == 0 ? 1 : 0;
box.Mins[d] = bigBox.Mins[d] + (bigBox.Deltas[d] / 2) * translationCoef;
box.Maxs[d] = bigBox.Mins[d] + (bigBox.Deltas[d] / 2) * (1 + translationCoef);
}
boxes.Add(box);
}
return boxes;
}
public static void Main()
{
Box bigBox = new Box(5);
bigBox.Mins = new int[] { 0, 10, 30, 20, 40 };
bigBox.Maxs = new int[] { 80, 50, 110, 40, 50 };
List<Box> smallBoxes = Class1.GetSmallBoxes(bigBox);
}
}
public class Box
{
public int Dimension;
public int[] Mins;
public int[] Maxs;
public Box(int dimension)
{
Dimension = dimension;
Mins = new int[dimension];
Maxs = new int[dimension];
}
public int[] Deltas
{
get
{
int[] deltas = new int[Dimension];
for (int i = 0; i < Dimension; i++)
{
deltas[i] = Maxs[i] - Mins[i];
}
return deltas;
}
}
public override string ToString()
{
string str;
str = "Min[";
foreach (int min in Mins)
{
str += min.ToString() + ", ";
}
str = str.Substring(0, str.Length - 2);
str += "] -- Max[";
foreach (int max in Maxs)
{
str += max.ToString() + ", ";
}
str = str.Substring(0, str.Length - 2);
return str;
}
}
}