在C语言中生成特定平面上的随机点

3

我有一个三维空间中的二维平面: x+y+z=1,我想要在这个平面上生成随机点(x,y,z)。如何选择这些点使它们均匀分布?


2
请记住,SO不是一个“代码编写服务”,请展示您已经尝试过的内容,我们很乐意解决问题。 :) - shauryachats
4
生成 xy,然后计算 z - Eugene Sh.
你需要更加具体一些。你遇到了什么问题:设计算法、C语法、生成随机数还是其他什么?首先阅读http://stackoverflow.com/help/how-to-ask也不会有坏处。 - Michał Kwiatkowski
4
如果没有指定允许的范围和分布,就无法生成随机点。什么是“3D平面”?按定义,平面是二维的。你的意思是三维空间中的二维平面。 - Keith Thompson
2
等一下...我收回所有的话。这个表面是无限延伸的,这意味着你必须指定随机范围。比我最初想象的要困难得多。 - Lee Daniel Crocker
显示剩余4条评论
3个回答

6

问题

正如评论中所提到的,该问题未明确说明。尽管如此,这是一个有趣的问题。因为没有给出分布,我只选择了一个。以下是更精确/一般的问题:

假设我有一个平面 PR^3 中,由 ax + by + cz = d 定义。

c 是点 P 上距离原点最近的点。

如何在 c 的某个半径 r 内均匀地选择 P 上的一个点?

算法

n = (a,b,c)n 是垂直于 P 的向量。

方向

  1. 生成任意非零向量在平面 ax + by + cz = d 上,称其为 w。您可以通过将 n 与不平行于 n 的任何非零向量进行叉乘来实现此目的。

  2. [0,2pi) 范围内随机旋转 w,使其绕 n 旋转。您可以使用http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula来实现。

  3. 现在,通过归一化,您已经得到了方向。

direction = direction  /  direction.magnitude

射线的起源

  1. 如果 d 是0,则完成。否则:

  2. 根据http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane,计算平面到 Vector3(0,0,0)的距离为 c。

  3. 翻译射线的起点

 origin of the ray = vector3.zero +  c * ( n )

比例尺 = 随机范围(最小值,最大值)

所以关键是

  • 光线的起点 + 比例尺 * (方向_)

代码

这是我实现该算法的 C 代码。我从头开始编写了所有矢量工具,因此有点混乱。我没有彻底测试过这个代码。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

typedef struct {
    double x, y, z;
} vec3;

vec3 vec(double x, double y, double z);
vec3 crossp(vec3 u, vec3 v);
vec3 add(vec3 u, vec3 v);
double dotp(vec3 u, vec3 v);
double norm2(vec3 u);
double norm(vec3 u);
vec3 scale(vec3 u, double s);
vec3 normalize(vec3 u);
void print_vec3(vec3 u);

// generates a random point on the plane ax + by + cz = d
vec3 random_on_plane(double r, double a, double b, double c, double d) {
    // The normal vector for the plane
    vec3 n = vec(a, b, c);

    // create a normal vector on the plane ax + by + cz = 0
    // we take any vector not parallel to n
    // and find the cross product
    vec3 w;
    if (n.x == 0)
        w = crossp(n, vec(1,0,0));
    else
        w = crossp(n, vec(0,0,1));

    // rotate the vector around n by a random angle
    // using Rodrigues' rotation formula
    // http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
    double theta = ((double)rand() / RAND_MAX) * M_PI;
    vec3 k = normalize(n);
    w = add(scale(w, cos(theta)),
            scale(crossp(k, w), sin(theta)));

    // Scale the vector fill our disk.
    // If the radius is zero, generate unit vectors
    if (r == 0) {
        w = scale(w, r/norm(w));
    } else {
        double rand_r = ((double)rand() / RAND_MAX) * r;
        w = scale(w, rand_r/norm(w));
    }

    // now translate the vector from ax + by + cz = 0
    // to the plane ax + by + cz = d
    // http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane
    if (d != 0) {
        vec3 t = scale(n, d / norm2(n));
        w = add(w, t);
    }

    return w;
}

