How does the 'canonize' function (given below, from Ukkonen's paper) work, and in particular, when is the while loop finished? I think the value of p' - k' will always remain less than that of p - k. Am I right or wrong?
procedure canonize(s, (k, p)):
1. if p < k then return (s, k)
2. else
3. find the tk–transition g'(s, (k', p')) = s' from s;
4. while p' − k' <= p − k do
5. k = k + p' − k' + 1;
6. s = s';
7. if k <= p then find the tk–transition g'(s, (k', p')) = s' from s;
8. return (s, k).
What the canonize function does is what is described at the very end of this SA post, where we consider a situation like this:
The situation is this:
The active point is at (red,'d',3), i.e. three characters into the defg edge going out of the red node.
Now we follow a suffix link to the green node. In theory, our active node is now (green,'d',3).
Unfortunately that point does not exist, because the de edge going out of the green node has got only 2 characters. Hence, we apply the canonize function.
It works like this:
The starting character of the edge we are interested in is d. This character is referred to as tk in Ukkonen's notation. So, "finding the tk-edge" means finding the de edge at the green node.
That edge is only two characters in length. I.e. (p' - k') == 2 in Ukkonen's notation. But the original edge had three characters: (p - k) == 3. So <= is true and we enter the loop.
We shorten the edge we are looking for from def to f. This is what the p := p + (k' - p') + 1 step does.
We advance to the state the de edge points to, i.e. the blue state. That is what s := s' does.
Since the remaining part f of the edge is not empty (k <= p), we identify the relevant outgoing edge (that is the fg edge going out of the blue node). This step sets k' and p' to entirely new values, because they now refer to the string fg, and its length (p' - k') will now be 2.
The length of the remaining edge f, (p - k), is now 1, and the length of the candidate edge fg for the new active point, (p' - k'), is 2. Hence the loop condition
while (p' - k') <= (p - k) do
is no longer true, so the loop ends, and indeed the new (and correct) active point is (blue,'f',1).
[Actually, in Ukkonen's notation, the end pointer p of an edge points to the position of the final character of the edge, not the position that follows it. Hence, strictly speaking, (p - k) is 0, not 1, and (p' - k') is 1, not 2. But what matters is not the absolute value of the length, but the relative comparison of the two different lengths.]
A few final notes:
Pointers like p and k refer to positions in the original input text t. That can be quite confusing. For example, the pointers used in the de edge at the green node will refer to some substring de of t, and the pointers used in the fg edge at the blue node will refer to some substring fg of t. Although the string defg must appear as one continuous string somewhere in t, the substring fg might appear in other places, too. So, the pointer k of the fg edge is not necessarily the end pointer p of the de edge plus one.
What counts when we decide whether or not to end the loop is therefore not the absolute positions k or p, but the length (p - k) of the remaining edge compared to the length (p' - k') of the current candidate edge.
In your question, line 4 of the code snippet, there is a typo: It should be k' instead of k;.
I had to change the canonize function because it didn't handle the auxiliary state properly. I added the following code after the 'p < k' if:
if (s == auxiliary)
{
s = root;
k++;
if (p < k)
return;
}
It seems to work now :)
Related
Lets say we have the Tic-Tac-Toe game at hand. And we want
to precompute a winning strategy in the following way as a tree.
From the winning moves only one is select and stored in the tree.
The many loosing moves the opponent has, all are stored in the
tree, so that we can blindly use the tree to guide us to our
next winning move, which is than again only one in the tree,
/
o---*- ..
\ /
o---*- ..
\
and so on, one, multiple, one, multiple etc.. How would one
do this in Prolog so that computing one such tree can be done
quite quickly for Tic-Tac-Toe game and a given start configuration?
I would do it like this (adapting from min max):
My choices are white nodes, other player's choices are black nodes.
Form a solving tree depth-first.
If the game has ended mark this leaf as won, tie or lost.
If a black node is not a leaf and all of its children are marked: "keep" all childnodes, mark the node as the worst cenario from its children (lost before tie before won)
If a white node has only one child left: copy the marking.
If a white node has one marked child which is maked as won, ignore the left over unmarked child-nodes (no traversing necessary). Mark as won.
If a white node has two marked children: keep only the best child, prefer won over tie over lost. If the mark is won or there are no unmarked nodes left the 2 above rules hold. Otherwise traverse an unmarked child to repeat this process.
the marking is only important for the minmax, not for the choice-tree.
for every childnode store the move to reach it
form the final tree as nested list (while traversing the tree)
Output would look like this:
[[1,
[2,[4,
[3,[..],[..]],[5,[3,[..],[..],[..]]]
]],
[3,[4,[5,..],[2,..]]],
[4,..],
[5,..]
]]
I start and have only one option: using move 1. Then black has the options 2, 3, 4 and 5, where these are all possible moves for black. If he chooses 2, I choose 4. Black now has option 3 and 5. If he chooses 5, I choose 3 and so on. Be sure to have enough RAM available ;)
Here is some proof of the pudding. One can use aggregate_all/3 for min/max, here is a little adaptation of the code here. But the code below does not yet return a winning strategy. It only returns a first move and a score:
% best(+Board, +Player, -Move, -Score)
best(X, P, N, W) :-
move(X, P, Y, N),
(win(Y, P) -> W = 1;
other(P, Q),
(tie(Y, Q) -> W = 0;
aggregate_all(max(V), best(Y, Q, _, V), H),
W is -H)).
We can check the moves and scores we get for a position:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W).
N = 2,
W = -1 ;
N = 5,
W = 1 ;
N = 6,
W = -1 ;
N = 8,
W = -1 ;
N = 9,
W = -1
Now how would we go about and store a winning strategy and choose a winning strategy? One idea here is now to replace the aggregate_all/3 by a findall/3. This should give us the multi branches of a winning strategy:
% best(+Board, +Player, -Tree, -Score)
best(X, P, N-R, W) :-
move(X, P, Y, N),
(win(Y, P) -> R = [], W = 1;
other(P, Q),
(tie(Y, Q) -> R = [], W = 0;
findall(M-V, best(Y, Q, M, V), L),
max_value(L, -1, H),
(Q == x ->
filter_value(L, H, J),
random_member(U, J),
R = [U];
strip_value(L, R)),
W is -H)).
We use random_member/2 for the single branches. Rerun to get different correct results:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[6-[]], 6-[9-[]], 8-[9-[]], 9-[6-[]]],
W = 1 ;
No
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[8-[6-[9-[]], 9-[6-[]]]], 6-[9-[]], 8-[6-[]], 9-[6-[]]],
W = 1 ;
No
Open source:
Prolog code for the tic-tac-toe game
score via aggregate, return first move
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac2-pl
Prolog code for the tic-tac-toe game
score via findall, return random winning strategy
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac3-pl
I constructed a finite automata for the language L of all strings made of the symbols 0, 1 and 2 (Σ = {0, 1, 2}) where the last symbol is not smaller than the first symbol. E.g., the strings 0, 2012, 01231 and 102 are in the language, but 10, 2021 and 201 are not in the language.
Then from that an GNFA so I can convert to RE.
My RE looks like this:
(0(0+1+2)* )(1(0(1+2)+1+2)* )(2((0+1)2+2))*)
I have no idea if this is correct, as I think I understand RE but not entirely sure.
Could someone please tell me if it’s correct and if not why?
There is a general method to convert any DFA into a regular expression, and is probably what you should be using to solve this homework problem.
For your attempt specifically, you can tell whether an RE is incorrect by finding a word that should be in the language, but that your RE doesn't accept, or a word that shouldn't be in the language that the RE does accept. In this case, the string 1002 should be in the language, but the RE doesn't match it.
There are two primary reasons why this string isn't matched. The first is that there should be a union rather than a concatenation between the three major parts of the language (words starting with 0, 1 and 2, respectively:
(0(0+1+2)*) (1(0(1+2)+1+2)*) (2((0+1)2+2))*) // wrong
(0(0+1+2)*) + (1(0(1+2)+1+2)*) + (2((0+1)2+2))*) // better
The second problem is that in the 1 and 2 cases, the digits smaller than the starting digit need to be repeatable:
(1(0 (1+2)+1+2)*) // wrong
(1(0*(1+2)+1+2)*) // better
If you do both of those things, the RE will be correct. I'll leave it as an exercise for you to follow that step for the 2 case.
The next thing you can try is find a way to make the RE more compact:
(1(0*(1+2)+1+2)*) // verbose
(1(0*(1+2))*) // equivalent, but more compact
This last step is just a matter of preference. You don't need the trailing +1+2 because 0* can be of zero length, so 0*(1+2) covers the +1+2 case.
You can use an algorithm but this DFA might be easy enough to convert as a one-off.
First, note that if the first symbol seen in the initial state is 0, you transition to state A and remain there. A is accepting. This means any string beginning with 0 is accepted. Thus, our regular expression might as well have a term like 0(0+1+2)* in it.
Second, note that if the first symbol seen in the initial state is 1, you transition to state B and remain in states B and D from that point on. You only leave B if you see 0 and you stay out of B as long as you keep seeing 0. The only way to end on D is if the last symbol you saw was 0. Therefore, strings beginning with 1 are accepted if and only if the strings don't end in 0. We can have a term like 1(0+1+2)*(1+2) in our regular expression as well to cover these cases.
Third, note that if the first symbol seen in the initial state is 2, you transition to state C and remain in states C and E from that point on. You leave state C if you see anything but 2 and stay out of B until you see a 2 again. The only way to end up on C is if the last symbol you saw was 2. Therefore, strings beginning with 2 are accepted if and only if the strings end in 2. We can have a term like 2(0+1+2)*(2) in our regular expression as well to cover these cases.
Finally, we see that there are no other cases to consider; our three terms cover all cases and the union of them fully describes our language:
0(0+1+2)* + 1(0+1+2)*(1+2) + 2(0+1+2)*2
It was easy to just write out the answer here because this DFA is sort of like three simple DFAs put together with a start state. More complicated DFAs might be easier to convert to REs using algorithms that don't require you understand or follow what the DFA is doing.
Note that if the start state is accepting (mentioned in a comment on another answer) the RE changes as follows:
e + 0(0+1+2)* + 1(0+1+2)*(1+2) + 2(0+1+2)*2
Basically, we just tack the empty string onto it since it is not already generated by any of the other parts of the aggregate expression.
You have the equivalent of what is known as a right-linear system. It's right-linear because the variables occur on the right hand sides only to the first degree and only on the right-hand sides of each term. The system that you have may be written - with a change in labels from 0,1,2 to u,v,w - as
S ≥ u A + v B + w C
A ≥ 1 + (u + v + w) A
B ≥ 1 + u D + (v + w) B
C ≥ 1 + (u + v) E + w C
D ≥ u D + (v + w) B
E ≥ (u + v) E + w C
The underlying algebra is known as a Kleene algebra. It is defined by the following identities that serve as its fundamental properties
(xy)z = x(yz), x1 = x = 1x,
(x + y) + z = x + (y + z), x + 0 = x = 0 + x,
y0z = 0, w(x + y)z = wxz + wyz,
x + y = y + x, x + x = x,
with a partial ordering relation defined by
x ≤ y ⇔ y ≥ x ⇔ ∃z(x + z = y) ⇔ x + y = y
With respect to this ordering relation, all finite subsets have least upper bounds, including the following
0 = ⋁ ∅, x + y = ⋁ {x, y}
The sum operator "+" is the least upper bound operator.
The system you have is a right-linear fixed point system, since it expresses the variables on the left as a (right-linear) function, as given on the right, of the variables. The object being specified by the system is the least solution with respect to the ordering; i.e. the least fixed point solution; and the regular expression sought out is the value that the main variable has in the least fixed point solution.
The last axiom(s) for Kleene algebras can be stated in any of a number of equivalent ways, including the following:
0* = 1
the least fixed point solution to x ≥ a + bx + xc is x = b* a c*.
There are other ways to express it. A consequence is that one has identities such as the following:
1 + a a* = a* = 1 + a* a
(a + b)* = a* (b a*)*
(a b)* a = a (b a)*
In general, right linear systems, such as the one corresponding to your problem may be written in vector-matrix form as 𝐪 ≥ 𝐚 + A 𝐪, with the least fixed point solution given in matrix form as 𝐪 = A* 𝐚. The central theorem of Kleene algebras is that all finite right-linear systems have least fixed point solutions; so that one can actually define matrix algebras over Kleene algebras with product and sum given respectively as matrix product and matrix sum, and that this algebra can be made into a Kleene algebra with a suitably-defined matrix star operation through which the least fixed point solution is expressed. If the matrix A decomposes into block form as
B C
D E
then the star A* of the matrix has the block form
(B + C E* D)* (B + C E* D)* C E*
(E + D B* C)* D B* (E + D B* C)*
So, what this is actually saying is that for a vector-matrix system of the form
x ≥ a + B x + C y
y ≥ b + D x + E y
the least fixed point solution is given by
x = (B + C E* D)* (a + C E* b)
y = (E + D B* C)* (D B* a + b)
The star of a matrix, if expressed directly in terms of its components, will generally be huge and highly redundant. For an n×n matrix, it has size O(n³) - cubic in n - if you allow for redundant sub-expressions to be defined by macros. Otherwise, if you in-line insert all the redundancy then I think it blows up to a highly-redundant mess that is exponential in n in size.
So, there's intelligence required and involved (literally meaning: AI) in finding or pruning optimal forms that avoid the blow-up as much as possible. That's a non-trivial job for any purported matrix solver and regular expression synthesis compiler.
An heuristic, for your system, is to solve for the variables that don't have a "1" on the right-hand side and in-line substitute the solutions - and to work from bottom-up in terms of the dependency chain of the variables. That would mean starting with D and E first
D ≥ u* (v + w) B
E ≥ (u + v)* w C
In-line substitute into the other inequations
S ≥ u A + v B + w C
A ≥ 1 + (u + v + w) A
B ≥ 1 + u u* (v + w) B + (v + w) B
C ≥ 1 + (u + v) (u + v)* w C + w C
Apply Kleene algebra identities (e.g. x x* y + y = x* y)
S ≥ u A + v B + w C
A ≥ 1 + (u + v + w) A
B ≥ 1 + u* (v + w) B
C ≥ 1 + (u + v)* w C
Solve for the next layer of dependency up: A, B and C:
A ≥ (u + v + w)*
B ≥ (u* (v + w))*
C ≥ ((u + v)* w)*
Apply some more Kleene algebra (e.g. (x* y)* = 1 + (x + y)* y) to get
B ≥ 1 + N (v + w)
C ≥ 1 + N w
where, for convenience we set N = (u + v + w)*. In-line substitute at the top-level:
S ≥ u N + v (1 + N (v + w)) + w (1 + N w).
The least fixed point solution, in the main variable S, is thus:
S = u N + v + v N (v + w) + w + w N w.
where
N = (u + v + w)*.
As you can already see, even with this simple example, there's a lot of chess-playing to navigate through the system to find an optimally-pruned solution. So, it's certainly not a trivial problem. What you're essentially doing is synthesizing a control-flow structure for a program in a structured programming language from a set of goto's ... essentially the core process of reverse-compiling from assembly language to a high level language.
One measure of optimization is that of minimizing the loop-depth - which here means minimizing the depth of the stars or the star height. For example, the expression x* (y x*)* has star-height 2 but reduces to (x + y)*, which has star height 1. Methods for reducing star-height come out of the research by Hashiguchi and his resolution of the minimal star-height problem. His proof and solution (dating, I believe, from the 1980's or 1990's) is complex and to this day the process still goes on of making something more practical of it and rendering it in more accessible form.
Hashiguchi's formulation was cast in the older 1950's and 1960's formulation, predating the axiomatization of Kleene algebras (which was in the 1990's), so to date, nobody has rewritten his solution in entirely algebraic form within the framework of Kleene algebras anywhere in the literature ... as far as I'm aware. Whoever accomplishes this will have, as a result, a core element of an intelligent regular expression synthesis compiler, but also of a reverse-compiler and programming language synthesis de-compiler. Essentially, with something like that on hand, you'd be able to read code straight from binary and the lid will be blown off the world of proprietary systems. [Bite tongue, bite tongue, mustn't reveal secret yet, must keep the ring hidden.]
I am working with some SVG files, and I would like to know how to differentiate a simple rotation, from a flip PLUS a rotation.
What I know :
example matrix :
transform="matrix(a,b,c,d,e,f)" Theorical
transform="matrix(1.866 0 -0 1.866 91.480 278.480)" Practical
We can determinate the flip of an element in this matrix by looking at the elements "a" and "d". A negative "a" means an horizontal flip and a negative "d" means a vertical flip.
My troubles arrive when I do a flip PLUS a rotation. The fact is that when I do a simple rotation, "a" and "d" can be negative too ! So how can we determinate if we have only a flip, or only a rotation, or a rotation PLUS a flip ?
Here is a matrix of an element on which I did a simple horizontal flip :
transform="matrix(-2.150 -0.012 -0.012 2.150 252.235 43.335)"
"a" element(-2.150) is negative.
Here is a matrix of an element on which I did a rotation of 135 degrees anti clockwise :
transform="matrix(-1.560 -1.479 1.479 -1.560 245.655 46.646)"
"a" element(-1.560) is negative too, but it is a simple rotation with no flip.
Here is a matrix of an element on which I did a horizontal flip PLUS a rotation of 135 degrees anti clockwise :
transform="matrix(1.674 -1.349 -1.349 -1.674 238.428 45.969)"
"a" element(1.674) is positive dispite of the flip.
Do you know a method with which I could always know if there is a simple rotation, or a simple flip, or a rotation PLUS a flip ?
If I am not clear enough, do not hesitate to ask me for more details.
Short answer: if ad - bc < 0, it's a reflection.
Long answer: if I understand the Mozilla docs correctly, (x, y) -> (ax + cy, bx + dy) plus a translation we don't need to worry about.
So, what we do is imagine the vectors in 3 dimensions: (x,y,0) -> (ax+cy,bx+dy,0). Take a unit i vector (1,0,0), and a unit j vector (0,1,0) and apply the transformation to each, to get i' and j'.
Now, the cunning bit: calculate the cross product of i' and j' and see whether it still points in the same direction as i x j (= k, (0,0,1) ). If so, the pair i' and j' are the same 'handedness' as i and j and no reflection has taken place. If it is opposite (i.e. pointing along -k) it has been reflected.
Cranking through the numbers, i' = (a,b,0) and j' is (c,d,0), and i' x j' is (0,0,ad-bc). If ad-bc < 0, then it's pointing along -k and a reflection has taken place.
Thanks for the answer, but it did not completely answered my question. I found a solution, so I post it here. I know my solution is not the best and could be improved easily, but I did not have the time to optimize it.
Here is what I did :
With the a and b element of the matrix, I calculated the radiant angle with the atan2 ( in Java Math.atan2(b, a) ). I transformed it in degrees.
Once we have got the degree angle, I did four cases, one for each quarter of the trigonometric circle :
First, I noticed that when you do a flip (horizontal or vertical), "b" and "c" elements have always the same sign.
So, here is the first condition : (sign of b) == (sign of c).
Depending on the sign of "b" element (or "c", because "b" == "c") AND of "d" element, we can determinate if we do a horizontal or a vertical flip.
So to determine precisely if we do a flip (H or V), we need to know the sign of "b" and "c", PLUS the sign of "d".
The last condition is on the sign of the degree angle calculated at the start. It will help us to determine the angle of rotation.
What I understood is that the axes move while we do a flip.
Let me show you the complete solution. I found four conditions, one for each quarter of the trigonometric circle :
double radianAngle = Math.atan2(b, a);
double degreeAngle = Math.toDegrees(radianAngle);
// if "b" and "c" have the same sign means there is a flip (H or V)
if (((b < 0) == ( c < 0))) {
if (b < 0 && d > 0 && degreeAngle < 0) {
// It is a horizontal flip
// The new angle is (- 180 - degreeAngle)
} else if (b > 0 && d < 0 && degreeAngle > 0) {
// It is a vertical flip
// The new angle is (-degreeAngle)
} else if (b > 0 && d > 0 && degreeAngle > 0) {
// It is a horizontal flip
// The new angle is (180 - degreeAngle)
} else if (b < 0 && d < 0 && degreeAngle < 0) {
// It is a horizontal flip
// The new angle is (-degreeAngle)
}
} else {
// No flip (H or V)
}
There are two more conditions which match the cases to the limits :
if (degreeAngle == 180 && a < 0) {
// It is a pure horizontal flip, with no rotation
} else if (degreeAngle == 0 && d < 0) {
// It is a pure vertical flip with no rotation
}
Here is what I finally used, and it works for all the cases.
I think it would have been easier to understand with more draws, but I do not have the time to do a more detailed answer.
Hope it helps,
I found this code snippet on raywenderlich.com, however the link to the explanation wasn't valid anymore. I "translated" the answer into Swift, I hope you can understand, it's actually quite easy even without knowing the language. Could anyone explain what exactly is going on here? Thanks for any help.
class func linesCross(#line1: Line, line2: Line) -> Bool {
let denominator = (line1.end.y - line1.start.y) * (line2.end.x - line2.start.x) -
(line1.end.x - line1.start.x) * (line2.end.y - line2.start.y)
if denominator == 0 { return false } //lines are parallel
let ua = ((line1.end.x - line1.start.x) * (line2.start.y - line1.start.y) -
(line1.end.y - line1.start.y) * (line2.start.x - line1.start.x)) / denominator
let ub = ((line2.end.x - line2.start.x) * (line2.start.y - line1.start.y) -
(line2.end.y - line2.start.y) * (line2.start.x - line1.start.x)) / denominator
//lines may touch each other - no test for equality here
return ua > 0 && ua < 1 && ub > 0 && ub < 1
}
You can find a detailed segment-intersection algorithm
in the book Computational Geometry in C, Sec. 7.7.
The SegSegInt code described there is available here.
I recommend avoiding slope calculations.
There are several "degenerate" cases that require care: collinear segments
overlapping or not, one segment endpoint in the interior of the other segments,
etc. I wrote the code to return an indication of these special cases.
This is what the code is doing.
Every point P in the segment AB can be described as:
P = A + u(B - A)
for some constant 0 <= u <= 1. In fact, when u=0 you get P=A, and you getP=B when u=1. Intermediate values of u will give you intermediate values of P in the segment. For instance, when u = 0.5 you will get the point in the middle. In general, you can think of the parameter u as the ratio between the lengths of AP and AB.
Now, if you have another segment CD you can describe the points Q on it in the same way, but with a different u, which I will call v:
Q = C + v(D - C)
Again, keep in mind that Q lies between C and D if, and only if, 0 <= v <= 1 (same as above for P).
To find the intersection between the two segments you have to equate P=Q. In other words, you need to find u and v, both between 0 and 1 such that:
A + u(B - A) = C + v(D - C)
So, you have this equation and you have to see if it is solvable within the given constraints on u and v.
Given that A, B, C and D are points with two coordinates x,y each, you can open the equation above into two equations:
ax + u(bx - ax) = cx + v(dx - cx)
ay + u(by - ay) = cy + v(dy - cy)
where ax = A.x, ay = A.y, etc., are the coordinates of the points.
Now we are left with a 2x2 linear system. In matrix form:
|bx-ax cx-dx| |u| = |cx-ax|
|by-ay cy-dy| |v| |cy-ay|
The determinant of the matrix is
det = (bx-ax)(cy-dy) - (by-ay)(cx-dx)
This quantity corresponds to the denominator of the code snippet (please check).
Now, multiplying both sides by the cofactor matrix:
|cy-dy dx-cx|
|ay-by bx-ax|
we get
det*u = (cy-dy)(cx-ax) + (dx-cx)(cy-ay)
det*v = (ay-by)(cx-ax) + (bx-ax)(cy-ay)
which correspond to the variables ua and ub defined in the code (check this too!)
Finally, once you have u and v you can check whether they are both between 0 and 1 and in that case return that there is intersection. Otherwise, there isn't.
For a given line the slope is
m=(y_end-y_start)/(x_end-x_start)
if two slopes are equal, the lines are parallel
m1=m1
(y1_end-y_start)/(x1_end-x1_start)=(y2_end-y2_start)/(x2_end-x2_start)
And this is equivalent to checking that the denominator is not zero,
Regarding the rest of the code, find the explanation on wikipedia under "Given two points on each line"
Could someone tell me if I've coded this correctly? This is my code for solving for the sides of a triangle given its perimeter, altitude, and angle (for the algebra see http://www.analyzemath.com/Geometry/challenge/triangle_per_alt_angle.html)
Prompt P
Prompt H
Prompt L [the angle]
(HP^2)/(2H(1+cos(L))+2Psin(L))→Y
(-P^2-2(1+cos(L))Y/(-2P)→Z
(Z+sqrt(Z^2-4Y))/2→N
[The same as above but Z-sqrt...]→R
If N>0
N→U
If R>0
R→U
Y/U→V
sqrt(U^2+V^2-2UVcos(L))→W
Disp U
Disp V
Disp W
Also, how would I fix this so that I can input angle = 90?
Also, in this code does it matter if the altitude is the one between b and c (refer to the website again)?
Thanks in advance
The code already works with L=90°.
Yes, the altitude must be the distance from point A to the base a between points B and C, forming a right-angle with that base. The derivation made that assumption, specifically with respect to the way it used h and a in the second area formula 1/2 h a. That exact formula would not apply if h was drawn differently.
The reason your second set of inputs resulted in a non-real answer is that sometimes a set of mathematical parameters can be inconsistent with each other and describe an impossible construct, and your P, h, and L values do exactly that. Specifically, they describe an impossible triangle.
Given an altitude h and angle L, the smallest perimeter P that can be achieved is an isosceles triangle split down the middle by h. With L=30, this would have perimeter P = a + b + c = 2h tan15 + h/cos15 + h/cos15, which, plugging in your h=3, results in P=7.819. You instead tried to use P=3+sqrt(3)=4.732. Try using various numbers less than 7.819 (plus a little; I've rounded here) and you'll see they all result in imaginary results. That's math telling you you're calculating something that cannot exist in reality.
If you fill in the missing close parenthesis between the Y and the / in line 5, then your code works perfectly.
I wrote the code slightly differently from you, here's what I did:
Prompt P
Prompt H
Prompt L
HP²/(2H(1+cos(L))+2Psin(L))→Y
(HP-Ysin(L))/H→Z
Z²-4Y→D
If D<0:Then
Disp "IMAGINARY"
Stop
End
(Z+√(D))/2→C
Y/C→B
P-(B+C)→A
Disp A
Disp B
Disp C
Edit: #Gabriel, there's nothing special (with respect to this question) about the angles 30-60-90; there is an infinite number of sets of P, h, and L inputs that describe such triangles. However, if you actually want to arrive at such triangles in the answer, you've actually changed the question; instead of just knowing one angle L plus P and h, you now know three angles (30-60-90) plus P and h. You've now over-specified the triangle, so that it is pretty well certain that a randomly generated set of inputs will describe an impossible triangle. As a contrived example, if you specified h as 0.0001 and P as 99999, then that's clearly impossible, because a triangle with a tiny altitude and fairly unextreme angles (which 30-60-90 are) cannot possibly achieve a perimeter many times its altitude.
If you want to start with just one of P or h, then you can derive equations to calculate all parameters of the triangle from the known P or h plus the knowledge of the 30-60-90 angles.
To give one example of this, if we assume that side a forms the base of the triangle between the 90° and 60° angles, then we have L=30 and (labelling the 60° angle as B) we have h=b, and you can get simple equations for all parameters:
P = a + h + c
sin60 = h/c
cos60 = a/c
=> P = c cos60 + c sin60 + c
P = c(cos60 + sin60 + 1)
c = P/(cos60 + sin60 + 1)
b = h = c sin60
a = c cos60
Plugging in P=100 we have
c = 100/(cos60 + sin60 + 1) = 42.265
b = h = 36.603
a = 21.132
If you plug in P=100, h=36.603, and L=30 into the code, you'll see you get these exact results.
Always optimize for speed, then size.
Further optimizing bgoldst's code:
Prompt P,H,L
HP²/(2H(1+cos(L))+2Psin(L
.5(Z+√((HP-sin(L)Ans)/H)²-4Ans
{Y/C→B,P-B-Ans,Ans