Punto aleatorio en una esfera

31

El reto

Escriba un programa o función que no tome entrada y genere un vector de longitud en una dirección aleatoria teóricamente uniforme .1

Esto es equivalente a un punto aleatorio en la esfera descrita por

X2+y2+z2=1

resultando en una distribución como tal

Distribución aleatoria de puntos en una esfera con radio 1.

Salida

Tres flotadores de una distribución aleatoria teóricamente uniforme para la cual la ecuación mantiene fiel a los límites de precisión.X2+y2+z2=1

Observaciones de desafío

  • La distribución aleatoria debe ser teóricamente uniforme . Es decir, si el generador de números pseudoaleatorios fuera reemplazado por un RNG verdadero de los números reales , daría como resultado una distribución aleatoria uniforme de puntos en la esfera.
  • Generar tres números aleatorios a partir de una distribución uniforme y normalizarlos no es válido: habrá un sesgo hacia las esquinas del espacio tridimensional.
  • Del mismo modo, generar dos números aleatorios a partir de una distribución uniforme y usarlos como coordenadas esféricas no es válido: habrá un sesgo hacia los polos de la esfera.
  • La uniformidad adecuada se puede lograr mediante algoritmos que incluyen, entre otros:
    • Genere tres números aleatorios , y partir de una distribución normal (gaussiana) alrededor de y normalícelos. Xyz00 0
    • Genere tres números aleatorios , y partir de una distribución uniforme en el rango . Calcule la longitud del vector por . Entonces, si , rechaza el vector y genera un nuevo conjunto de números. De lo contrario, si , normalice el vector y devuelva el resultado. Xyz( - 1 , 1 ) l = (-1,1)l=X2+y2+z2l>1l1
    • Genere dos números aleatorios y de una distribución uniforme en el rango y conviértalos a coordenadas esféricas de la siguiente manera: para que , y puedan calcularse medianteyoj( 0 , 1 ) θ(0 0,1)
      θ=2×π×yoϕ=cos-1(2×j-1)
      Xyz
      X=cos(θ)×pecado(ϕ)y=pecado(θ)×pecado(ϕ)z=cos(ϕ)
  • Proporcione en su respuesta una breve descripción del algoritmo que está utilizando.
  • Lea más sobre la selección de puntos de esfera en MathWorld .

Ejemplos de salida

[ 0.72422852 -0.58643067  0.36275628]
[-0.79158628 -0.17595886  0.58517488]
[-0.16428481 -0.90804027  0.38532243]
[ 0.61238768  0.75123833 -0.24621596]
[-0.81111161 -0.46269121  0.35779156]

Observaciones generales

Jitse
fuente
¿Está bien elegir 3 reales de manera uniforme en [-1, 1], luego rechazarlos (y repetir) si la suma de sus cuadrados no es 1?
Grimmy
66
@ Grimy Me gusta esa escapatoria. No, no está permitido, porque en teoría existe una probabilidad cero de cualquier salida.
Jitse
¿No es la sugerencia de @ Grimy similar a la segunda implementación de ejemplo mencionada por usted? Esa solución también tiene teóricamente cero posibilidades de producir cualquier salida
Saswat Padhi
2
@SaswatPadhi No, eso tiene la posibilidad pi/6 ≈ 0.5236de producir una salida. Esa es el área de la esfera inscrita en el cubo de área unitaria
Luis Mendo
1
@LuisMendo Ya veo, cierto. La probabilidad es ~ 0.5 en ese caso, como usted mencionó. Para la propuesta de Grimy, es ~ 0.
Saswat Padhi

Respuestas:

24

R , 23 bytes

x=rnorm(3)
x/(x%*%x)^.5

Pruébalo en línea!

Genera 3 realizaciones de la distribución N(0,1) y normaliza el vector resultante.

Parcela de 1000 realizaciones:

ingrese la descripción de la imagen aquí