int main(void) {
    int i;
    srand(time(NULL));

    for (i = 0; i < 100; i++) {
        vec3 r = random_on_plane(10, 1, 1, 1, 1);
        printf("random v = ");
        print_vec3(r);
        printf("sum = %f, norm = %f\n", r.x + r.y + r.z, norm(r));
    }
}

vec3 vec(double x, double y, double z) {
    vec3 u;
    u.x = x;
    u.y = y;
    u.z = z;
    return u;
}

vec3 crossp(vec3 u, vec3 v) {
    vec3 w;
    w.x = (u.y * v.z) - (u.z * v.y);
    w.y = (u.z * v.x) - (u.x * v.z);
    w.z = (u.x * v.y) - (u.y * v.x);
    return w;
}

double dotp(vec3 u, vec3 v) {
    return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
}

double norm2(vec3 u) {
    return dotp(u, u);
}

double norm(vec3 u) {
    return sqrt(norm2(u));
}

vec3 scale(vec3 u, double s) {
    u.x *= s;
    u.y *= s;
    u.z *= s;
    return u;
}

vec3 add(vec3 u, vec3 v) {
    u.x += v.x;
    u.y += v.y;
    u.z += v.z;
    return u;
}

vec3 normalize(vec3 u) {
    return scale(u, 1/norm(u));
}

void print_vec3(vec3 u) {
    printf("%f %f %f\n", u.x, u.y, u.z);
}

2

Eugene几乎正确:在区间[0,1)上生成两个随机数,分别称为A、B。然后x=min(A,B),y=max(A,B)-x,z=1-(x+y)。基本上,您选择线段[0,1)上的两个点,您的三个坐标是由这两个点定义的三个区间。


3
注意:前提条件是x、y和z的范围为[0,1),这一点在你的问题中没有明确说明。这个曲面无限延伸,因此你需要更具体地说明范围。 - Lee Daniel Crocker
...并按比例缩放abc。但你总是会得到分布在三角形内的点。 - Cris Luengo

1

我先给你一个简单的算法

x = rand()
y = rand()
z = 1 - x - y

现在让我们看一下该算法的实现。
这段代码将产生任何类型的数字(正数或负数)。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main()
  {
    srand(time(NULL));
    int x= ( rand() - rand() )  ;
    int y= ( rand() - rand() )  ;
    int z=1-x-y;
    printf("x=%d y=%d z=%d",x,y,z); 
  }

只需使用 srand() 来为随机数生成器设定种子,使用 rand() 分配一个随机数。

如果您需要创建指定范围内的随机数,请使用 rand() % ( maxnumber + 1 ),其中 maxnumber 是您想要的最大值。

如果您希望所有的数字都是正数,则可以尝试以下方法:

int main()
  {
    srand(time(NULL));  
    int x, y , z = -1;
    while ( z < 0 ) 
     {
      x = rand()  ;
      y = rand() ;
      z = 1 - (x + y );
     }
    printf("x=%d y=%d z=%d",x,y,z); 
  }

警告
上述代码可能需要一些时间才能执行完毕,因此不要期望立即得到结果。


@LeeDanielCrocker,应该是随机数字。 - Arun A S
1
是的,但随机数仍然具有分布函数。通常人们希望它们均匀分布(即每个集合(x,y,z)等可能)。您的方案使得x> z的集合更有可能出现。 - Lee Daniel Crocker
@LeeDanielCrocker,我已经编辑过了。虽然可能不是最好的,但它修复了那个问题。 - Arun A S
那不会生成负数吗? - Lee Daniel Crocker
@LeeDanielCrocker,我承认我不擅长数学,但负数有什么问题吗?OP并没有提到这一点,而且据我记得,笛卡尔平面上可以有负点。 - Arun A S
你说得对,我没有理由假设一个正数范围。 - Lee Daniel Crocker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接