Ped*_*o77 8 matlab vector linear-algebra
我正在寻找一种简单的方法来创建一个受锥形区域约束的随机单位向量.原点始终是[0,0,0].
我的解决方案到目前为止:
function v = GetRandomVectorInsideCone(coneDir,coneAngleDegree)
coneDir = normc(coneDir);
ang = coneAngleDegree + 1;
while ang > coneAngleDegree
v = [randn(1); randn(1); randn(1)];
v = v + coneDir;
v = normc(v);
ang = atan2(norm(cross(v,coneDir)), dot(v,coneDir))*180/pi;
end
Run Code Online (Sandbox Code Playgroud)
我的代码循环,直到随机生成的单位向量在定义的锥体内.有没有更好的方法呢?
使用Ahmed Fasih代码进行频率分配(在评论中). 我想知道如何获得矩形或正态分布.
c = [1;1;1]; angs = arrayfun(@(i) subspace(c, GetRandomVectorInsideCone(c, 30)), 1:1e5) * 180/pi; figure(); hist(angs, 50);
Run Code Online (Sandbox Code Playgroud)
测试代码:
clearvars; clc; close all;
coneDir = [randn(1); randn(1); randn(1)];
coneDir = [0 0 1]';
coneDir = normc(coneDir);
coneAngle = 45;
N = 1000;
vAngles = zeros(N,1);
vs = zeros(3,N);
for i=1:N
vs(:,i) = GetRandomVectorInsideCone(coneDir,coneAngle);
vAngles(i) = subspace(vs(:,i),coneDir)*180/pi;
end
maxAngle = max(vAngles);
minAngle = min(vAngles);
meanAngle = mean(vAngles);
AngleStd = std(vAngles);
fprintf('v angle\n');
fprintf('Direction: [%.3f %.3f %.3f]^T. Angle: %.2fº\n',coneDir,coneAngle);
fprintf('Min: %.2fº. Max: %.2fº\n',minAngle,maxAngle);
fprintf('Mean: %.2fº\n',meanAngle);
fprintf('Standard Dev: %.2fº\n',AngleStd);
%% Plot
figure;
grid on;
rotate3d on;
axis equal;
axis vis3d;
axis tight;
hold on;
xlabel('X'); ylabel('Y'); zlabel('Z');
% Plot all vectors
p1 = [0 0 0]';
for i=1:N
p2 = vs(:,i);
plot3ex(p1,p2);
end
% Trying to plot the limiting cone, but no success here :(
% k = [0 1];
% [X,Y,Z] = cylinder([0 1 0]');
% testsubject = surf(X,Y,Z);
% set(testsubject,'FaceAlpha',0.5)
% N = 50;
% r = linspace(0, 1, N);
% [X,Y,Z] = cylinder(r, N);
%
% h = surf(X, Y, Z);
%
% rotate(h, [1 1 0], 90);
Run Code Online (Sandbox Code Playgroud)
plot3ex.m:
function p = plot3ex(varargin)
% Plots a line from each p1 to each p2.
% Inputs:
% p1 3xN
% p2 3xN
% args plot3 configuration string
% NOTE: p1 and p2 number of points can range from 1 to N
% but if the number of points are different, one must be 1!
% PVB 2016
p1 = varargin{1};
p2 = varargin{2};
extraArgs = varargin(3:end);
N1 = size(p1,2);
N2 = size(p2,2);
N = N1;
if N1 == 1 && N2 > 1
N = N2;
elseif N1 > 1 && N2 == 1
N = N1
elseif N1 ~= N2
error('if size(p1,2) ~= size(p1,2): size(p1,2) and/or size(p1,2) must be 1 !');
end
for i=1:N
i1 = i;
i2 = i;
if i > N1
i1 = N1;
end
if i > N2
i2 = N2;
end
x = [p1(1,i1) p2(1,i2)];
y = [p1(2,i1) p2(2,i2)];
z = [p1(3,i1) p2(3,i2)];
p = plot3(x,y,z,extraArgs{:});
end
Run Code Online (Sandbox Code Playgroud)
Ahm*_*sih 10
这是解决方案.它基于https://math.stackexchange.com/a/205589/81266上的精彩答案.我在Mathworld上了解到球形帽是这个带有平面的3球形切割后,我通过谷歌搜索"球冠上的随机点"找到了这个答案.
这是功能:
function r = randSphericalCap(coneAngleDegree, coneDir, N, RNG)
if ~exist('coneDir', 'var') || isempty(coneDir)
coneDir = [0;0;1];
end
if ~exist('N', 'var') || isempty(N)
N = 1;
end
if ~exist('RNG', 'var') || isempty(RNG)
RNG = RandStream.getGlobalStream();
end
coneAngle = coneAngleDegree * pi/180;
% Generate points on the spherical cap around the north pole [1].
% [1] See https://math.stackexchange.com/a/205589/81266
z = RNG.rand(1, N) * (1 - cos(coneAngle)) + cos(coneAngle);
phi = RNG.rand(1, N) * 2 * pi;
x = sqrt(1-z.^2).*cos(phi);
y = sqrt(1-z.^2).*sin(phi);
% If the spherical cap is centered around the north pole, we're done.
if all(coneDir(:) == [0;0;1])
r = [x; y; z];
return;
end
% Find the rotation axis `u` and rotation angle `rot` [1]
u = normc(cross([0;0;1], normc(coneDir)));
rot = acos(dot(normc(coneDir), [0;0;1]));
% Convert rotation axis and angle to 3x3 rotation matrix [2]
% [2] See https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
crossMatrix = @(x,y,z) [0 -z y; z 0 -x; -y x 0];
R = cos(rot) * eye(3) + sin(rot) * crossMatrix(u(1), u(2), u(3)) + (1-cos(rot))*(u * u');
% Rotate [x; y; z] from north pole to `coneDir`.
r = R * [x; y; z];
end
function y = normc(x)
y = bsxfun(@rdivide, x, sqrt(sum(x.^2)));
end
Run Code Online (Sandbox Code Playgroud)
这段代码只是在math.stackexchange上实现了joriki的答案,填写了joriki省略的所有细节.
这是一个显示如何使用它的脚本.
clearvars
coneDir = [1;1;1];
coneAngleDegree = 30;
N = 1e4;
sol = randSphericalCap(coneAngleDegree, coneDir, N);
figure;plot3(sol(1,:), sol(2,:), sol(3,:), 'b.', 0,0,0,'rx');
grid
xlabel('x'); ylabel('y'); zlabel('z')
legend('random points','origin','location','best')
title('Final random points on spherical cap')
Run Code Online (Sandbox Code Playgroud)
以下是以[1; 1; 1]矢量为中心的30°球形帽的10'000点的3D图:
这是120°球形帽:
现在,如果你看一下这些随机点之间角度的直方图coneDir = [1;1;1],你会看到分布是偏斜的.这是分布:
生成此代码的代码:
normc = @(x) bsxfun(@rdivide, x, sqrt(sum(x.^2)));
mysubspace = @(a,b) real(acos(sum(bsxfun(@times, normc(a), normc(b)))));
angs = arrayfun(@(i) mysubspace(coneDir, sol(:,i)), 1:N) * 180/pi;
nBins = 16;
[n, edges] = histcounts(angs, nBins);
centers = diff(edges(1:2))*[0:(length(n)-1)] + mean(edges(1:2));
figure('color','white');
bar(centers, n);
xlabel('Angle (degrees)')
ylabel('Frequency')
title(sprintf('Histogram of angles between coneDir and random points: %d deg', coneAngleDegree))
Run Code Online (Sandbox Code Playgroud)
嗯,这很有道理!如果你从120°球形帽产生点coneDir,当然 1°帽将会有很少的样品,而10°和11°帽之间的条带将有更多的点.因此,我们想要做的是将给定角度处的点数归一化球形帽在该角度的表面积.
这是一个函数,它给出了球面帽的表面积,半径R和角度为弧度theta(Mathworld的球冠制品上的公式16 ):
rThetaToH = @(R, theta) R * (1 - cos(theta));
rThetaToS = @(R, theta) 2 * pi * R * rThetaToH(R, theta);
Run Code Online (Sandbox Code Playgroud)
然后,我们可以n通过bin边缘的球形帽的表面积差异来标准化每个bin(上图)的直方图计数:
figure('color','white');
bar(centers, n ./ diff(rThetaToS(1, edges * pi/180)))
Run Code Online (Sandbox Code Playgroud)
图:
这告诉我们"随机向量的数量除以直方图箱边缘之间的球形段的表面积".这是统一的!
(注意:如果对原始代码生成的向量进行归一化直方图,使用拒绝采样,则同样成立:标准化直方图是均匀的.与此相比,拒绝采样的代价是昂贵的.)
(注2:注意通过首先生成方位角/仰角然后将这些球面坐标转换为笛卡尔坐标来挑选球体上随机点的天真方式是不好的,因为它在极点附近聚集点:Mathworld,示例,示例2.在整个球体上拾取点的一种方法是从3D正态分布中采样:这样你就不会在极点附近聚集.所以我相信你的原始技术非常合适,给你很好的,均匀分布的点没有任何聚束的球体.上面描述的这个算法也做了"正确的事情",应该避免聚束.仔细评估任何提出的算法,以确保避免聚束 - 近极问题.)