Robin Ryder
fuente
2
¿Puedes justificar 3 ejes distribuidos normalmente dando como resultado una distribución uniforme sobre la esfera? (No lo veo)
Jeffrey apoya a Mónica el
44
@ Jeffrey Es bastante conocido en probabilidad / estadística; pero la prueba para 2D (que se extiende perfectamente a 3 dimensiones) es aproximadamente: sea e independiente. Entonces f X ( x ) = K e - 1X,Ynorte(0 0,1)yfY(y)=Ke-1FX(X)=Kmi-12X2, entonces por independenciafXY(x,y)=K2e-1fY(y)=Kmi-12y2dondez=(x,y), por lo que está claro que la distribución dezdepende únicamente de la magnitud dez, y por lo tanto la dirección se distribuye uniformemente. FXY(X,y)=K2mi-12(X2+y2)=FZ(z)=K2mi-12z2z=(X,y)zz
Giuseppe
1
Entonces, la distribución normal nos da puntos distribuidos uniformemente alrededor del círculo, y dividir por la magnitud asegura que los puntos se encuentren en el círculo
Giuseppe
23

Código de máquina x86-64 - 63 62 55 49 bytes

6A 4F                push        4Fh  
68 00 00 80 3F       push        3F800000h  
C4 E2 79 18 4C 24 05 vbroadcastss xmm1,dword ptr [rsp+5]  
rand:
0F C7 F0             rdrand      eax  
73 FB                jnc         rand  
66 0F 6E C0          movd        xmm0,eax  
greaterThanOne:
66 0F 38 DC C0       aesenc      xmm0,xmm0  
0F 5B C0             cvtdq2ps    xmm0,xmm0  
0F 5E C1             divps       xmm0,xmm1  
C4 E3 79 40 D0 7F    vdpps       xmm2,xmm0,xmm0,7Fh  
0F 2F 14 24          comiss      xmm2,dword ptr [rsp]  
75 E9                jne         greaterThanOne
58                   pop         rax  
58                   pop         rax  
C3                   ret  

Utiliza el segundo algoritmo, modificado. Devuelve el vector de [x, y, z, 0]en xmm0.

Explicación:

push 4Fh
push 3f800000h

Empuja el valor de 1 y 2 ^ 31 como flotante a la pila. Los datos se superponen debido a la extensión del signo, ahorrando unos pocos bytes.

vbroadcastss xmm1,dword ptr [rsp+5] Carga el valor de 2 ^ 31 en 4 posiciones de xmm1.

rdrand      eax  
jnc         rand  
movd        xmm0,eax

Genera un entero aleatorio de 32 bits y lo carga en la parte inferior de xmm0.

aesenc      xmm0,xmm0  
cvtdq2ps    xmm0,xmm0  
divps       xmm0,xmm1 

Genera un entero aleatorio de 32 bits, conviértalo en flotante (con signo) y divídalo entre 2 ^ 31 para obtener números entre -1 y 1.

vdpps xmm2,xmm0,xmm0,7Fhagrega los cuadrados de los 3 flotadores inferiores usando un producto de punto por sí mismo, enmascarando el flotador superior. Esto le da la longitud

comiss      xmm2,dword ptr [rsp]  
jne          rand+9h (07FF7A1DE1C9Eh)

Compara la longitud al cuadrado con 1 y rechaza los valores si no es igual a 1. Si la longitud al cuadrado es uno, entonces la longitud también es uno. Eso significa que el vector ya está normalizado y guarda una raíz cuadrada y divide.

pop         rax  
pop         rax 

Restaurar la pila.

ret devuelve valor en xmm0

Pruébalo en línea .

yo'
fuente
77
+1 Usar aesencpara producir 128 bits "aleatorios" es simplemente hermoso.
DocMax
13

Python 2 , 86 bytes

from random import*;R=random
z=R()*2-1
a=(1-z*z)**.5*1j**(4*R())
print a.real,a.imag,z

Pruébalo en línea!

Genera la coordenada z uniformemente de -1 a 1. Luego, las coordenadas xey se muestrean uniformemente en un círculo de radio (1-z*z)**.5.

Puede que no sea obvio que la distribución esférica es uniforme en factor sobre la coordenada z (y así sobre cada coordenada). Esto es algo especial para la dimensión 3. Vea esta prueba de que el área de superficie de un corte horizontal de una esfera es proporcional a su altura. Aunque las rebanadas cercanas al ecuador tienen un radio mayor, las rebanadas cercanas al polo se titulan más hacia adentro, y resulta que estos dos efectos se cancelan exactamente.

Para generar un ángulo aleatorio en este círculo, elevamos la unidad imaginaria 1ja una potencia aleatoria uniforme entre 0 y 4, lo que nos ahorra la necesidad de necesitar funciones trigonométricas, pi o e, cualquiera de las cuales necesitaría una importación. Luego extraemos la parte imaginaria real. Si podemos generar un número complejo para dos de las coordenadas, la última línea podría ser print a,z.


