How can I calculate neighborhood overlap in weighted network? - graph

How can I calculate neighborhood overlap between two nodes (i,j) in a weighted graph?
"...we define the neighborhood overlap of an edge connecting A and B to be the ratio: (number of nodes who are neighbors of both A and B)/ (number of nodes who are neighbors of at least one of A or B) where in the denominator we don’t count A or B themselves (even though A is a neighbor of B and B is a neighbor of A)."
https://www.cs.cornell.edu/home/kleinber/networks-book/networks-book-ch03.pdf

networkx has a built-in function to find the common neighbors of two nodes in a graph: common_neighbors. Now we only need to find the number of nodes that are neighbors to either A or B. Note that
| N(u) ⋃ N(v) | = |N(u)| + |N(v)| - | N(u) ⋂ N(v)|
where N(u) is the set of neighbors of u. Since we don't count A or B in the denominator, we need to subtract 2 from | N(u) ⋃ N(v) |.
def neighborhood_overlap(g, u, v):
n_common_nbrs = len(set(nx.common_neighbors(g, u, v)))
n_join_nbrs = g.degree(u) + g.degree(v) - n_common_nbrs - 2
return n_common_nbrs / n_join_nbrs
However, you still need to decide what happens if the denominator is 0, i.e., when the nodes A and B form a connected component of the graph.

I know the tag of the question is networkx, but since this is the first thing that pop ups on Google, on igraph the function would be
neighborhood_overlap <- function(g, node1, node2)
{
nh1 = neighborhood(graph=g, order=1, nodes=node1)[[1]]
nh2 = neighborhood(graph=g, order=1, nodes=node2)[[1]]
common = intersect(nh1, nh2)
commonl = length(common)
# | N(u) ⋃ N(v) | = |N(u)| + |N(v)| - | N(u) ⋂ N(v)|
union = degree(net_vc, v=node1) + degree(net_vc, v=node2) - commonl - 2
return(commonl/union)
}

Related

Could not find the optimal solution after adding constraints

