Counting Robot Paths(python) - python-3.x

I don't know how to start on it.
Let's image a 5x5 square.
The robot starts on the upper left-hand corner and ens on the bottom right-hand corner.
We would like to have a robot travel from the start square at the upper left to the end square at the lower right.
At each square, the robot has only one of two moves:
It may go one square to the right or
It may go one square down.
Write a program to determine how many ways may this be done? In other words, how many paths are there from “start” to “end” with the moves at each step restricted as above?

This is represented by a recursive tree traversal. You can write a single recursive method to do this, something like:
traverse_moves, taking a board state (where the robot is, in this case) and returning an integer
Compute the next moves you can make - from 0 to 2 total. If 0, return the integer 1. If 1 or 2, create the board state for each valid move and call this method with the state you created, summing the return values of all of your calls and returning the sum you got.
e.g. for a 2x2 board, create the state where the robot is in the top left. This state has two possible moves, so it will call itself with robot bottom left and robot top right. Each of those states has one possible move and will call themself with robot bottom right. Both of these 'leaves' of the traversal tree will return 1, and these 1s will be returned and propagated and summed up the recursive to give you 2 as the correct answer.
Caller
| 2
TL calls itself with BL and TR
/ 1 \ 1
BL TR call themselves with BR, their only valid move
| 1 | 1
BR BR return 1s each for being leaf nodes, the 1s are returned and summed
EDIT: pseudocode
int traverseMoves(BoardState state)
{
List<BoardState> nextMoves = possibleMovesFrom(state);
if (nextMoves.Length == 0)
{
return 1;
}
int returnValue = 0;
foreach (BoardState move in nextMoves)
{
returnValue += traverseMoves(move);
}
return returnValue;
}

Related

"unique" crossover for genetic algorithm - TSP

I am creating a Genetic Algorithm to solve the Traveling Salesman Problem.
Currently, two 2D lists represent the two parents that need to be crossed:
path_1 = np.shuffle(np.arange(12).reshape(6, 2))
path_2 = np.arange(12).reshape(6,2)
Suppose each element in the list represents an (x, y) coordinate on a cartesian plane, and the 2D list represents the path that the "traveling salesman" must take (from index 0 to index -1).
Since the TSP requires that all points are included in a path, the resulting child of this crossover must have no duplicate points.
I have little idea as to how I can make such crossover and have the resulting child representative of both parents.
You need to use an ordered crossover operator, like OX1.
OX1 is a fairly simple permutation crossover.
Basically, a swath of consecutive alleles from parent 1 drops down,
and remaining values are placed in the child in the order which they
appear in parent 2.
I used to run TSP with these operators:
Crossover: Ordered Crossver (OX1).
Mutation: Reverse Sequence Mutation (RSM)
Selection: Roulette Wheel Selection
You can do something like this,
Choose half (or any random number between 0 to (length - 1)) coordinates from one parent using any approach, lets say where i % 2 == 0.
These can be positioned into the child using multiple approaches: either randomly, or all in the starting (or ending), or alternate position.
Now the remaining coordinated need to come from the 2nd parent for which you can traverse in the 2nd parent and if the coordinate is not chosen add it in the empty spaces.
For example,
I am choosing even positioned coordinated from parent 1 and putting it in even position indices in the child and then traversing in parent 2 to put the remaining coordinated in the odd position indices in the child.
def crossover(p1, p2, number_of_cities):
chk = {}
for i in range(number_of_cities):
chk[i] = 0
child = [-1] * number_of_cities
for x in range(len(p1)):
if x % 2 == 0:
child[x] = p1[x]
chk[p1[x]] = 1
y = 1
for x in range(len(p2)):
if chk[p2[x]] == 0:
child[y] = p2[x]
y += 2
return child
This approach preserves the order of cities visited from both parents.
Also since it is not symmetric p1 and p2 can be switched to give 2 children and the better (or both) can be chosen.

Explain this Towers of Hanoi solution (written in python)