86 bytes

from random import*
a,b,c=map(gauss,[0]*3,[1]*3)
R=(a*a+b*b+c*c)**.5
print a/R,b/R,c/R

Pruébalo en línea!

Genera tres normales y escala el resultado.


Python 2 con numpy, 57 bytes

from numpy import*
a=random.randn(3)
print a/sum(a*a)**.5

Pruébalo en línea!

sum(a*a)**.5es más corto que linalg.norm(a). También podríamos hacer dot(a,a)lo mismo que sum(a*a). En Python 3, esto se puede acortar para a@ausar el nuevo operador @.

xnor
fuente
1
Me gusta tu primer acercamiento. Tengo problemas para entender cómo se evita un sesgo hacia el ecuador si z, desde una distribución uniforme, no se modifica.
Jitse
2
@Jitse La distribución esférica es uniforme en factor sobre cada coordenada. Esto es algo especial para la dimensión 3. Vea, por ejemplo, esta prueba de que el área de superficie de un corte de una esfera es proporcional a su altura. Con respecto a la intuición de que esto está sesgado en el ecuador, tenga en cuenta que mientras que las rebanadas cercanas al ecuador tienen un radio más grande, las que están cerca del polo se titulan más hacia adentro, lo que da más área, y resulta que estos dos efectos se cancelan exactamente.
xnor
¡Muy agradable! Gracias por la aclaración y la referencia.
Jitse
@Jitse Gracias, lo agregué al cuerpo. Sin embargo, me di cuenta de que solo estaba tomando muestras positivas z, y lo arreglé por unos pocos bytes.
xnor
1
@Jitse ¡De hecho, el área de superficie de una esfera es igual al área de superficie lateral del cilindro envolvente!
Neil
13

Octava , 40 33 22 bytes

Muestramos una distribución normal estándar 3d y normalizamos el vector:

(x=randn(1,3))/norm(x)

Pruébalo en línea!

falla
fuente
Solo para Octave (es decir, no MATLAB), puede guardar un byte con esto
Tom Carpenter,
1
@TomCarpenter ¡Gracias! En este caso, ya que es solo una expresión, incluso podemos omitir el disp:)
flawr
10

Unidad C # , 34 bytes

f=>UnityEngine.Random.onUnitSphere

La unidad tiene un valor incorporado para los valores aleatorios de la esfera de la unidad, así que pensé en publicarlo.

Draco18s
fuente
Buen uso de un +1 integrado, podría enviar una función para que sea un poco más cortaf=>Random.onUnitSphere
LiefdeWen
@LiefdeWen Sabía sobre lambdas, pero no estaba seguro si eso era suficiente (en términos de validez en Code Golf) porque no está declarando fel Tipo; usando varsolo funciona dentro de un método y System.Func<Vector3>fue más largo.
Draco18s
1
En codegolf, devolver una función está perfectamente bien, y no tiene que contar la declaración, lo que significa que puede hacer cosas furtivas con parámetros dinámicos. Tampoco cuentas el último punto y coma. Sin embargo, sí cuenta todos los enunciados que agrega. por lo tanto, su conteo de bytes debe incluir el uso. Pero f=>Random.onUnitSpherees una presentación perfectamente válida
LiefdeWen
@LiefdeWen Sí, simplemente no estaba seguro de cómo se manejó la declaración y no estaba realmente dispuesto a "buscar meta".
Draco18s
f=>UnityEngine.Random.onUnitSpherete salva elusing
Orace
6

MATL , 10 bytes

1&3Xrt2&|/

Pruébalo en línea!

Explicación

Esto utiliza el primer enfoque descrito en el desafío.

1&3Xr  % Generate a 1×3 vector of i.i.d standard Gaussian variables
t      % Duplicate
2&|    % Compute the 2-norm
/      % Divide, element-wise. Implicitly display
Luis Mendo
fuente
6

Ruby , 34 50 49 bytes

->{[z=rand*2-1]+((1-z*z)**0.5*1i**(rand*4)).rect}

Pruébalo en línea!

Devuelve una matriz de 3 números [z,y,x].

