Given a list of items x1 ... xn and associated probabilties p1 ... pn that sum up to 1 there's a well known procedure to select a random item with its associated proabability by sorting the list according to weight, choosing a random value between 1 and 0, and adding up to a culmination sum until it exceeds the value selected and return the item at this point.
So if we have x1 -> 0.5, x2 -> 0.3, x3 -> 0.2, if the randomly chosen value is less than 0.5 x1 will be chosen, if between 0.5 and 0.8, x2, and else x3.
This requires sorting so it needs O(nlogn) time. Is there anything more efficient than that?
I don't think you would actually need to sort the list for the algorithm to work.
x1 = 0.2, x2 = 0.7, x3 = 0.1
If you sort then you have:
x3: 0.0 to 0.1 = 10%
x1: 0.1 to 0.3 = 20%
x2: 0.3 to 1.0 = 70%
If you dont, you instead get:
x1: 0.0 to 0.2 = 20%
x2: 0.2 to 0.9 = 70%
x3: 0.9 to 1.0 = 10%
Just eliminating the sort and iterating through would make it O(n).
Related
I am trying to figure out how to sample for two random variables uniformly in the region where the sum of the two is greater than zero. I thought a solution might be to sample for X~U(-1,1) and then sample for Y~U(-x,1) where x would be the current sample for X.
But this resulted in a distribution that looks like this.
This doesn't look uniformly distributed as the density of points at the top left is higher and keeps reducing as we move to the right. Can someone point out where the flaw in my reasoning is and how to possibly fix this?
Thank you
You just need to make sure that adjust the density of x points away from the "top-left" corner appropriately. I'd also suggest generating in [0,1] and then transforming into [-1,1] afterwards.
For example:
import numpy as np
# generate points, sqrt takes care of moving points away from zero
n = 50000
x = np.sqrt(np.random.uniform(size=n))
y = np.random.uniform(1-x)
# transform to -1,1
x = x * 2 - 1
y = y * 2 - 1
plotting these gives:
which looks reasonable to me. Note I've colored the [-1,1] square to show where it should fit.
Could you please elaborate a bit on how you arrived at the answer?
Well, the main problem consists in getting a fair way to sample the non-uniform distribution of coordinate X.
From elementary geometry, the area of the part of the upper triangle with x < x0 is: (1/2) * (x0 + 1)2. As the total area of this upper triangle is equal to 2, it follows that the cumulative probability P of (X < x0) within the upper triangle is: P = (1/4) * (x0 + 1)2.
So, inverting the last formula, we have: x0 = 2*sqrt(P) - 1
Now, from the Inverse Transform Sampling theorem, we know that we can generate a fair sampling of X by reinterpreting P as a random variable U0 uniformly distributed between 0 and 1.
In Python, this gives us:
u0 = random.uniform(0.0, 1.0)
x = (2*math.sqrt(u0)) - 1.0
or equivalently:
u0 = random.random()
x = (2 * math.sqrt(u0)) - 1.0
Note that this is essentially the same maths as in the excellent answer by #SamMason. That thing comes from a general statistical principle. It can just as well be used to prove that a fair sampling of the latitude on a 3D sphere is given by arcsin(2*u - 1).
So now we have x, but we still need y. The underlying 2D density is an uniform one, so for a given x, all possible values of y are equidistributed.
The interval of possible values for y is [-x, 1]. So if U1 is yet another independent random variable uniformly distributed between 0 and 1, y can be drawn from the equation:
y = (1+x) * u1 - x
which in Python is rendered by:
u1 = random.random()
y = (1+x)*u1 - x
Overall, the Python code can be written like this:
import math
import random
import matplotlib.pyplot as plt
def mySampler():
u0 = random.random()
u1 = random.random()
x = 2*math.sqrt(u0) - 1.0
y = (1+x)*u1 - x
return (x,y)
#--- Main program:
points = (mySampler() for _ in range(10000)) # an iterator object
xx, yy = zip(*points)
plt.scatter(xx, yy, s=0.2)
plt.show()
Graphically, the result looks good enough:
Side note: a cheaper, ad hoc solution:
There is always the possibility of sampling uniformly in the whole square, and rejecting the points whose x+y sum happens to be negative. But this is a bit wasteful. We can have a more elegant solution by noting that the “bad” region has the same shape and area as the “good” region.
So if we get a “bad” point, instead of just rejecting it, we can replace it by its symmetic point with respect to the x+y=0 dividing line. This can be done using the following Python code:
def mySampler2():
x0 = random.uniform(-1.0, 1.0)
y0 = random.uniform(-1.0, 1.0)
s = x0+y0
if (s >= 0):
return (x0, y0) # good point
else:
return (x0-s, y0-s) # symmetric of bad point
This works fine too. And this is probably the cheapest possible solution regarding CPU time, as we reject nothing, and we don't need to compute a square root.
Following Generate random locations within a triangular domain
Code, to sample uniformly in any triangle, Python 3.9.4, Win 10 x64
import math
import random
import matplotlib.pyplot as plt
def trisample(A, B, C):
"""
Given three vertices A, B, C,
sample point uniformly in the triangle
"""
r1 = random.random()
r2 = random.random()
s1 = math.sqrt(r1)
x = A[0] * (1.0 - s1) + B[0] * (1.0 - r2) * s1 + C[0] * r2 * s1
y = A[1] * (1.0 - s1) + B[1] * (1.0 - r2) * s1 + C[1] * r2 * s1
return (x, y)
random.seed(312345)
A = (1, 0)
B = (1, 1)
C = (0, 1)
points = [trisample(A, B, C) for _ in range(10000)]
xx, yy = zip(*points)
plt.scatter(xx, yy, s=0.2)
plt.show()
The problem is as follows:
Given two different points P1 = (x1,y1), P2 = (x2,y2) and point G=(a,b), find c and d such that G'=(c,d) is the reflection of G about the line P1P2
What I am looking for is a method to do this quickly. Since I am working on floating point numbers, I'd also like to use a method which minimizes the absolute value of the exponent in scientific notation, but that is second priority.
What I have tried: let R be the vector which is the projection of vector (G-P1) onto vector (P2-P1). Then, the reflection is achieved by taking Q = P1 + R, which is the projection of G onto the line, and then G' = 2Q-G. Now this is all cool and dandy, but calculating the projection is the hard part here.
How I calculate the projection of vector A onto B:
The scalar product of A and B is |A|*|B|*cos(theta), where theta is the directed angle from A to B. You can obtain the value of the scalar product by taking xAxB + yAyB. But the projection is of length |A|*cos(theta), so we have to divide the scalar product by |B|. Now, we have the length, but not the direction. The direction is along vector B, so we must multiply by the unit vector along B, which is B/|B|. Ultimately, we get the formula (xAxB + yAyB)*B/|B|2.
The actual problem:
This is kind of a roundabout way to do this, and I am looking for a more direct formula from the coordinates. Additionally (although less important), calculating the length of a vector as I need to do in computing the projection and scalar product is problematic, when the numbers I am working on are big, because I may get a floating point overflow or something like that.
If this is of any significance, I am working in OCaml.
Thanks in advance
Formulas are really very simple.
Projection (for line AB and point P) seems similar to yours:
L = A + AB * ScalarProduct(AB, AP) / ScalarProduct(AB, AB)
Reflection point
P' = P + 2*(L-P) = 2*L-P
Working Python example:
def refl(x1, y1, x2, y2, xp, yp):
x12 = x2 - x1
y12 = y2 - y1
xxp = xp - x1
yyp = yp - y1
dotp = x12 * xxp + y12 * yyp
dot12 = x12 * x12 + y12 * y12
coeff = dotp / dot12
lx = x1 + x12 * coeff
ly = y1 + y12 * coeff
return 2*lx-xp, 2*ly-yp
print(refl(0, 0, 2, 2, 0, 1))
>>> (1.0, 0.0)
I am new to the promising language of Julia, in the hope that it will accelerate my stiff ordinary differential equations. Here is the thing:
1) The equation must be defined in matrix form, by using mass, damping, stiffness matrices outa Matlab with dimension 400x400. The common state-space represenation for 2nd order ODE's is implemented.
2) Apart from the linear dynamics, there are nonlinear forces acting, which depend on certain states of it. These forces must be defined inside the ode function.
However the state variables do not change at all, although the should, due to the inital conditions. Here is an example code, with smaller matrices, for prototyping:
#Load packages
using LinearAlgebra
using OrdinaryDiffEq
using DifferentialEquations
using Plots
# Define constant matrices (here generated for the example)
const M=Matrix{Float64}(I, 4, 4) # Mass matrix
const C=zeros(Float64, 4, 4) # Damping matrix
const K=[10.0 0.0 0.0 0.0; 0.0 7.0 0.0 0.0; 0.0 0.0 6.0 0.0;0.0 0.0 5.0 0.0] # Stiffness matrix
x0 = [0.0;0.0;0.0;0.0; 1.0; 1.0; 1.0; 1.0] # Initial conditions
tspan = (0.,1.0) # Simulation time span
#Define the underlying equation
function FourDOFoscillator(xdot,x,p,t)
xdot=[-inv(M)*C -inv(M)*K; Matrix{Float64}(I, 4, 4) zeros(Float64, 4, 4)]*x
end
#Pass to Solvers
prob = ODEProblem(FourDOFoscillator,x0,tspan)
sol = solve(prob,alg_hints=[:stiff],reltol=1e-8,abstol=1e-8)
plot(sol)
What am I missing?
Thanks
Betelgeuse
You're not mutating the output, and instead creating a new array. If you do xdot.= it works.
#Load packages
using LinearAlgebra
using OrdinaryDiffEq
using DifferentialEquations
using Plots
# Define constant matrices (here generated for the example)
const M=Matrix{Float64}(I, 4, 4) # Mass matrix
const C=zeros(Float64, 4, 4) # Damping matrix
const K=[10.0 0.0 0.0 0.0; 0.0 7.0 0.0 0.0; 0.0 0.0 6.0 0.0;0.0 0.0 5.0 0.0] # Stiffness matrix
x0 = [0.0;0.0;0.0;0.0; 1.0; 1.0; 1.0; 1.0] # Initial conditions
tspan = (0.,1.0) # Simulation time span
#Define the underlying equation
function FourDOFoscillator(xdot,x,p,t)
xdot.=[-inv(M)*C -inv(M)*K; Matrix{Float64}(I, 4, 4) zeros(Float64, 4, 4)]*x
end
#Pass to Solvers
prob = ODEProblem(FourDOFoscillator,x0,tspan)
sol = solve(prob,alg_hints=[:stiff],reltol=1e-8,abstol=1e-8)
plot(sol)
There are six charges, two negative (yellow), and four positive (blue) confined to a plane as shown below.
I plotted the equal potential lines. Now, I want to draw several lines that follow the smallest potential gradient (black lines). These lines have the properties such that they do not touch any positive charges, but always end at negative charges.
I did this in MATLAB, and I found that for any point on the black lines, if I calculate the potential distribution on a line segment that is perpendicular to the tangent at that point, the smallest potential is always achieved at that point.
Although it is possible to found such points by hand when there are not many charges, and I can then connect them to make the black curves. I am wondering if there is any way to automate this process? When dealing with different orientations, how do I choose the 'right area' to find the smallest potential points? If I look at the whole domain, the smallest potential points are usually located faraway from the charges.
Thank you so much for your time looking at this problem. Please comment below if you have any thoughts.
Cheers,
Mike
Here are my plots:
equal potential lines with field line:
equal potential lines with boundaries:
Here is the codes.
[X, Y] = meshgrid(0:.1:20, 0:.1:20);
x = [ 5 15 10 7 9 2];
y = [ 10 10 17 4 13 18];
q = [ 2 1 -2 2 -3 2];
U = zeros(201, 201, 2);
UU = zeros(201, 201);
for i = 1:6
r = sqrt((X-x(i)).^2 + (Y-y(i)).^2 + 0.01); % to avoid NaN
U(:,:,i) = q(i)./r;
UU = UU + U(:,:,i);
end
[dx, dy] = gradient(-UU);
%quiver(X, Y, dx, dy,2 );
% define the starting point of stremlines (electric field lines)
% originating from positive charges
th = linspace(0,360,19)*pi/180;
x0 = 0.1 * cos(th);
y0 = 0.1 * sin(th);
x1 = 1 * cos(th);
y1 = 1 * sin(th);
hold on
for i = 1:6
if q(i) > 0
streamline(X, Y, dx, dy, x0 + x(i), y0 + y(i));
end
end
% plot the boundaries, starting points picked by trial and error
streamline(X, Y, dx, dy, 13.20, 6.73)
streamline(X, Y, dx, dy, 13.19, 6.75)
streamline(X, Y, dx, dy, 1.85, 13.36)
streamline(X, Y, dx, dy, 1.84, 13.36)
streamline(X, Y, dx, dy, 5.58, 7.05)
streamline(X, Y, dx, dy, 5.58, 7.04)
hold off
I looked at the eigenvector matrix of a given matrix, but when I try to inverse it I have an error in eigenvector_matrix_inv().
require 'matrix'
m = Matrix[ [0.5703125, 1.8369140625, 0.0, 0.0],
[-0.6875, -0.4609375, 0.0, 0.0],
[0.0, 0.0, -2.1796875, 8.7119140625],
[0.0, 0.0, -0.6875, 2.2890625] ]
meigen = m.eigen.eigenvector_matrix
meiveni = m.eigen.eigenvector_matrix_inv
# .../matrix.rb:930:in `block in inverse_from': Not Regular Matrix (ExceptionForMatrix::ErrNotRegular)
It should not be singular, as checked with Mathematica:
mruby = {{0.5703125, 1.8369140625, 0.0, 0.0}, {-0.6875, -0.4609375,
0.0, 0.0}, {0.0, 0.0, -2.1796875, 8.7119140625}, {0.0,
0.0, -0.6875, 2.2890625}};
Inverse[Eigenvectors[mruby]]
giving
{{0.586146 - 0.302685 I, 0.586146 + 0.302685 I, 0. + 0. I,
0. + 0. I}, {0. - 1.07831 I, 0. + 1.07831 I, 0. + 0. I,
0. + 0. I}, {0. + 0. I, 0. + 0. I, 0.519354 + 1.16217 I,
0.519354 - 1.16217 I}, {0. + 0. I, 0. + 0. I, 0. - 4.53135 I,
0. + 4.53135 I}}
What am I doing wrong ?
Should I take special care of something particular in Ruby ?
You don't invert a matrix when you do eigenvalue problems. There are lots of algorithms, but inversion isn't one of them.
Your matrix is a bit odd: You've got two positive and two negative diagonal elements. I think the eigenvectors having complex entries suggests that it's not what you'd usually have: real eigenvalues with real eigenvectors.
Either your matrix is incorrect or you've chosen the wrong algorithm. See if a Hessian matrix is what you have and look at appropriate algorithms.