My code is as follows:
gekko = GEKKO(remote=True)
# create variable, each variable is a vector, each element
# of the vector is a binary
s = []
for i in range(N):
s.append(gekko.Array(gekko.Var, s_len[i], value=0, lb=0, ub=1, integer=True))
# some constants used in the objective/constraint function
c, d, r, m, L = create_c_d_r_m_L() # they are all numpy ndarry
# define the objective function
def objective():
obj = 0
for i in range(N):
obj += np.dot(s[i], c[i]) + np.dot(s[i], d[i])
for idx, (i, j) in enumerate(E):
obj += np.dot(np.dot(s[i], r[idx].reshape(s_len[i], s_len[j])),\
s[j]) # s[i] * r[i, j] * s[j]
return obj
# add constraints
# (a) each vector can only have and must have one 1
for i in range(N):
gekko.Equation(gekko.sum(s[i]) == 1)
# (b)
for t in range(N):
peak_mem = gekko.sum([np.dot(s[i], m[i]) for i in L[t]])
gekko.Equation(peak_mem < DEVICE_MEM)
# DEVICE_MEM is a predefined big int
# solve
gekko.Obj(objective())
gekko.solve(disp=True)
I found that when removing constraint (b), the solver can output the optimal solution for s. However, if we add (b) and set DEVICE_MEM to a very large number (which should not affect the solution), the s is not optimal anymore. I'm wondering if I am doing something wrong here because I tried both APOPT(solvertype=1) and IPOPT (solvertype=3) and they give the same nonoptimal results.
To give more context to the problem: this is an optimization over the graph. N represents the number of nodes in the graph. E is the set that contains all edges in the graph. c, d, m are three types of cost of a node. r is the cost of edges. Each node has multiple strategies (represented by the vector s[i]), and we need to select the best strategy for each node so that the overall cost is minimal.
Detailed constants:
# s_len: record the length of each vector
# (the # of strategies for each node,
# here we assume the length are all 10)
s_len = np.ones(N) * 10
# c, d, m are the costs of each node
# let's assume the c/d/m cost for i node is just i
c, d, m = [], [], []
for i in range(N):
c[i] = s_len[i] * [i]
d[i] = s_len[i] * [i]
m[i] = s_len[i] * [i]
# r is the edge cost, let's assume the cost for
# each edge is just i * j
r = []
for (i,j) in E: # E records all edges
cur_r = s_len[i] * s_len[j] * [i*j]
r.append(cur_r)
# L contains the node ids, we just randomly generate 10 integers here
L = []
for i in range(N):
cur_L = [randrange(N) for _ in range(10)]
L.append(cur_L)
I've been stuck on this for a while and any comments/answers are highly appreciated! Thanks!
Try reframing the inequality constraint:
for t in range(N):
peak_mem = gekko.sum([np.dot(s[i], m[i]) for i in L[t]])
gekko.Equation(peak_mem < DEVICE_MEM)
as a variable with an upper bound:
peak_mem = m.Array(m.Var,N,ub=DEVICE_MEM)
for t in range(N):
m.Equation(peak_mem[t]==\
gekko.sum([np.dot(s[i], m[i]) for i in L[t]])
The N inequality constraints peak_mem < DEVICE_MEM are converted to equality constraints with slack variables as s[i] = DEVICE_MEM - peak_mem with a simple inequality constraint on the slack s[i]>=0. If the inequality constraint far from the bound, then the slack variable can be very large. Formulating the equation as a variable may help.
I tried using the information in the question to pose a minimal problem that could reproduce the error and the potential solution. If you need more specific suggestions, please modify the code to be a complete and minimal example that reproduces the error. This helps with verifying the solution.

The game with the marbles

Problem: There are R red marbles, G green marbles and B blue marbles (R≤G≤B) Count the number of ways to arrange them in a straight line so that the two marbles next to each other are of different colors.
For example, R=G=B=2, the answer is 30.
I have tried using recursion and of course TLE:
Define r(R,B,G) to be the number of ways of arranging them where the first marble is red. Define b(R,B,G),g(R,B,G) respectively.
Then r(R, B, G) = b(R-1,B,G) + g(R-1,B,G)
And the answer is r(R,B,G) + b(R,B,G) + g(R,B,G)
But we can see that r(R, B, G) = b(B, R, G) ...
So, we just need a function f(x,y,z)=f(y,x−1,z)+f(z,x−1,y)
And the answer is f(x,y,z) + f(y,z,x) + f(z,x,y).
The time limit is 2 seconds.
I don't think dynamic is not TLE because R, G, B <= 2e5
Some things to limit the recursion:
If R>G+B+1, then there is no way to avoid having 2 adjacent reds. (Similar argument for G>R+B+1 & B>R+G+1.)
If R=G+B+1, then you alternate reds with non-reds, and your problem is reduced to how many ways you can arrange G greens and B blacks w/o worrying about adjacency (and should thus have a closed-form solution). (Again, similar argument for G=R+B+1 and B=R+G+1.)
You can use symmetry to cut down the number of recursions.
For example, if (R, G, B) = (30, 20, 10) and the last marble was red, the number of permutations from this position is exactly the same as if the last marble was blue and (R, G, B) = (10, 20, 30).
Given that R ≤ G ≤ B is set as a starting condition, I would suggest keeping this relationship true by swapping the three values when necessary.
Here's some Python code I came up with:
memo = {}
def marble_seq(r, g, b, last):
# last = colour of last marble placed (-1:nothing, 0:red, 1:green, 2:blue)
if r == g == b == 0:
# All the marbles have been placed, so we found a solution
return 1
# Enforce r <= g <= b
if r > g:
r, g = g, r
last = (0x201 >> last * 4) & 0x0f # [1, 0, 2][last]
if r > b:
r, b = b, r
last = (0x012 >> last * 4) & 0x0f # [2, 1, 0][last]
if g > b:
g, b = b, g
last = (0x120 >> last * 4) & 0x0f # [0, 2, 1][last]
# Abort if there are too many marbles of one colour
if b>r+g+1:
return 0
# Fetch value from memo if available
if (r,g,b,last) in memo:
return memo[(r,g,b,last)]
# Otherwise check remaining permutations by recursion
result = 0
if last != 0 and r > 0:
result += marble_seq(r-1,g,b,0)
if last != 1 and g > 0:
result += marble_seq(r,g-1,b,1)
if last != 2 and b > 0:
result += marble_seq(r,g,b-1,2)
memo[(r,g,b,last)] = result
return result
marble_seq(50,60,70,-1) # Call with `last` set to -1 initially
(Result: 205435997562313431685415150793926465693838980981664)
This probably still won't work fast enough for values up to 2×105, but even with values in the hundreds, the results are quite enormous. Are you sure you stated the problem correctly? Perhaps you're supposed to give the results modulo some prime number?

Counting nodes and arcs in BFS Algorithm

In DFS you can count the elements by initializating two counters and incrementing them in the DFS-VISIT procedure(+1 node every time the procedure is called and +1 arc everytime that the adjacency list is explored). I was wondering How to obtain the same result in BFS.
This is the BFS pseudocode from Cormen's "Introduction to Algorithms", where G is the graph, s is the source node, d is the distance and π is the father node. How can i modify it to obtain the number of nodes and arcs in G?
BFS(G, s)
for each node u ∈ G.V - {s}
u.color = white
u.d = ∞
u.π = NIL
s.color = GRAY
s.d = 0
s.π = NIL
Q = Ø
ENQUEUE(Q, s)
while Q != Ø
u = DEQUEUE(Q)
for each v ∈ G.Adj[u]
if v.color == WHITE
v.color = GRAY
v.d = u.d + 1
v.π = u
ENQUEUE(Q, v)
u.color = BLACK
Well, both the adjacency list traversal and new vertex(node) discovery are done in the final while loop of your pseudocode. So, something like the modification given below could work.
numArcs = 0
numNodes = 0
while Q != Ø
u = DEQUEUE(Q)
numNodes += 1
for each v ∈ G.Adj[u]
numArcs += 1
if v.color == WHITE
v.color = GRAY
v.d = u.d + 1
v.π = u
ENQUEUE(Q, v)
u.color = BLACK
Note that if you want to count all the arcs, the incrementation of numArcs should be outside the scope of the if statement that follows it, as that if statement is only entered when the destination node is not previously enqueued.
Notice also that this algorithm gives only the number of arcs and nodes in the connected component including the starting node s. So, unless your BFS algorithm is modified to handle the case of the graph having multiple connected components, this algorithm will not find out all the nodes and arcs in a graph that is not connected.

Line of intersection between two planes

How can I find the line of intersection between two planes?
I know the mathematics idea, and I did the cross product between the the planes normal vectors
but how to get the line from the resulted vector programmatically
The equation of the plane is ax + by + cz + d = 0, where (a,b,c) is the plane's normal, and d is the distance to the origin. This means that every point (x,y,z) that satisfies that equation is a member of the plane.
Given two planes:
P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0
The intersection between the two is the set of points that verifies both equations. To find points along this line, you can simply pick a value for x, any value, and then solve the equations for y and z.
y = (-c1z -a1x -d1) / b1
z = ((b2/b1)*(a1x+d1) -a2x -d2)/(c2 - c1*b2/b1)
If you make x=0, this gets simpler:
y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)
Finding the line between two planes can be calculated using a simplified version of the 3-plane intersection algorithm.
The 2'nd, "more robust method" from bobobobo's answer references the 3-plane intersection.
While this works well for 2 planes (where the 3rd plane can be calculated using the cross product of the first two), the problem can be further reduced for the 2-plane version.
No need to use a 3x3 matrix determinant,instead we can use the squared length of the cross product between the first and second plane (which is the direction of the 3'rd plane).
No need to include the 3rd planes distance,(calculating the final location).
No need to negate the distances.Save some cpu-cycles by swapping the cross product order instead.
Including this code-example, since it may not be immediately obvious.
// Intersection of 2-planes: a variation based on the 3-plane version.
// see: Graphics Gems 1 pg 305
//
// Note that the 'normal' components of the planes need not be unit length
bool isect_plane_plane_to_normal_ray(
const Plane& p1, const Plane& p2,
// output args
Vector3f& r_point, Vector3f& r_normal)
{
// logically the 3rd plane, but we only use the normal component.
const Vector3f p3_normal = p1.normal.cross(p2.normal);
const float det = p3_normal.length_squared();
// If the determinant is 0, that means parallel planes, no intersection.
// note: you may want to check against an epsilon value here.
if (det != 0.0) {
// calculate the final (point, normal)
r_point = ((p3_normal.cross(p2.normal) * p1.d) +
(p1.normal.cross(p3_normal) * p2.d)) / det;
r_normal = p3_normal;
return true;
}
else {
return false;
}
}
Adding this answer for completeness, since at time of writing, none of the answers here contain a working code-example which directly addresses the question.
Though other answers here already covered the principles.
Finding a point on the line
To get the intersection of 2 planes, you need a point on the line and the direction of that line.
Finding the direction of that line is really easy, just cross the 2 normals of the 2 planes that are intersecting.
lineDir = n1 × n2
But that line passes through the origin, and the line that runs along your plane intersections might not. So, Martinho's answer provides a great start to finding a point on the line of intersection (basically any point that is on both planes).
In case you wanted to see the derivation for how to solve this, here's the math behind it:
First let x=0. Now we have 2 unknowns in 2 equations instead of 3 unknowns in 2 equations (we arbitrarily chose one of the unknowns).
Then the plane equations are (A terms were eliminated since we chose x=0):
B1y + C1z + D1 = 0
B2y + C2z + D2 = 0
We want y and z such that those equations are both solved correctly (=0) for the B1, C1 given.
So, just multiply the top eq by (-B2/B1) to get
-B2y + (-B2/B1)*C1z + (-B2/B1)*D1 = 0
B2y + C2z + D2 = 0
Add the eqs to get
z = ( (-B2/B1)*D1 - D2 ) / (C2 * B2/B1)*C1)
Throw the z you find into the 1st equation now to find y as
y = (-D1 - C1z) / B1
Note the best variable to make 0 is the one with the lowest coefficients, because it carries no information anyway. So if C1 and C2 were both 0, choosing z=0 (instead of x=0) would be a better choice.
The above solution can still screw up if B1=0 (which isn't that unlikely). You could add in some if statements that check if B1=0, and if it is, be sure to solve for one of the other variables instead.
Solution using intersection of 3 planes
From user's answer, a closed form solution for the intersection of 3 planes was actually in Graphics Gems 1. The formula is:
P_intersection = (( point_on1 • n1 )( n2 × n3 ) + ( point_on2 • n2 )( n3 × n1 ) + ( point_on3 • n3 )( n1 × n2 )) / det(n1,n2,n3)
Actually point_on1 • n1 = -d1 (assuming you write your planes Ax + By + Cz + D=0, and not =-D). So, you could rewrite it as:
P_intersection = (( -d1 )( n2 × n3 ) + ( -d2 )( n3 × n1 ) + ( -d3 )( n1 × n2 )) / det(n1,n2,n3)
A function that intersects 3 planes:
// Intersection of 3 planes, Graphics Gems 1 pg 305
static Vector3f getIntersection( const Plane& plane1, const Plane& plane2, const Plane& plane3 )
{
float det = Matrix3f::det( plane1.normal, plane2.normal, plane3.normal ) ;
// If the determinant is 0, that means parallel planes, no intn.
if( det == 0.f ) return 0 ; //could return inf or whatever
return ( plane2.normal.cross( plane3.normal )*-plane1.d +
plane3.normal.cross( plane1.normal )*-plane2.d +
plane1.normal.cross( plane2.normal )*-plane3.d ) / det ;
}
Proof it works (yellow dot is intersection of rgb planes here)
Getting the line
Once you have a point of intersection common to the 2 planes, the line just goes
P + t*d
Where P is the point of intersection, t can go from (-inf, inf), and d is the direction vector that is the cross product of the normals of the two original planes.
The line of intersection between the red and blue planes looks like this
Efficiency and stability
The "robust" (2nd way) takes 48 elementary ops by my count, vs the 36 elementary ops that the 1st way (isolation of x,y) uses. There is a trade off between stability and # computations between these 2 ways.
It'd be pretty catastrophic to get (0,inf,inf) back from a call to the 1st way in the case that B1 was 0 and you didn't check. So adding in if statements and making sure not to divide by 0 to the 1st way may give you the stability at the cost of code bloat, and the added branching (which might be quite expensive). The 3 plane intersection method is almost branchless and won't give you infinities.
This method avoids division by zero as long as the two planes are not parallel.
If these are the planes:
A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0
1) Find a vector parallel to the line of intersection. This is also the normal of a 3rd plane which is perpendicular to the other two planes:
(A3,B3,C3) = (A1,B1,C1) cross (A2,B2,C2)
2) Form a system of 3 equations. These describe 3 planes which intersect at a point:
A1*x1 + B1*y1 + C1*z1 + D1 = 0
A2*x1 + B2*y1 + C2*z1 + D2 = 0
A3*x1 + B3*y1 + C3*z1 = 0
3) Solve them to find x1,y1,z1. This is a point on the line of intersection.
4) The parametric equations of the line of intersection are:
x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t
The determinant-based approach is neat, but it's hard to follow why it works.
Here's another way that's more intuitive.
The idea is to first go from the origin to the closest point on the first plane (p1), and then from there go to the closest point on the line of intersection of the two planes. (Along a vector that I'm calling v below.)
Given
=====
First plane: n1 • r = k1
Second plane: n2 • r = k2
Working
=======
dir = n1 × n2
p1 = (k1 / (n1 • n1)) * n1
v = n1 × dir
pt = LineIntersectPlane(line = (p1, v), plane = (n2, k2))
LineIntersectPlane
==================
#We have n2 • (p1 + lambda * v) = k2
lambda = (k2 - n2 • p1) / (n2 • v)
Return p1 + lambda * v
Output
======
Line where two planes intersect: (pt, dir)
This should give the same point as the determinant-based approach. There's almost certainly a link between the two. At least the denominator, n2 • v, is the same, if we apply the "scalar triple product" rule. So these methods are probably similar as far as condition numbers go.
Don't forget to check for (almost) parallel planes. For example: if (dir • dir < 1e-8) should work well if unit normals are used.
You can find the formula for the intersection line of two planes in this link.
P1: a1x + b1y + c1z = d1
P2: a2x + b2y + c2z = d2
n1=(a1,b1,c1); n2=(a2,b2,c2); n12=Norm[Cross[n1,n2]]^2
If n12 != 0
a1 = (d1*Norm[n2]^2 - d2*n1.n2)/n12;
a2 = (d2*Norm[n1]^2 - d1*n1.n2)/n12;
P = a1 n1 + a2 n2;
(*formula for the intersection line*)
Li[t_] := P + t*Cross[n1, n2];
The cross product of the line is the direction of the intersection line. Now you need a point in the intersection.
You can do this by taking a point on the cross product, then subtracting Normal of plane A * distance to plane A and Normal of plane B * distance to plane b. Cleaner:
p = Point on cross product
intersection point = ([p] - ([Normal of plane A] * [distance from p to plane A]) - ([Normal of plane B] * [distance from p to plane B]))
Edit:
You have two planes with two normals:
N1 and N2
The cross product is the direction of the Intersection Line:
C = N1 x N2
The class above has a function to calculate the distance between a point and a plane. Use it to get the distance of some point p on C to both planes:
p = C //p = 1 times C to get a point on C
d1 = plane1.getDistance(p)
d2 = plane2.getDistance(p)
Intersection line:
resultPoint1 = (p - (d1 * N1) - (d2 * N2))
resultPoint2 = resultPoint1 + C

Calculate Triangle From Area And Angles

I'm trying to calculate triangles base on the Area and the angles. If Angle-B is 90° then the formula works, but in my case, the angle can be from 0.1° to 179.8°. The formula assumes that the angle is 90, so I was thinking that there might be something that is hidden that could work for very angle. Here is the formula:
The formula in code would be:
Height = sqrt((2 * Area) / (tan(Angle-A)));
I'm looking for the second half of the formula. Would the next part of the formula be something like this:
cos(sin(AngleB))
Okay, new try: If my calculations are correct, side B equals sqrt(2*area*sin(angle-B)/(sin(angle-A)*sin(angle-C))
Since Area = 1/2 * A * B * sin(c) = 1/2 * C * B * sin(a) = 1/2 * A * C * sin(b) we get:
A = 2 * area / (B * sin(c)) and using this we get:
C = sin(c) * B / sin(b) and when we place that back into the equation of area, we get:
B = sqrt(2*area*sin(angle-B)/(sin(angle-A)*sin(angle-C))
When you know one side and all the angles, calculating the other sides should be easy using normal trigonometry.
tziki's answer is correct, but I'd like to elaborate on how it's derived.
We start with angles and area as knowns. I'm going to use the labels in the OP's diagram for this explanation.
First we have the basic truth that the area of a triangle is half the product of its base and height: Area = base * height / 2. We want to be able to determine the relationship between base and height so that we can reduce this equation to one unknown and solve for base.
Another important thing to know is that the height of the triangle is proportional to Side-A: height = Side-A * sin(Angle-B). So knowing Side-A will give us the height.
Now we need to establish a relationship between Side-A and Side-C (the base). The most appropriate rule here is the sine law: Side-A/sin(A) = Side-C/sin(C). We re-arrange this equation to find Side-A in terms of Side-C: Side-A = Side-C * sin(A)/sin(C).
We can now insert this result into the height equation to get the formula for height in terms of Side-C only: height = Side-C * sin(A) * sin(B) / sin(C)
Using Side-C as the base in the area equation, we can now find the area in terms of Side-C only: Area = Side-C^2 * sin(A) * sin(B) / 2sin(C)
Then re-arrange this equation to find Side-C in terms of Area:
Side-C = SQRT(2 * Area * sin(C) / (sin(B) * (sin(A)))
And that gives you one side. This can be repeated to find the other sides, or you can use a different approach to find the other sides knowing this one.
You already have your answer, but I had to solve this kind of exercise for a job interview some time ago. It's not hard, and it didn't took me much time to arrive to the following solution.
Read it through and it should be self explanatory.
Create a Python module that solves triangles by aplying the sin and cosin theorems.
The module receives as parameters some of a triangle's values and, if possible, returns the values of all it's angles and side lengths.
The parameters are received as a dict and it should be able to be called stand-alone from the command line.
from __future__ import division
import sys, logging
from math import radians, degrees, acos, cos, sin, sqrt, asin
class InconsistentDataError(TypeError):
pass
class InsufficientDataError(TypeError):
pass
class NonUpdatable(dict):
"""Dictionary whose items can be set only once."""
def __setitem__(self, i, y):
if self.get(i, None):
raise InconsistentDataError()
super(NonUpdatable, self).__setitem__(i, y)
def get_known_sides(**kwarg):
"""Filter from the input elements the Side elements."""
return dict([i for i in kwarg.iteritems() if i[0].isupper()])
def get_known_angles(**kwarg):
"""Filter from the input elements the Angle elements."""
return dict([i for i in kwarg.iteritems() if i[0].islower()])
def get_opposite_angle(C, B, A):
"""
Get the angle corresponding to C.
Keyword arguments:
A -- right side of the angle (real number > 0)
B -- left side of the angle (real number > 0)
C -- side opposite to the angle (real number > 0)
Returns:
angle opposite to C
"""
return degrees(acos((A**2 + B**2 - C**2) / (2 * A * B)))
def get_side(A, B, c):
"""
Calculate the Side corresponding to the Angle c.
Keyword arguments:
A -- left side of C (real number > 0)
B -- right side of C (real number > 0)
c -- angle opposite to side C (real number)
Returns:
side C, opposite to c
"""
return sqrt(A**2 + B**2 - 2*A*B*cos(radians(c)))
def get_overlapping_angle(known_angles, known_sides):
"""
Calculate the Angle of a known side, knowing the angle to another known side.
Keyword arguments:
known_angles -- (dict of angles)
known_sides -- (dict of sides)
Returns:
angle of the known side, to which there is no known angle
"""
a = (set([i.lower() for i in known_sides.iterkeys()]) -
set([i.lower() for i in known_angles.iterkeys()])).pop()
b = (set([i.lower() for i in known_sides.iterkeys()]) &
set([i.lower() for i in known_angles.iterkeys()])).pop()
y = (known_sides[a.upper()]/known_sides[b.upper()]) * sin(radians(known_angles[b.lower()]))
if y > 1: y = 1 #Rounding error fix --- y = 1.000000000001; asin(y) -> Exception
return {a.lower(): degrees(asin(y))}
def get_angles(A, B, C):
"""
Calculate all the angles, given the length of all the sides.
Keyword arguments:
A -- side A (real number > 0)
B -- side B (real number > 0)
C -- side C (real number > 0)
Returns:
dict of angles
"""
sides = {"A":A,"B":B,"C":C}
_sides = sides.keys()
angles = {}
for side in sides.keys():
angles[side.lower()] = get_opposite_angle(
sides[_sides[0]],
sides[_sides[1]],
sides[_sides[2]])
_sides.append(_sides.pop(0))
return angles
def get_triangle_values(**kwargs):
"""Calculate the missing values of a triangle based on the known values."""
known_params = kwargs
angles = NonUpdatable({
"a":0,
"b":0,
"c":0,
})
sides = NonUpdatable({
"A":0,
"B":0,
"C":0,
})
if len(known_params) < 3:
raise InsufficientDataError("Three parameters are needed to calculate triangle's values.")
if str(known_params.keys()).islower():
raise TypeError("At least one length needed.")
known_sides = NonUpdatable(get_known_sides(**known_params))
sides.update(known_sides)
known_angles = NonUpdatable(get_known_angles(**known_params))
angles.update(known_angles)
if len(known_angles) == 3 and sum(known_angles.itervalues()) != 180:
raise InconsistentDataError("One of the sides is too long.")
if len(known_sides) == 3:
x=[side for side in known_sides.itervalues() if (sum(known_sides.itervalues()) - side) < side]
if len(x):
raise InconsistentDataError("One of the sides is too long.")
for angle, value in get_angles(**known_sides).iteritems():
# Done this way to force exception when overwriting a
# user input angle, otherwise it would be a simple assignment.
# >>> angles = get_angles(**known_sides)
# This means inconsistent input data.
angles[angle] = value
else: # There are angles given and not enough sides.
if len(known_angles) > 1:
#2 angles given. Get last angle and calculate missing sides
for angle, val in angles.iteritems():
if val == 0:
angles[angle] = 180. - sum(angles.itervalues())
known_sides = known_sides.items()
for side, length in sides.iteritems():
if length == 0:
sides[side] = known_sides[0][1] / \
sin(radians(angles[known_sides[0][0].lower()])) * \
sin(radians(angles[side.lower()]))
else:
unknown_side = (set(sides.keys()) - set(known_sides.keys())).pop()
chars = [ord(i.lower()) for i in known_params.iterkeys()]
chars.sort()
if chars[0] < chars[1] < chars[2]:
sides[unknown_side] = get_side(known_sides.values()[0], known_sides.values()[1], known_angles[unknown_side.lower()])
angles = get_angles(**sides)
else:
known_angles.update(get_overlapping_angle(known_angles, known_sides))
angles.update(known_angles)
for angle, val in angles.iteritems():
if val == 0:
angles[angle] = 180. - sum(angles.itervalues())
sides[unknown_side] = get_side(known_sides.values()[0], known_sides.values()[1], angles[unknown_side.lower()])
angles.update(sides)
return angles
if __name__ == "__main__":
try:
values = get_triangle_values( **eval(sys.argv[1], {}, {}) )
except IndexError, e:
values = get_triangle_values(A=10,B=10,C=10)
except InsufficientDataError, e:
print "Not enough data!"
exit(1)
except InconsistentDataError, e:
print "Data is inconsistent!"
exit(1)
print values
A, B and C are the side lengths and a, b and c are the angles, so c is the angle opposite to the side C.
Tests
How about calculating the lengths of two of the sides of a triangle where the third size is assigned a length of 1 (using the law of sines on the angles, with that side assigned length 1), then scale the triangle until its area matches the area you have? Then you can calculate the height fairly easily. http://en.wikipedia.org/wiki/Triangle_area#Using_trigonometry
If I made no mistake the length of a side x between two angles a and b should be the following.
________________
/ 2A 2A
x = / ------ + ------
\/ tan(a) tan(b)
So to calculate side-C you but in angle-A and angle-B.
(Now double checked it - seems to be correct.)
The formula you give for Side-A seems to be correct IF the triangle is isosceles, i.e., Angle-B = Angle-C (you get this using the law of sines and the sine formula for the area). If it is not isosceles, you seem to need to know the other angles; the general formula is:
Side-A = sqrt(2*Area*sin(Angle-A)/(sin(Angle-B)*sin(Angle-C)))
Of course, I did this in my head, so check the math ;)
Edit: Ok, fixed the formula after checkin on paper.

Resources