xy yse generan al elevar i(raíz cuadrada de -1) a una potencia aleatoria entre 0 y 4. Este número complejo debe escalarse adecuadamente de acuerdo con el zvalor de acuerdo con el teorema de Pitágoras:(x**2 + y**2) + z**2 = 1.

La zcoordenada (que se genera primero) es simplemente un número distribuido uniformemente entre -1 y 1. Aunque no es inmediatamente obvio, dA / dz para un corte a través de una esfera es constante (e igual al perímetro de un círculo del mismo radio que toda la esfera).

Aparentemente, esto fue descubierto por Arquímedes, quien lo describió de una manera muy poco parecida al cálculo, y se conoce como el teorema de la Arqueta de Arquímedes. Ver https://brilliant.org/wiki/surface-area-sphere/

Otra referencia de los comentarios sobre la respuesta de xnor. Una URL sorprendentemente corta, que describe una fórmula sorprendentemente simple: http://mathworld.wolfram.com/Zone.html

Level River St
fuente
@Jitse Olvidé reducir la escala de xey en valores altos de z. Efectivamente los puntos definieron un cilindro. ¡Ya está arreglado pero cuesta muchos bytes! Podría ahorrar algunos si la salida se puede expresar con un número complejo [z, x+yi]. Lo dejaré como está a menos que diga que está bien.
Level River St
¡Se ve bien! Realmente me gusta este enfoque. Por coherencia, la salida requerida es de tres flotantes, por lo que sugiero dejarlo así.
Jitse
¿Por qué no usar en z*zlugar de z**2?
Value Ink
@ValueInk, sí, gracias, me di cuenta de que me había perdido eso z*z. Lo he editado ahora. La otra cosa que podría hacer es reemplazar rand*4con algo como z*99o x*9E9(limitando efectivamente los posibles valores a una espiral muy fina en la esfera), pero creo que eso reduce la calidad del azar.
Level River St
4

05AB1E , 23 22 bytes

[тε5°x<Ýs/<Ω}DnOtDî#}/

Implementa el segundo algoritmo.

Pruébelo en línea u obtenga algunas salidas aleatorias más .

Explicación:

[0 0,1)0.000010.00000000159

[            # Start an infinite loop:
 тε          #  Push 100, and map (basically, create a list with 3 values):
   5°        #   Push 100,000 (10**5)
     x       #   Double it to 200,000 (without popping)
      <      #   Decrease it by 1 to 199,999
       Ý     #   Create a list in the range [0, 199,999]
        s/   #   Swap to get 100,000 again, and divide each value in the list by this
          <  #   And then decrease by 1 to change the range [0,2) to [-1,1)
           Ω #   And pop and push a random value from this list
  }          #  After the map, we have our three random values
   D         #   Duplicate this list
    n        #   Square each inner value
     O       #   Take the sum of these squares
      t      #   Take the square-root of that
       D     #   Duplicate that as well
        î    #   Ceil it, and if it's now exactly 1:
         #   #    Stop the infinite loop
}/           # After the infinite loop: normalize by dividing
             # (after which the result is output implicitly)
Kevin Cruijssen
fuente
1
l<1l1lX0 0<X1l<0,51
@Jitse Ok, implementé la normalización en mis respuestas Java y 05AB1E. Espero que todo esté correcto ahora.
Kevin Cruijssen
v1v==1v<10 0<X1l1
4

TI-BASIC, 15 bytes *