I am trying to understand this particular solution of Towers of Hanoi problem. It is a recursive solution.
A = [5, 4, 3, 2, 1]
B = []
C = []
def move(n, source, target, auxiliary, frm):
print('n here : ', n)
if n > 0:
print('frm : ', frm, n)
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target, '1')
# move the nth disk from source to target
target.append(source.pop())
print('n:', n)
# Display our progress
print(source, target, auxiliary, '##############', sep = '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, target, source, '2')
# initiate call from source A to target C with auxiliary B
move(5, A, C, B, '0')
You can see it live here
https://repl.it/JUzY/0
After the first line of ######### gets printed, the value of n = 0, becomes 2. How does this happen? Am I missing out some concept of recursion ? Help me in understanding this.
You need to understand scope. You say "the value of n = 0, but suddenly it becomes 2". But there is more than one "n", because the scope of n is the method. Each time move gets called, that method call has its own instance of n with its own value.
At the bottom of the script, you call move(5, A, C, B, '0'). Inside that call, n == 5. The first thing that does is call move(n - 1, source, auxiliary, target, '1'). Inside that call, n == 4. But when it eventually returns, n in the original function call is still 5.
A good way to understand how recursion works, is to work through the program execution by hand, using sheets of paper. I'm going to use a Post-It note for function calls, and a notebook for output. You also have your "model", in the form of the lists A,B,C. Since elements move around, you could represent those elements with scraps of paper or scrabble tiles.
Start with the intial move(4, A, C, B, '0'). (I'm starting at 4 because the number of steps grows rapidly). Since we are calling a function, take a new post-it to represent the function call, and write on it:
n = 4
source = A
target = C
auxiliary = B
frm = '0'
Now work through the code of move doing what it says. Write any print output on this one.
When you reach the call to move(n - 1, source, auxiliary, target, '1'), make a note on the post-it of the line number you're at, then get a new post-it, and write the values that are going into it:
n = 4 (because 4 - 1)
source = A
target = B (caller's auxiliary)
auxiliary = C (caller's target)
frm = '1'
Place this on top of the previous post-it. You are not allowed to look at the covered up post-it until it's revealed again. Keep doing this, and you'll end up with a stack of five post-its.
The fifth post-it has n = 0. Now when you step through its code, because it starts with if n > 0:, the call returns immediately. This function call is over, so tear up that one post-it and throw it away. The values of n etc. in that post-it have no effect on the other post-its.
You made a note of the line-number you were at, so carry on executing the code by hand from that line. You will produce some output, then call move again -- use post-its for this move too, again covering up the current post-it and growing your stack until you make a call with n == 0 and can remove an item from the stack.
If you keep doing this, you'll see the stack grow and shrink several times, you'll reach the original post-it once, do its print, then grow the stack again, then reach the original post-it again, and finally finish.
The stack of post-it notes is an exact model of the execution stack of a program's runtime. When you see stack traces (even of non-recursive programs), this is what they represent. Each post-it is a stack frame, and local variables have a scope that is restricted to that stack frame.
The recursive solution to Hanoi can be expressed in English as:
To move a stack of size zero, do nothing
To move a bigger stack from src to destination via auxiliary:
First move n-1 discs from src to auxiliary (to get them out of the way)
do that the same way we are doing this.
Then move the revealed disc to destination
Then move the n-1 discs from auxiliary to destination, using src as a spare peg
do that the same way we are doing this

Counting minimum number of swaps to group characters in string

I'm trying to solve a pretty complex problem with strings:
Given is a string with up to 100000 characters, consisting of only two different characters 'L' and 'R'. A sequence 'RL' is considered "bad", and such occurrences have to be reduced by applying swaps.
However, the string is to be considered circular, so even the string 'LLLRRR' has an 'RL' sequence formed by the last 'R' and the first 'L'.
Swaps of two consecutive elements can be made. So we can swap only elements that are on positions i and i+1, or on position 0 and n-1, if n is the length of the string (the string is 0-indexed).
The goal is to find the minimum number of swaps needed to leave only one bad connection in the string.
Example
For the string 'RLLRRL' the problem can be solved with exactly one swap: swap the first and the last characters (since the string is circular). The string will thus become 'LLLRRR' with one bad connection.
What I tried
My idea is to use dynamical programming, and to calculate for any given 'L' how many swaps are needed to put all other 'L's left to that 'L', and, alternatively, to the right of this 'L'. For any 'R' I calculate the same.
This algorithm works in O(N) time, but it doesn't give the correct result.
It doesn't work when I have to swap the first and the last elements. What should I add to my algorithm to make it work also for those swaps?
The problem can be solved in linear time.
Some observations and definitions:
The target of having only one bad connection is another way of saying that the L letters should all be grouped together, as well as the R letters (in a circular string)
Let a group denote a series of subsequent letters of the same kind that cannot be made larger (because of surrounding letters that differ). By combining individual swaps you can "move" a group with one or more "steps". An example -- I will write . instead of L so it is more easily readable:
RRR...RR....
There are 4 groups here: RRR, ..., RR and ..... Suppose you want to join the group of two "R" with the left-sided "R" group in the above string. Then you could "move" that middle group with 3 steps to the left by performing 6 swaps:
RRR...RR....
RRR..R.R....
RRR..RR.....
RRR.R.R.....
RRR.RR......
RRRR.R......
RRRRR.......
These 6 swaps are what constitutes one group move. The cost of the move is 6, and is the product of the size of the group (2) and the distance it travels (3). Note that this move is exactly the same as when we would have moved the group with three "L" characters (cf. the dots) to the right.
I will use the word "move" in this meaning.
There is always a solution that can be expressed as a series of group moves, where each group move reduces the number of groups with two, i.e. with each such move, two R groups are merged into one, and consequently also two L groups are merged. In other words, there is always a solution where none of the groups has to split with one part of it moving to the left and another to the right. I will not give a proof of this claim here.
There is always a solution that has one group that will not move at all: all other groups of the same letter will move towards it. As a consequence there is also a group of the opposite letter that will not move, somewhere at the other end of the circle. Again, I will not prove this here.
The problem is then equivalent to minimizing the total cost (swaps) of the moves of the groups that represent one of the two letters (so half of all the groups). The other half of the groups move at the same time as was shown in the above example.
Algorithm
An algorithm could go like this:
Create an array of integers, where each value represents the size of a group. The array would list the groups in the order they appear. This would take the circular property into account, so that the first group (with index 0) would also account for the letter(s) at the very end of the string that are the same as the first letter(s). So at even indices you would have groups that represent counts of one particular letter, and at odd indices there would be counts of the other letter. It does not really matter which of the two letters they represent. The array of groups will always have an even number of entries. This array is all we need to solve the problem.
Pick the first group (index 0), and assume it will not move. Call it the "middle group". Determine which is the group of the opposite colour (with odd index) that will not have to move either. Call this other group the "split group". This split group will split the remaining odd groups into two sections, where the sum of their values (counts) is each less or equal the total of both sums. This represents the fact that it is cheaper for even groups to move in one direction than in the other in order to merge with the group at index 0.
Now determine the cost (number of swaps) for moving all even groups towards the middle group.
This may or may not be a solution, since the choice of the middle group was arbitrary.
The above would have to be repeated for the scenarios where any of the other even groups was taken as middle group.
Now the essence of the algorithm is to avoid redoing the whole operation when taking another group as the middle group. It turns out it is possible to take the next even group as middle group (at index 2) and adjust the previously calculated cost in constant time (on average) to arrive at the cost for this choice of the middle group. For this one has to keep a few parameters in memory: the cost for performing the moves in the left direction, and the cost for performing the moves in the right direction. Also the sum of even-group sizes needs to be maintained for each of both directions. And finally the sum of the odd-group sizes needs to be maintained as well for both directions. Each of these parameters can be adjusted when taking the next even group as middle group. Often the corresponding split group has to be re-identified as well, but also that can happen on average in constant time.
Without going to deep into this, here is a working implementation in simple JavaScript:
Code
function minimumSwaps(s) {
var groups, start, n, i, minCost, halfSpace, splitAt, space,
cost, costLeft, costRight, distLeft, distRight, itemsLeft, itemsRight;
// 1. Get group sizes
groups = [];
start = 0;
for (i = 1; i < s.length; i++) {
if (s[i] != s[start]) {
groups.push(i - start);
start = i;
}
}
// ... exit when the number of groups is already optimal
if (groups.length <= 2) return 0; // zero swaps
// ... the number of groups should be even (because of circle)
if (groups.length % 2 == 1) { // last character not same as first
groups.push(s.length - start);
} else { // Ends are connected: add to the length of first group
groups[0] += s.length - start;
}
n = groups.length;
// 2. Get the parameters of the scenario where group 0 is the middle:
// i.e. the members of group 0 do not move in that case.
// Get sum of odd groups, which we consider as "space", while even
// groups are considered items to be moved.
halfSpace = 0;
for (i = 1; i < n; i+=2) {
halfSpace += groups[i];
}
halfSpace /= 2;
// Get split-point between what is "left" from the "middle"
// and what is "right" from it:
space = 0;
for (i = 1; space < halfSpace; i+=2) {
space += groups[i];
}
splitAt = i-2;
// Get sum of items, and cost, to the right of group 0
itemsRight = distRight = costRight = 0;
for (i = 2; i < splitAt; i+=2) {
distRight += groups[i-1];
itemsRight += groups[i];
costRight += groups[i] * distRight;
}
// Get sum of items, and cost, to the left of group 0
itemsLeft = distLeft = costLeft = 0;
for (i = n-2; i > splitAt; i-=2) {
distLeft += groups[i+1];
itemsLeft += groups[i];
costLeft += groups[i] * distLeft;
}
cost = costLeft + costRight;
minCost = cost;
// 3. Translate the cost parameters by incremental changes for
// where the mid-point is set to the next even group
for (i = 2; i < n; i += 2) {
distLeft += groups[i-1];
itemsLeft += groups[i-2];
costLeft += itemsLeft * groups[i-1];
costRight -= itemsRight * groups[i-1];
itemsRight -= groups[i];
distRight -= groups[i-1];
// See if we need to change the split point. Items that get
// at the different side of the split point represent items
// that have a shorter route via the other half of the circle.
while (distLeft >= halfSpace) {
costLeft -= groups[(splitAt+1)%n] * distLeft;
distLeft -= groups[(splitAt+2)%n];
itemsLeft -= groups[(splitAt+1)%n];
itemsRight += groups[(splitAt+1)%n];
distRight += groups[splitAt];
costRight += groups[(splitAt+1)%n] * distRight;
splitAt = (splitAt+2)%n;
}
cost = costLeft + costRight;
if (cost < minCost) minCost = cost;
}
return minCost;
}
function validate(s) {
return new Set(s).size <= 2; // maximum 2 different letters used
}
// I/O
inp.oninput = function () {
var s, result, start;
s = inp.value;
start = performance.now(); // get timing
if (validate(s)) {
result = minimumSwaps(s); // apply algorithm
} else {
result = 'Please use only 2 different characters';
}
outp.textContent = result;
ms.textContent = Math.round(performance.now() - start);
}
rnd.onclick = function () {
inp.value = Array.from(Array(100000), _ =>
Math.random() < 0.5 ? "L" : "R").join('');
if (inp.value.length != 100000) alert('Your browser truncated the input!');
inp.oninput(); // trigger input handler
}
inp.oninput(); // trigger input handler
input { width: 100% }
<p>
<b>Enter LR series:</b>
<input id="inp" value="RLLRRL"><br>
<button id="rnd">Produce random of size 100000</button>
</p><p>
<b>Number of swaps: </b><span id="outp"></span><br>
<b>Time used: </b><span id="ms"></span>ms
</p>
Time Complexity
The preprocessing (creating the groups array, etc..), and calculation of the cost for when the first group is the middle group, all consist of non-nested loops with at most n iterations, so this part is O(n).
The calculation of the cost for when the middle group is any of the other even groups consists of a loop (for the choosing the middle group), and another inner loop for adjusting the choice of the split group. Even though this inner loop may iterate multiple times for one iteration of the outer loop, in total this inner loop will not have more iterations than n, so the total execution time of this outer loop is still O(n).
Therefore the time complexity is O(n).
Note that the result for a string of 100 000 characters is calculated in a fraction of a second (see the number of milliseconds displayed by the above snippet).
The task is to re-order the items in a circular list like this:
LRLLLRLLLRRLLRLLLRRLRLLLRLLRLRLRLRRLLLRRRLRLLRLLRL
so that we get a list like this:
RRRRRRLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRR
or this:
LLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRLLLLL
where the two types of items are grouped together, but the exact position of these two groups is not important.
The first task is to count the number of items in each group, so we iterate over the list once, and for the example above the result would be:
#L = 30
#R = 20
Then, the simple brute-force solution would be to consider every position in the list as the start of the L-zone, starting with position 0, iterate over the whole list and count how many steps each item is away from the border of the zone where it should be:
LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRR <- desired output
LRLLLRLLLRRLLRLLLRRLRLLLRLLRLRLRLRRLLLRRRLRLLRLLRL <- input
< < << < >> > > > >< < <<< > >> >> > <- direction to move
We would then consider the L-zone to start at position 1, and do the whole calculation again:
RLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRR <- desired output
LRLLLRLLLRRLLRLLLRRLRLLLRLLRLRLRLRRLLLRRRLRLLRLLRL <- input
<< < << < >> > > > > < <<< > >> >> > <- direction to move
After calculating the total number of steps for every position of the L-zone, we would know which position requires the least number of steps. This is of course a method with N2 complexity.
If we could calculate the number of required steps with the L-zone at position X, based on the calculation for the L-zone at position X-1 (without iterating over the whole list again), this could bring the complexity down to N.
To do so, we'd need to keep track of the number of wrong items in each half of each zone, and the total number of steps for the wrong items in each of these four half-zones:
LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRR <- desired output
<<<<<<<<<<<<<<<>>>>>>>>>>>>>>><<<<<<<<<<>>>>>>>>>> <- half-zones
LRLLLRLLLRRLLRLLLRRLRLLLRLLRLRLRLRRLLLRRLRRLLRLLRL <- input
< < << < >> > > > >< < <<< > >> >> > <- direction to move
5 6 5 6 <- wrong items
43 45 25 31 <- required steps
When we move right to the next position, the total number of steps in left-moving zones will decrease by the number of wrong items in that zone, and the total number of steps in right-moving zones will increase by the number of wrong items in that zone (because every item is now one step closer/further from the edge of the zone.
5 6 5 6 <- wrong items
38 51 20 37 <- required steps
However, we need to check the four border-points to see whether any wrong items have moved from one half-zone to another, and adjust the item and step count accordingly.
In the example, the L that was the first item of the L-zone has now become the last item in the R-zone, so we increment the R> half-zone's item and step count to 7 and 38.
Also, the L that was the first item in the R-zone, has become the last item of the L zone, so we decrement the item count for the R< half-zone to 4.
Also, the L in the middle of the R-zone has moved from the R> to the R< half-zone, so we decrement and increment the item counts for R> and R< to 6 and 5, and decrease and increase the step counts with 10 (the length of the R> and R< half-zones) to 28 and 30.
RLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRR <- desired output
><<<<<<<<<<<<<<<>>>>>>>>>>>>>>><<<<<<<<<<>>>>>>>>> <- half-zones
LRLLLRLLLRRLLRLLLRRLRLLLRLLRLRLRLRRLLLRRLRRLLRLLRL <- input
>< < << < >> > > > > < <<< < >> >> > <- direction to move
5 6 5 6 <- wrong items
38 51 30 28 <- required steps
So the total number of required steps when the L-zone starts at position 0 was 144, and we have calculated that when the L-zone starts at position 1, the total is now 147, by looking at what happens at four positions in the list, instead of having to iterate over the whole list again.
UPDATE
While thinking of how to implement this, I realised that the number of wrong items moving right in a zone must be the same as the number of wrong items moving left in the other zone; otherwise the border between the zones ends up in the wrong place. This means that the L and R zones aren't split into two half-zones of equal length, and the "mid" point in a zone moves according to how many wrong items are to the left and right of it. I still think it's possible to turn this into working code with O(N) efficiency, but it's probably not as straightforward as I first described it.
O(n) time solution:
L L R L L R R R L L R R L R
Number of R's to the next group of L's to the left:
1 1 1 1 3 3 2
NumRsToLeft: [1, 1, 3, 2]
Number of swaps needed, where 0 indicates the static L group, and | represents
the point to the right of which L's move right, wrapping only when not at the end
(enough L's must move to their right to replace any R's left of the static group):
2*0 + 2*1 + 2*(3+1) + 1*(2+3+1) |
2*1 + 2*0 + 2*3 | + 1*(1+1)
There are not enough L's to place the static group in the third or fourth position.
Variables: 0 1 4 6 |
1 0 3 | 2
Function: 2*v_1 + 2*v_2 + 2*v_3 + 1*v_4
Coefficients (group sizes): [2, 2, 2, 1]
Change in the total swaps needed when moving the static L group from i to (i+1):
Subtract: PSum(CoefficientsToBeGoingLeft) * NumRsToLeft[i+1]
Subtract: c_j * PSum(NumRsToLeft[i+1...j]) for c_j <- CoefficientsNoLongerGoingLeft
Add: (PSum(CoefficientsAlreadyGoingRight) + Coefficients[i]) * NumRsToLeft[i+1]
Add: c_j * PSum(NumRsToLeft[j+1...i+1]) for c_j <- NewCoefficientsGoingRight
(PSum can be calculated in O(1) time with prefix sums; and the count of coefficients
converting from a left move to a right move throughout the whole calculation is not
more than n. This outline does not include the potential splitting of the last new
group converting from left move to right move.)

In toroidal space, how to calculate which direction is the shortest between two points

Basically I am creating an algorithm that plays the game of Snake independently.
I have managed to make it work out which direction to head to within the square of the game, but the version of Snake that I made has toroidal space, namely that when the snake's head goes off one side of the square, it reappears on the opposite side.
On that basis, it is not important for me to calculate what the distance between two points (namely the snake's head and the food block), but rather what direction the food is in relation to the head.
I.e. on a grid of 50 pixels square, when the head is at [row 49, column 49] and the food is at [1,1], I don't want the snake to have to travel 48 squares up and to the left. I'd rather it just travel 2 squares down and 2 right.
What formula would I then use to calculate which direction will result in the shortest travel time between the head and the food?
PS please let me know if I should post this in another Stack Exchange site and I will do so.
It's probably easier to consider one dimensional case. Basically a circular buffer, see pic:
You have the player "P" at position 3 and your "Goal" at position 9, and total play space Width = 10. The direction of shortest motion towards goal could then be given by:
if(P.coord < Goal.coord)
{
int dist1 = Goal.coord - P.coord;
int dist2 = (Width - Goal.coord) + P.coord;
if(dist1 < dist2)
{
//move forwards
}
else
{
//move backwards
}
}
else
{
int dist1 = P.coord - Goal.coord;
int dist2 = (Width - P.coord) + Goal.coord;
if(dist1 < dist2)
{
//move backwards
}
else
{
//move forwards
}
}
Similarly handle the 'else' part if p>coord > Goal.coord. Now extend it to 2 dimensions.

How to calculate area of intersection of an arbitrary triangle with a square?

So, I've been struggling with a frankly now infuriating problem all day today.
Given a set of verticies of a triangle on a plane (just 3 points, 6 free parameters), I need to calculate the area of intersection of this triangle with the unit square defined by {0,0} and {1,1}. (I choose this because any square in 2D can be transformed to this, and the same transformation can move the 3 vertices).
So, now the problem is simplified down to only 6 parameters, 3 points... which I think is short enough that I'd be willing to code up the full solution / find the full solution.
( I would like this to run on a GPU for literally more than 2 million triangles every <0.5 seconds, if possible. as for the need for simplification / no data structures / libraries)
In terms of my attempt at the solution, I've... got a list of ways I've come up with, none of which seem fast or ... specific to the nice case (too general).
Option 1: Find the enclosed polygon, it can be anything from a triangle up to a 6-gon. Do this by use of some intersection of convex polygon in O(n) time algorithms that I found. Then I would sort these intersection points (new vertices, up to 7 of them O(n log n) ), in either CW or CCw order, so that I can run a simple area algorithm on the points (based on Greens function) (O(n) again). This is the fastest i can come with for an arbitrary convex n-gon intersecting with another m-gon. However... my problem is definitely not that complex, its a special case, so it should have a better solution...
Option 2:
Since I know its a triangle and unit square, i can simply find the list of intersection points in a more brute force way (rather than using some algorithm that is ... frankly a little frustrating to implement, as listed above)
There are only 19 points to check. 4 points are corners of square inside of triangle. 3 points are triangle inside square. And then for each line of the triangle, each will intersect 4 lines from the square (eg. y=0, y=1, x=0, x=1 lines). that is another 12 points. so, 12+3+4 = 19 points to check.
Once I have the, at most 6, at fewest 3, points that do this intersection, i can then follow up with one of two methods that I can think of.
2a: Sort them by increasing x value, and simply decompose the shape into its sub triangle / 4-gon shapes, each with an easy formula based on the limiting top and bottom lines. sum up the areas.
or 2b: Again sort the intersection points in some cyclic way, and then calculate the area based on greens function.
Unfortunately, this still ends up being just as complex as far as I can tell. I can start breaking up all the cases a little more, for finding the intersection points, since i know its just 0s and 1s for the square, which makes the math drop out some terms.. but it's not necessarily simple.
Option 3: Start separating the problem based on various conditions. Eg. 0, 1, 2, or 3 points of triangle inside square. And then for each case, run through all possible number of intersections, and then for each of those cases of polygon shapes, write down the area solution uniquely.
Option 4: some formula with heaviside step functions. This is the one I want the most probably, I suspect it'll be a little... big, but maybe I'm optimistic that it is possible, and that it would be the fastest computationally run time once I have the formula.
--- Overall, I know that it can be solved using some high level library (clipper for instance). I also realize that writing general solutions isn't so hard when using data structures of various kinds (linked list, followed by sorting it). And all those cases would be okay, if I just needed to do this a few times. But, since I need to run it as an image processing step, on the order of >9 * 1024*1024 times per image, and I'm taking images at .. lets say 1 fps (technically I will want to push this speed up as fast as possible, but lower bound is 1 second to calculate 9 million of these triangle intersection area problems). This might not be possible on a CPU, which is fine, I'll probably end up implementing it in Cuda anyways, but I do want to push the limit of speed on this problem.
Edit: So, I ended up going with Option 2b. Since there are only 19 intersections possible, of which at most 6 will define the shape, I first find those 3 to 6 verticies. Then i sort them in a cyclic (CCW) order. And then I find the area by calculating the area of that polygon.
Here is my test code I wrote to do that (it's for Igor, but should be readable as pseudocode) Unfortunately it's a little long winded, but.. I think other than my crappy sorting algorithm (shouldn't be more than 20 swaps though, so not so much overhead for writing better sorting)... other than that sorting, I don't think I can make it any faster. Though, I am open to any suggestions or oversights I might have had in chosing this option.
function calculateAreaUnitSquare(xPos, yPos)
wave xPos
wave yPos
// First, make array of destination. Only 7 possible results at most for this geometry.
Make/o/N=(7) outputVertexX = NaN
Make/o/N=(7) outputVertexY = NaN
variable pointsfound = 0
// Check 4 corners of square
// Do this by checking each corner against the parameterized plane described by basis vectors p2-p0 and p1-p0.
// (eg. project onto point - p0 onto p2-p0 and onto p1-p0. Using appropriate parameterization scaling (not unit).
// Once we have the parameterizations, then it's possible to check if it is inside the triangle, by checking that u and v are bounded by u>0, v>0 1-u-v > 0
variable denom = yPos[0]*xPos[1]-xPos[0]*yPos[1]-yPos[0]*xPos[2]+yPos[1]*xPos[2]+xPos[0]*yPos[2]-xPos[1]*yPos[2]
//variable u00 = yPos[0]*xPos[1]-xPos[0]*yPos[1]-yPos[0]*Xx+yPos[1]*Xx+xPos[0]*Yx-xPos[1]*Yx
//variable v00 = -yPos[2]*Xx+yPos[0]*(Xx-xPos[2])+xPos[0]*(yPos[2]-Yx)+yPos[2]*Yx
variable u00 = (yPos[0]*xPos[1]-xPos[0]*yPos[1])/denom
variable v00 = (yPos[0]*(-xPos[2])+xPos[0]*(yPos[2]))/denom
variable u01 =(yPos[0]*xPos[1]-xPos[0]*yPos[1]+xPos[0]-xPos[1])/denom
variable v01 =(yPos[0]*(-xPos[2])+xPos[0]*(yPos[2]-1)+xPos[2])/denom
variable u11 = (yPos[0]*xPos[1]-xPos[0]*yPos[1]-yPos[0]+yPos[1]+xPos[0]-xPos[1])/denom
variable v11 = (-yPos[2]+yPos[0]*(1-xPos[2])+xPos[0]*(yPos[2]-1)+xPos[2])/denom
variable u10 = (yPos[0]*xPos[1]-xPos[0]*yPos[1]-yPos[0]+yPos[1])/denom
variable v10 = (-yPos[2]+yPos[0]*(1-xPos[2])+xPos[0]*(yPos[2]))/denom
if(u00 >= 0 && v00 >=0 && (1-u00-v00) >=0)
outputVertexX[pointsfound] = 0
outputVertexY[pointsfound] = 0
pointsfound+=1
endif
if(u01 >= 0 && v01 >=0 && (1-u01-v01) >=0)
outputVertexX[pointsfound] = 0
outputVertexY[pointsfound] = 1
pointsfound+=1
endif
if(u10 >= 0 && v10 >=0 && (1-u10-v10) >=0)
outputVertexX[pointsfound] = 1
outputVertexY[pointsfound] = 0
pointsfound+=1
endif
if(u11 >= 0 && v11 >=0 && (1-u11-v11) >=0)
outputVertexX[pointsfound] = 1
outputVertexY[pointsfound] = 1
pointsfound+=1
endif
// Check 3 points for triangle. This is easy, just see if its bounded in the unit square. if it is, add it.
variable i = 0
for(i=0; i<3; i+=1)
if(xPos[i] >= 0 && xPos[i] <= 1 )
if(yPos[i] >=0 && yPos[i] <=1)
if(!((xPos[i] == 0 || xPos[i] == 1) && (yPos[i] == 0 || yPos[i] == 1) ))
outputVertexX[pointsfound] = xPos[i]
outputVertexY[pointsfound] = yPos[i]
pointsfound+=1
endif
endif
endif
endfor
// Check intersections.
// Procedure is: loop over 3 lines of triangle.
// For each line
// Check if vertical
// If not vertical, find y intercept with x=0 and x=1 lines.
// if y intercept is between 0 and 1, then add the point
// Check if horizontal
// if not horizontal, find x intercept with y=0 and y=1 lines
// if x intercept is between 0 and 1, then add the point
for(i=0; i<3; i+=1)
variable iN = mod(i+1,3)
if(xPos[i] != xPos[iN])
variable tx0 = xPos[i]/(xPos[i] - xPos[iN])
variable tx1 = (xPos[i]-1)/(xPos[i] - xPos[iN])
if(tx0 >0 && tx0 < 1)
variable yInt = (yPos[iN]-yPos[i])*tx0+yPos[i]
if(yInt > 0 && yInt <1)
outputVertexX[pointsfound] = 0
outputVertexY[pointsfound] = yInt
pointsfound+=1
endif
endif
if(tx1 >0 && tx1 < 1)
yInt = (yPos[iN]-yPos[i])*tx1+yPos[i]
if(yInt > 0 && yInt <1)
outputVertexX[pointsfound] = 1
outputVertexY[pointsfound] = yInt
pointsfound+=1
endif
endif
endif
if(yPos[i] != yPos[iN])
variable ty0 = yPos[i]/(yPos[i] - yPos[iN])
variable ty1 = (yPos[i]-1)/(yPos[i] - yPos[iN])
if(ty0 >0 && ty0 < 1)
variable xInt = (xPos[iN]-xPos[i])*ty0+xPos[i]
if(xInt > 0 && xInt <1)
outputVertexX[pointsfound] = xInt
outputVertexY[pointsfound] = 0
pointsfound+=1
endif
endif
if(ty1 >0 && ty1 < 1)
xInt = (xPos[iN]-xPos[i])*ty1+xPos[i]
if(xInt > 0 && xInt <1)
outputVertexX[pointsfound] = xInt
outputVertexY[pointsfound] = 1
pointsfound+=1
endif
endif
endif
endfor
// Now we have all 6 verticies that we need. Next step: find the lowest y point of the verticies
// if there are multiple with same low y point, find lowest X of these.
// swap this vertex to be first vertex.
variable lowY = 1
variable lowX = 1
variable m = 0;
for (i=0; i<pointsfound ; i+=1)
if (outputVertexY[i] < lowY)
m=i
lowY = outputVertexY[i]
lowX = outputVertexX[i]
elseif(outputVertexY[i] == lowY)
if(outputVertexX[i] < lowX)
m=i
lowY = outputVertexY[i]
lowX = outputVertexX[i]
endif
endif
endfor
outputVertexX[m] = outputVertexX[0]
outputVertexY[m] = outputVertexY[0]
outputVertexX[0] = lowX
outputVertexY[0] = lowY
// now we have the bottom left corner point, (bottom prefered).
// calculate the cos(theta) of unit x hat vector to the other verticies
make/o/N=(pointsfound) angles = (p!=0)?( (outputVertexX[p]-lowX) / sqrt( (outputVertexX[p]-lowX)^2+(outputVertexY[p]-lowY)^2) ) : 0
// Now sort the remaining verticies based on this angle offset. This will orient the points for a convex polygon in its maximal size / ccw orientation
// (This sort is crappy, but there will be in theory, at most 25 swaps. Which in the grand sceme of operations, isn't so bad.
variable j
for(i=1; i<pointsfound; i+=1)
for(j=i+1; j<pointsfound; j+=1)
if( angles[j] > angles[i] )
variable tempX = outputVertexX[j]
variable tempY = outputVertexY[j]
outputVertexX[j] = outputVertexX[i]
outputVertexY[j] =outputVertexY[i]
outputVertexX[i] = tempX
outputVertexY[i] = tempY
variable tempA = angles[j]
angles[j] = angles[i]
angles[i] = tempA
endif
endfor
endfor
// Now the list is ordered!
// now calculate the area given a list of CCW oriented points on a convex polygon.
// has a simple and easy math formula : http://www.mathwords.com/a/area_convex_polygon.htm
variable totA = 0
for(i = 0; i<pointsfound; i+=1)
totA += outputVertexX[i]*outputVertexY[mod(i+1,pointsfound)] - outputVertexY[i]*outputVertexX[mod(i+1,pointsfound)]
endfor
totA /= 2
return totA
end
I think the Cohen-Sutherland line-clipping algorithm is your friend here.
First off check the bounding box of the triangle against the square to catch the trivial cases (triangle inside square, triangle outside square).
Next check for the case where the square lies completely within the triangle.
Next consider your triangle vertices A, B and C in clockwise order. Clip the line segments AB, BC and CA against the square. They will either be altered such that they lie within the square or are found to lie outside, in which case they can be ignored.
You now have an ordered list of up to three line segments that define the some of the edges intersection polygon. It is easy to work out how to traverse from one edge to the next to find the other edges of the intersection polygon. Consider the endpoint of one line segment (e) against the start of the next (s)
If e is coincident with s, as would be the case when a triangle vertex lies within the square, then no traversal is required.
If e and s differ, then we need to traverse clockwise around the boundary of the square.
Note that this traversal will be in clockwise order, so there is no need to compute the vertices of the intersection shape, sort them into order and then compute the area. The area can be computed as you go without having to store the vertices.
Consider the following examples:
In the first case:
We clip the lines AB, BC and CA against the square, producing the line segments ab>ba and ca>ac
ab>ba forms the first edge of the intersection polygon
To traverse from ba to ca: ba lies on y=1, while ca does not, so the next edge is ca>(1,1)
(1,1) and ca both lie on x=1, so the next edge is (1,1)>ca
The next edge is a line segment we already have, ca>ac
ac and ab are coincident, so no traversal is needed (you might be as well just computing the area for a degenerate edge and avoiding the branch in these cases)
In the second case, clipping the triangle edges against the square gives us ab>ba, bc>cb and ca>ac. Traversal between these segments is trivial as the start and end points lie on the same square edges.
In the third case the traversal from ba to ca goes through two square vertices, but it is still a simple matter of comparing the square edges on which they lie:
ba lies on y=1, ca does not, so next vertex is (1,1)
(1,1) lies on x=1, ca does not, so next vertex is (1,0)
(1,0) lies on y=0, as does ca, so next vertex is ca.
Given the large number of triangles I would recommend scanline algorithm: sort all the points 1st by X and 2nd by Y, then proceed in X direction with a "scan line" that keeps a heap of Y-sorted intersections of all lines with that line. This approach has been widely used for Boolean operations on large collections of polygons: operations such as AND, OR, XOR, INSIDE, OUTSIDE, etc. all take O(n*log(n)).
It should be fairly straightforward to augment Boolean AND operation, implemented with the scanline algorithm to find the areas you need. The complexity will remain O(n*log(n)) on the number of triangles. The algorithm would also apply to intersections with arbitrary collections of arbitrary polygons, in case you would need to extend to that.
On the 2nd thought, if you don't need anything other than the triangle areas, you could do that in O(n), and scanline may be an overkill.
I came to this question late, but I think I've come up with a more fully flushed out solution along the lines of ryanm's answer. I'll give an outline of for others trying to do this problem at least somewhat efficiently.
First you have two trivial cases to check:
1) Triangle lies entirely within the square
2) Square lies entirely within the triangle (Just check if all corners are inside the triangle)
If neither is true, then things get interesting.
First, use either the Cohen-Sutherland or Liang-Barsky algorithm to clip each edge of the triangle to the square. (The linked article contains a nice bit of code that you can essentially just copy-paste if you're using C).
Given a triangle edge, these algorithms will output either a clipped edge or a flag denoting that the edge lies entirely outside the square. If all edges lie outsize the square, then the triangle and the square are disjoint.
Otherwise, we know that the endpoints of the clipped edges constitute at least some of the vertices of the polygon representing the intersection.
We can avoid a tedious case-wise treatment by making a simple observation. All other vertices of the intersection polygon, if any, will be corners of the square that lie inside the triangle.
Simply put, the vertices of the intersection polygon will be the (unique) endpoints of the clipped triangle edges in addition to the corners of the square inside the triangle.
We'll assume that we want to order these vertices in a counter-clockwise fashion. Since the intersection polygon will always be convex, we can compute its centroid (the mean over all vertex positions) which will lie inside the polygon.
Then to each vertex, we can assign an angle using the atan2 function where the inputs are the y- and x- coordinates of the vector obtained by subtracting the centroid from the position of the vertex (i.e. the vector from the centroid to the vertex).
Finally, the vertices can be sorted in ascending order based on the values of the assigned angles, which constitutes a counter-clockwise ordering. Successive pairs of vertices correspond to the polygon edges.

Resources