:randNorm(0,1,3
:Ans/√(sum(Ans²

Usando el algoritmo "genera 3 valores normalmente distribuidos y normaliza ese vector".

Al finalizar un programa con una expresión, se imprime automáticamente el resultado en la pantalla de inicio después de que el programa finaliza, por lo que el resultado se muestra, no solo se genera y se oculta.

*: randNorm(es un token de dos bytes , el resto son tokens de un byte . He contado el inicial (inevitable) :, sin eso serían 14 bytes. Guardado como un programa con un nombre de una letra, ocupa 24 bytes de memoria, que incluye los 9 bytes de sobrecarga del sistema de archivos.

harold
fuente
3

JavaScript (ES7),  77 76  75 bytes

pecado(ϕ)=pecado(cos-1(z))=1-z2

with(Math)f=_=>[z=2*(r=random)()-1,cos(t=2*PI*r(q=(1-z*z)**.5))*q,sin(t)*q]

Pruébalo en línea!

Comentado

with(Math)                       // use Math
f = _ =>                         //
  [ z = 2 * (r = random)() - 1,  // z = 2 * j - 1
    cos(                         //
      t =                        // θ =
        2 * PI *                 //   2 * π * i
        r(q = (1 - z * z) ** .5) // q = sin(ɸ) = sin(arccos(z)) = √(1 - z²)
                                 // NB: it is safe to compute q here because
                                 //     Math.random ignores its parameter(s)
    ) * q,                       // x = cos(θ) * sin(ɸ)
    sin(t) * q                   // y = sin(θ) * sin(ɸ)
  ]                              //

JavaScript (ES6), 79 bytes

Implementa el algoritmo.

f=_=>(n=Math.hypot(...v=[0,0,0].map(_=>Math.random()*2-1)))>1?f():v.map(x=>x/n)

Pruébalo en línea!

Comentado

f = _ =>                         // f is a recursive function taking no parameter
  ( n = Math.hypot(...           // n is the Euclidean norm of
      v =                        // the vector v consisting of:
        [0, 0, 0].map(_ =>       //
          Math.random() * 2 - 1  //   3 uniform random values in [-1, 1]
        )                        //
  )) > 1 ?                       // if n is greater than 1:
    f()                          //   try again until it's not
  :                              // else:
    v.map(x => x / n)            //   return the normalized vector
Arnauld
fuente
3

Procesando 26 bytes

Programa completo

print(PVector.random3D());

Esta es la implementación https://github.com/processing/processing/blob/master/core/src/processing/core/PVector.java

  static public PVector random3D(PVector target, PApplet parent) {
    float angle;
    float vz;
    if (parent == null) {
      angle = (float) (Math.random()*Math.PI*2);
      vz    = (float) (Math.random()*2-1);
    } else {
      angle = parent.random(PConstants.TWO_PI);
      vz    = parent.random(-1,1);
    }
    float vx = (float) (Math.sqrt(1-vz*vz)*Math.cos(angle));
    float vy = (float) (Math.sqrt(1-vz*vz)*Math.sin(angle));
    if (target == null) {
      target = new PVector(vx, vy, vz);
      //target.normalize(); // Should be unnecessary
    } else {
      target.set(vx,vy,vz);
    }
    return target;
  }
PrincePolka
fuente
2
Es posible que desee aclarar que la implementación no es parte de su recuento de bytes. Lo perdí en la primera lectura, luego hice una doble toma.
Level River St
Sin embargo, me gusta que la implementación utilice esencialmente el mismo enfoque que yo
Level River St el
2

Python 2 , 86 bytes

from random import*
x,y,z=map(gauss,[0]*3,[1]*3);l=(x*x+y*y+z*z)**.5
print x/l,y/l,z/l

Pruébalo en línea!

Implementa el primer algoritmo.


Python 2 , 107103 bytes

from random import*
l=2
while l>1:x,y,z=map(uniform,[-1]*3,[1]*3);l=(x*x+y*y+z*z)**.5
print x/l,y/l,z/l

Pruébalo en línea!

Implementa el segundo algoritmo.

TFeld
fuente
2
@RobinRyder Esta implementación rechaza vectores con una longitud inicial> 1, que es válida como se especifica en el desafío.
Jitse
@Jitse Correcto, lo siento. Leí mal el código.
Robin Ryder
2

Haskell , 125 123 119 118 bytes

import System.Random
f=mapM(\_->randomRIO(-1,1))"lol">>= \a->last$f:[pure$(/n)<$>a|n<-[sqrt.sum$map(^2)a::Double],n<1]

Pruébalo en línea!

Hace tres uniformes randoms y muestras de rechazo.

Angs
fuente
Parece que tus randoms son de la distribución (0,1) en lugar de (-1,1), de modo que solo 1/8 de la esfera está cubierta.
Jitse
@Jitse gotcha, gracias por notarlo.
Angs
2

JavaScript, 95 bytes

f=(a=[x,y,z]=[0,0,0].map(e=>Math.random()*2-1))=>(s=Math.sqrt(x*x+y*y+z*z))>1?f():a.map(e=>e/s)

Usted no necesita no a la entrada a.

Naruyoko
fuente
Wow, me lo perdí por completo. Fijo.
Naruyoko
2

Julia 1.0 , 24 bytes

x=randn(3)
x/hypot(x...)

Pruébalo en línea!

Dibuja un vector de 3 valores, extraídos de una distribución normal alrededor de 0 con desviación estándar 1. Luego, simplemente los normaliza.

usuario3263164
fuente
randn(), de un par de pruebas rápidas, no parece estar limitado al rango requerido. Además, esto no incluye una verificación para hypot()devolver un valor >1, que debe ser rechazado.
Shaggy
3
@Shaggy parece randnsimular a partir de una distribución normal estándar en lugar de una uniforme (0,1), por lo que este enfoque es idéntico al R.
Giuseppe
@Giuseppe ¡Sí, exactamente!
user3263164
@Giuseppe, creo que es posible que no tenga una comprensión adecuada de las matemáticas detrás de este desafío, pero, si te entiendo correctamente, estás diciendo que si alguno de los flotadores está fuera de los límites de [-1,1)dividirlos por el hipotenusa, que será >1, compensa eso? Eso me lleva a preguntarme si el ternario en mi solución es necesario ...
Shaggy
@ Shaggy no, la distribución normal / gaussiana tiene algunas propiedades (específicamente, invariancia rotacional) que el uniforme no tiene, vea este comentario , por ejemplo
Giuseppe
2

MathGolf , 21 19 18 bytes

{╘3Ƀ∞(ß_²Σ√_1>}▲/

Implementación del segundo algoritmo.

Pruébelo en línea o vea algunas salidas más al mismo tiempo .

Explicación:

{              }▲   # Do-while true by popping the value:
                   #  Discard everything on the stack to clean up previous iterations
  3É                #  Loop 3 times, executing the following three operations:
    ƒ               #   Push a random value in the range [0,1]
                   #   Double it to make the range [0,2]
      (             #   Decrease it by 1 to make the range [-1,1]
       ß            #  Wrap these three values into a list
        _           #  Duplicate the list of random values
         ²          #  Square each value in the list
          Σ         #  Sum them
                   #  And take the square-root of that
            _       #  Duplicate it as well
             1>     #  And check if it's larger than 1
                 /  # After the do-while, divide to normalize
                    # (after which the entire stack joined together is output implicitly,
                    #  which is why we need the `╘` to cleanup after every iteration)
Kevin Cruijssen
fuente
2

Java 8 ( @Arnauld 's tercera algoritmo modificado), 131 126 119 111 109 bytes

v->{double k=2*M.random()-1,t=M.sqrt(1-k*k),r[]={k,M.cos(k=2*M.PI*M.random())*t,M.sin(k)*t};return r;}

Puerto de la respuesta de JavaScript de @Arnauld , ¡así que asegúrese de votarlo!
-2 bytes gracias a @ OlivierGrégoire .

Esto se implementa como:

k=norte[-1,1)
t=1-k2
tu=2π×(norte[0 0,1))
X,y,z={k,cos(tu)×t,pecado(tu)×t}

Pruébalo en línea.

Implementación previa del 3er algoritmo ( 131 126 119 bytes):

Math M;v->{double k=2*M.random()-1,t=2*M.PI*M.random();return k+","+M.cos(t)*M.sin(k=M.acos(k))+","+M.sin(t)*M.sin(k);}

Implementado como:

k=norte[-1,1)
t=2π×(norte[0 0,1))
X,y,z={k,cos(t)×pecado(arcos(k)),pecado(t)×pecado(arcos(k))}

Pruébalo en línea.

Explicación:

Math M;                         // Math on class-level to use for static calls to save bytes
v->{                            // Method with empty unused parameter & double-array return
  double k=2*M.random()-1,      //  Get a random value in the range [-1,1)
         t=M.sqrt(1-k*k),       //  Calculate the square-root of 1-k^2
    r[]={                       //  Create the result-array, containing:
         k,                     //   X: the random value `k`
         M.cos(k=2*M.PI         //   Y: first change `k` to TAU (2*PI)
                     *M.random()//       multiplied by a random [0,1) value
                )               //      Take the cosine of that
                 *t,            //      and multiply it by `t`
         M.sin(k)               //   Z: Also take the sine of the new `k` (TAU * random)
                  *t};          //      And multiply it by `t` as well
  return r;}                    //  Return this array as result

Java 8 (segundo algoritmo), 153 143 bytes

v->{double x=2,y=2,z=2,l;for(;(l=Math.sqrt(x*x+y*y+z*z))>1;y=m(),z=m())x=m();return x/l+","+y/l+","+z/l;};double m(){return Math.random()*2-1;}

Pruébalo en línea.

2do algoritmo:

v->{                              // Method with empty unused parameter & String return-type
  double x=2,y=2,z=2,l;           //  Start results a,b,c all at 2
  for(;(l=Math.sqrt(x*x+y*y+z*z)) //  Loop as long as the hypotenuse of x,y,z
       >1;                        //  is larger than 1
    y=m(),z=m())x=m();            //   Calculate a new x, y, and z
  return x/l+","+y/l+","+z/l;}    //  And return the normalized x,y,z as result
double m(){                       // Separated method to reduce bytes, which will:
  return Math.random()*2-1;}      //  Return a random value in the range [-1,1)
Kevin Cruijssen
fuente
Usar sqrt(1-k*k)realmente ahorra más bytes en Java que en JS. :)
Arnauld
@Arnauld Sí. En lugar de 3x M.sin, 1x M.cosy 1x M.acos, su enfoque utiliza 2x M.siny 1x M.sqrt, que es de donde provienen principalmente los bytes adicionales guardados. :)
Kevin Cruijssen
108 bytes Utiliza un segundo algoritmo modificado donde solo permito valores donde s == 1 (en lugar de s <= 1 y luego normalizando). A veces da una respuesta, pero la mayoría no lo hace debido al tiempo de espera. Editar: Vaya, olvidé Math.sqrt el resultado
Olivier Grégoire
En realidad, no, no es necesario sqrt porque sqrt (1) == 1. Así que estoy de pie con mi sugerencia de golf.
Olivier Grégoire
1
109 bytes (puede usar su salida de cadena en lugar de, double[]ya que eso no cambia el conteo de bytes).
Olivier Grégoire
1

Japt , 20 bytes

Implementación del puerto de Arnauld del segundo algoritmo.

MhV=3ÆMrJ1
>1?ß:V®/U

Pruébalo

MhV=3ÆMrJ1
Mh             :Get the hypotenuse of
  V=           :  Assign to V
    3Æ         :  Map the range [0,3)
      Mr       :    Random float
        J1     :    In range [-1,1)
>1?ß:V®/U      :Assign result to U
>1?            :If U is greater than 1
   ß           :  Run the programme again
    :V®/U      :Else map V, dividing all elements by U
Lanudo
fuente
1

Pyth , 24 bytes

W<1Ks^R2JmtO2.0 3;cR@K2J

Pruébalo en línea!

Utiliza el algoritmo n. ° 2

W                         # while 
 <1                       #   1 < 
   Ks                     #       K := sum(
     ^R2                  #               map(lambda x:x**2,
        Jm      3         #                    J := map(                            , range(3))
          tO2.0           #                             lambda x: random(0, 2.0) - 1           )):
                 ;        #   pass
                   R   J  # [return] map(lambda x:            , J)
                  c @K2   #                        x / sqrt(K)
ar4093
fuente
1

OCaml , 110 99 95 bytes

(fun f a c s->let t,p=f 4.*.a 0.,a(f 2.-.1.)in[c t*.s p;s t*.s p;c p])Random.float acos cos sin

yojlet ... infun()

Pruébalo en línea


Solución original:

Random.(let a,c,s,i,j=acos,cos,sin,float 4.,float 2. in let t,p=i*.(a 0.),a (j-.1.) in[c t*.s p;s t*.s p;c p])

Primero defino:

una=arcos,  do=cos,  s=pecadoyounif(0 0,4 4),  junif(0 0,2)

La Random.floatfunción de OCaml incluye los límites. Luego,

t=youna(0 0)=yoπ2,  pags=una(j-1)

ϕ=pagsθ=t-yoj

Saswat Padhi
fuente
1
No estoy muy familiarizado con este lenguaje, pero parece que usa los flotadores aleatorios entre 0y 1directamente como coordenadas esféricas. Esto es incorrecto, como se muestra en los comentarios 3 y 4 del desafío, ya que terminas con un sesgo hacia los polos de la esfera. Puede corregir esto aplicando el método que se muestra en el comentario 4.
Jitse
¡Gracias! Totalmente extrañado eso. Solucioné el error y actualicé mi respuesta
Saswat Padhi
1
¡Se ve bien! Muy buena primera respuesta!
Jitse
Gracias :) ¡Pude reducirlo a menos de 100 bytes!
Saswat Padhi