Ponto aleatório em uma esfera

31

O desafio

Escreva um programa ou função que não aceite nenhuma entrada e produza um vetor de comprimento em uma direção aleatória teoricamente uniforme .1

Isso é equivalente a um ponto aleatório na esfera descrito por

x2+y2+z2=1

resultando em uma distribuição como essa

Distribuição aleatória de pontos em uma esfera com raio 1.

Saída

Três flutuadores de uma distribuição aleatória teoricamente uniforme para a qual a equação é verdadeira para os limites de precisão.x2+y2+z2=1

Comentários do desafio

  • A distribuição aleatória precisa ser teoricamente uniforme . Ou seja, se o gerador de números pseudo-aleatórios fosse substituído por um RNG verdadeiro a partir dos números reais , isso resultaria em uma distribuição aleatória uniforme de pontos na esfera.
  • Gerar três números aleatórios a partir de uma distribuição uniforme e normalizá-los é inválido: haverá um viés nos cantos do espaço tridimensional.
  • Da mesma forma, gerar dois números aleatórios a partir de uma distribuição uniforme e usá-los como coordenadas esféricas é inválido: haverá um viés em direção aos pólos da esfera.
  • A uniformidade adequada pode ser alcançada por algoritmos, incluindo, entre outros:
    • Gerar três números aleatórios de x , y e z de um normal de distribuição (Gaussiana) em torno de 0 e normalizar-los.
    • Gerar três números aleatórios de x , y e z a partir de uma uniforme distribuição na gama de (1,1) . Calcule o comprimento do vetor por l=x2+y2+z2 . Então, sel>1, rejeite o vetor e gere um novo conjunto de números. Caso contrário, sel1, normalize o vetor e retorne o resultado.
    • Gerar dois números aleatórios i e j de uma uniforme distribuição na gama (0,1) e convertê-los em coordenadas esféricas assim:
      θ=2×π×iϕ=cos1(2×j1)
      para quex,yezpossam ser calculados por
      x=cos(θ)×sin(ϕ)y=sin(θ)×sin(ϕ)z=cos(ϕ)
  • Forneça na sua resposta uma breve descrição do algoritmo que você está usando.
  • Leia mais sobre a escolha de pontos de esfera no MathWorld .

Exemplos de saída

[ 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]

Observações gerais

Jitse
fonte
Tudo bem escolher 3 reais uniformemente em [-1, 1] e depois rejeitá-los (e repetir) se a soma de seus quadrados não for 1?
Grimmy 9/09
6
@ Grimy Eu gosto dessa brecha. Não, não é permitido, porque existe uma chance teoricamente zero de qualquer saída.
Jitse 9/09
A sugestão de @ Grimy não é semelhante ao segundo exemplo de implementação mencionado por você? Essa solução também tem teoricamente zero chance de produzir qualquer saída
Saswat Padhi 09/09
2
@SaswatPadhi Não, isso tem chance pi/6 ≈ 0.5236de produzir uma saída. Essa é a área da esfera inscrita no cubo de área unitária
Luis Mendo
1
@LuisMendo Entendo, certo. A probabilidade é de ~ 0,5 nesse caso, como você mencionou. Para a proposta de Grimy, é ~ 0.
Saswat Padhi

Respostas:

24

R , 23 bytes

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

Experimente online!

Gera 3 realizações da distribuição N(0,1) e normaliza o vetor resultante.

Gráfico de 1000 realizações:

insira a descrição da imagem aqui

Robin Ryder
fonte
2
Você pode justificar 3 eixos distribuídos normalmente, resultando em uma distribuição uniforme sobre a esfera? (Eu não vejo isso)
Jeffrey apoia Monica em
4
@ Jeffrey É bem conhecido em probabilidade / estatística; mas a prova para 2D (que se estende ordenadamente a três dimensões) é aproximadamente: seja e independente. Então f X ( x ) = K e - 1X,YN(0,1)eFY(y)=KE-1fX(x)=Ke12x2, então pela independênciafXY(x,y)=K2e-1fY(y)=Ke12y2ondez=(x,y), de modo que é evidente que a distribuição dezdepende unicamente da magnitude dez, e, assim, a direcção é uniformemente distribuída. fXY(x,y)=K2e12(x2+y2)=fZ(z)=K2e12z2z=(x,y)zz
Giuseppe
1
Portanto, a distribuição normal nos dá pontos uniformemente distribuídos ao redor do círculo, e dividir pela magnitude garante que os pontos fiquem no círculo
Giuseppe
23

x86-64 Código da máquina - 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  

Usa o segundo algoritmo, modificado. Retorna o vetor de [x, y, z, 0]em xmm0.

Explicação:

push 4Fh
push 3f800000h

Empurra o valor para 1 e 2 ^ 31 como um float para a pilha. Os dados se sobrepõem devido à extensão do sinal, economizando alguns bytes.

vbroadcastss xmm1,dword ptr [rsp+5] Carrega o valor de 2 ^ 31 em 4 posições de xmm1.

rdrand      eax  
jnc         rand  
movd        xmm0,eax

Gera um número inteiro aleatório de 32 bits e o carrega na parte inferior de xmm0.

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

Gera um número inteiro aleatório de 32 bits, converte-o em float (assinado) e divida por 2 ^ 31 para obter números entre -1 e 1.

vdpps xmm2,xmm0,xmm0,7Fhadiciona os quadrados dos 3 carros alegóricos inferiores usando um produto pontilhado, mascarando o carro alegórico superior. Isso dá o comprimento

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

Compara o comprimento ao quadrado com 1 e rejeita os valores se não for igual a 1. Se o comprimento ao quadrado for um, o comprimento também será um. Isso significa que o vetor já está normalizado e salva uma raiz quadrada e divide.

pop         rax  
pop         rax 

Restaure a pilha.

ret retorna valor em xmm0

Experimente online .

mim'
fonte
7
+1 Usar aesencpara produzir 128 bits "aleatórios" é simplesmente bonito.
DocMax 10/09
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

Experimente online!

Gera a coordenada z uniformemente de -1 a 1. Em seguida, as coordenadas x e y são amostradas uniformemente em um círculo de raio (1-z*z)**.5.

Pode não ser óbvio que a distribuição esférica seja fator uniforme sobre a coordenada z (e também sobre todas as coordenadas). Isso é algo especial para a dimensão 3. Veja esta prova de que a área de superfície de uma fatia horizontal de uma esfera é proporcional à sua altura. Embora as fatias próximas ao equador tenham um raio maior, as fatias próximas ao polo têm mais um título para dentro, e esses dois efeitos são exatamente cancelados.

Para gerar um ângulo aleatório nesse círculo, elevamos a unidade imaginária 1ja uma potência aleatória uniforme entre 0 e 4, o que nos impede de precisar de funções trigonométricas, pi ou e, qualquer uma das quais precisaria de uma importação. Extraímos então a parte imaginária real. Se pudermos gerar um número complexo para duas das coordenadas, a última linha pode ser apenas 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

Experimente online!

Gera três normais e dimensiona o resultado.


Python 2 com numpy, 57 bytes

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

Experimente online!

sum(a*a)**.5é mais curto que linalg.norm(a). Também poderíamos fazer dot(a,a)o mesmo tamanho que sum(a*a). No Python 3, isso pode ser reduzido para o a@auso do novo operador @.

xnor
fonte
1
Eu gosto da sua primeira abordagem. Estou tendo problemas para entender como evitar um viés em direção ao equador se z, a partir de uma distribuição uniforme, não for modificado.
Jitse 9/09
2
@Jitse A distribuição esférica é uniforme em cada coordenada. Isso é algo especial para a dimensão 3. Veja, por exemplo, esta prova de que a área de superfície de uma fatia de uma esfera é proporcional à sua altura. Com relação à intuição de que isso é tendencioso para o equador, observe que, enquanto as fatias próximas ao equador têm um raio maior, as próximas ao pólo são mais indicadas para dentro, o que fornece mais área, e esses dois efeitos são exatamente cancelados.
xnor 9/09
Muito agradável! Obrigado pelo esclarecimento e pela referência.
Jitse 9/09
@Jitse Obrigado, eu adicionei ao corpo. No entanto, percebi que estava apenas com uma amostra positiva ze o consertei por alguns bytes.
xnor 9/09
1
@Jitse De fato, a área de superfície de uma esfera é igual à área de superfície lateral do cilindro anexo!
Neil
13

Oitava , 40 33 22 bytes

Amostramos de uma distribuição normal padrão 3d e normalizamos o vetor:

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

Experimente online!

flawr
fonte
Somente para o Octave (ou seja, não no MATLAB), você pode salvar um byte com isso
Tom Carpenter
1
@TomCarpenter Thanks! Neste caso, como é apenas uma expressão, podemos até omitir o disp:)
flawr 10/09
10

Unidade C # , 34 bytes

f=>UnityEngine.Random.onUnitSphere

O Unity tem um builtin para valores aleatórios da esfera unitária, então pensei em publicá-lo.

Draco18s
fonte
Bom uso de um construído em +1, você pode apenas enviar uma função para ser um pouco mais curtaf=>Random.onUnitSphere
LiefdeWen 10/09
@LiefdeWen Eu sabia sobre lambdas, só não tinha certeza se isso era suficiente (em termos de validade no Code Golf) porque não está declarando fo Tipo; usando varapenas funciona dentro de um método e System.Func<Vector3>era mais longo.
Draco18s 10/09
1
No retorno do codegolf, uma função é perfeitamente adequada e você não precisa contar a declaração, o que significa que você pode fazer coisas sorrateiras com parâmetros dinâmicos. Você também não conta o último ponto e vírgula. No entanto, você conta todas as instruções de uso adicionadas. portanto, sua contagem de bytes precisa incluir o uso. Mas f=>Random.onUnitSphereé uma submissão perfeitamente válida
LiefdeWen 10/09
@LiefdeWen Sim, eu simplesmente não tinha certeza de como a declaração foi tratada e não estava realmente disposto a "pesquisar meta".
Draco18s 10/09
f=>UnityEngine.Random.onUnitSpherevocê economiza ousing
Orace 11/09
6

MATL , 10 bytes

1&3Xrt2&|/

Experimente online!

Explicação

Isso usa a primeira abordagem descrita no desafio.

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
fonte
6

Ruby , 34 50 49 bytes

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

Experimente online!

Retorna uma matriz de 3 números [z,y,x].

xe ysão gerados pelo aumento i(raiz quadrada de -1) de uma potência aleatória entre 0 e 4. Esse número complexo precisa ser dimensionado adequadamente de acordo com o zvalor de acordo com o teorema de Pitágoras:(x**2 + y**2) + z**2 = 1.

A zcoordenada (que é gerada primeiro) é simplesmente um número uniformemente distribuído entre -1 e 1. Embora não seja imediatamente óbvio, dA / dz para uma fatia na esfera é constante (e igual ao perímetro de um círculo com o mesmo raio que toda a esfera).

Aparentemente, isso foi descoberto por Arquimedes, que o descreveu de uma maneira muito não-calculista, e é conhecido como teorema de Archimedes Hat-Box. Veja https://brilliant.org/wiki/surface-area-sphere/

Outra referência dos comentários sobre a resposta do xnor. Um URL surpreendentemente curto, descrevendo uma fórmula surpreendentemente simples: http://mathworld.wolfram.com/Zone.html

Level River St
fonte
@Jitse Eu esqueci de diminuir o tamanho de xe y em valores altos de z. Efetivamente, os pontos definiram um cilindro. Está consertado agora, mas custa muitos bytes! Eu poderia economizar alguns se a saída puder ser expressa com um número complexo, [z, x+yi]deixarei como está, a menos que você diga que está tudo bem.
Level River St
Parece bom! Eu realmente gosto dessa abordagem. Para manter a consistência, a saída necessária é de três flutuadores, então sugiro deixá-lo assim.
Jitse 9/09
Por que não usar em z*zvez de z**2?
Valor da tinta
@ ValueInk sim, obrigado, eu percebi que tinha perdido isso z*z. Eu editei agora. A outra coisa que eu poderia fazer é substituir rand*4por algo comoz*99 ou x*9E9(efetivamente limitando os valores possíveis a uma espiral muito fina na esfera), mas acho que isso reduz a qualidade do acaso.
Level River St
4

05AB1E , 23 22 bytes

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

Implementa o segundo algoritmo.

Experimente online ou obtenha mais algumas saídas aleatórias .

Explicação:

[0 0,1)0,000010,000000001 , alterando o5 para 9no código (embora se torne bastante lento ..).

[            # 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
fonte
1
eu<1eu1eux0 0<x1eu<0,51
@Jitse Ok, implementou a normalização nas respostas Java e 05AB1E. Espero que esteja tudo correto agora.
Kevin Cruijssen 9/09
v1v==1v<10 0<x1eu1
4

TI-BASIC, 15 bytes *

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

Usando o algoritmo "gere 3 valores normalmente distribuídos e normalize esse vetor".

O encerramento de um programa com uma expressão imprime automaticamente o resultado na Tela inicial após o término do programa, para que o resultado seja realmente mostrado, não apenas gerado e perfurado.

*: randNorm(é um token de dois bytes , o restante são tokens de um byte . Eu contei o inicial (inevitável) :, sem isso, seriam 14 bytes. Salvo como um programa com um nome de uma letra, são necessários 24 bytes de memória, que incluem os 9 bytes de sobrecarga do sistema de arquivos.

harold
fonte
3

JavaScript (ES7),  77 76  75 bytes

pecado(ϕ)=pecado(porque-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]

Experimente online!

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 o algoritmo.

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

Experimente online!

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
fonte
3

Processando 26 bytes

Programa completo

print(PVector.random3D());

Esta é a implementação 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
fonte
2
Convém deixar mais claro que a implementação não faz parte da sua contagem de bytes. Eu perdi na primeira leitura e depois dei uma olhada dupla.
Level River St
Eu gosto que a implementação use essencialmente a mesma abordagem que eu, embora
Level River St
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

Experimente online!

Implementa o primeiro algoritmo.


Python 2 , 107 103 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

Experimente online!

Implementa o segundo algoritmo.

TFeld
fonte
2
@RobinRyder Esta implementação rejeita vetores com um comprimento inicial> 1, que é válido conforme especificado no desafio.
Jitse 9/09
@Jitse Certo, desculpe. Eu li mal o 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]

Experimente online!

Faz três randoms uniformes e amostragem de rejeição.

Angs
fonte
Parece que seus randoms são da distribuição (0,1) em vez de (-1,1), de modo que apenas 1/8 da esfera é coberta.
Jitse 9/09
@ Jitse gotcha, obrigado por perceber.
Angs 9/09
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)

Você não precisa não inserir a.

Naruyoko
fonte
Uau, eu perdi completamente isso. Fixo.
Naruyoko 9/09
2

Julia 1.0 , 24 bytes

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

Experimente online!

Desenha um vetor de 3 valores, desenhado a partir de uma distribuição normal em torno de 0 com desvio padrão 1. Em seguida, apenas os normaliza.

user3263164
fonte
randn(), em alguns testes rápidos, não parece estar vinculado ao intervalo necessário. Além disso, isso não inclui uma verificação para hypot()retornar um valor >1, que deve ser rejeitado.
Shaggy
3
@ Shaggy parece randnsimular a partir de uma distribuição normal padrão, em vez de uma distribuição uniforme (0,1), portanto essa abordagem é idêntica à R.
Giuseppe
@ Giuseppe Sim, exatamente!
user3263164 10/09
@ Giuseppe, acho que posso não ter uma compreensão adequada da matemática por trás desse desafio, mas, se estou entendendo corretamente, você está dizendo que se algum dos carros alegóricos estiver fora dos limites da [-1,1)divisão por eles pelo hipotenusa, qual será >1, compensa isso? Isso me leva a pensar se o ternário da minha solução é necessário ...
Shaggy
@ Shaggy não, a distribuição normal / Gaussiana tem algumas propriedades (especificamente, invariância rotacional) que o uniforme não possui, veja este comentário , por exemplo
Giuseppe
2

MathGolf , 21 19 18 bytes

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

Implementação do 2º algoritmo.

Experimente online ou veja mais algumas saídas ao mesmo tempo .

Explicação:

{              }▲   # 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
fonte
2

Java 8 ( terceiro algoritmo modificado por @Arnauld ), 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;}

Porta da resposta JavaScript de @Arnauld , certifique-se de vomitá-lo!
-2 bytes graças a @ OlivierGrégoire .

Isso é implementado como:

k=N[-1,1)
t=1-k2
você=2π×(N[0 0,1))
x,y,z={k,porque(você)×t,pecado(você)×t}

Experimente online.

Implementação anterior do terceiro 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=N[-1,1)
t=2π×(N[0 0,1))
x,y,z={k,porque(t)×pecado(arccos(k)),pecado(t)×pecado(arccos(k))}

Experimente online.

Explicação:

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;}

Experimente online.

2º 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
fonte
Usar sqrt(1-k*k)realmente salva mais bytes em Java do que em JS. :)
Arnauld 9/09
@Arnauld Yep. Em vez de 3x M.sin, 1x M.cose 1x M.acos, sua abordagem usa 2x M.sine 1x M.sqrt, que é a origem dos bytes salvos adicionais. :)
Kevin Cruijssen 9/09
108 bytes Usa um 2º algoritmo modificado, onde apenas permito valores em que s == 1 (em vez de s <= 1 e normalizando). Às vezes, dá uma resposta, mas a maioria não acontece por causa do tempo limite. Edit: Opa, eu esqueci o resultado Math.sqrt
Olivier Grégoire
Na verdade, não, não há necessidade de sqrt porque sqrt (1) == 1. Então, eu permaneço com a minha sugestão de golfe.
Olivier Grégoire
1
109 bytes (você pode usar a saída da string em vez de double[]não alterar a contagem de bytes).
Olivier Grégoire
1

Japonês , 20 bytes

Implementação do 2º algoritmo de Port of Arnauld .

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

Teste-o

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
Shaggy
fonte
1

Pitão , 24 bytes

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

Experimente online!

Usa o 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
fonte
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

EDIT: Raspado alguns bytes, inlining Eu e j, substituindo o primeiro let ... inpor a fune aproveitando a associatividade do operador para evitar alguns parênteses ().

Experimente online


Solução 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])

Primeiro eu defino:

uma=arccos,  c=porque,  s=pecadoEuunificar(0 0,4),  junificar(0 0,2)

A Random.floatfunção do OCaml inclui os limites. Então,

t=Euuma(0 0)=Euπ2,  p=uma(j-1)

Isso é muito semelhante ao terceiro exemplo de implementação (com ϕ=p e θ=t) - exceto que eu escolho Eu e j em intervalos maiores para evitar a multiplicação (com 2) posteriormente.

Saswat Padhi
fonte
1
Não estou familiarizado com esse idioma, mas parece que você usa as flutuações aleatórias entre 0e 1diretamente como coordenadas esféricas. Isso está incorreto, como mostrado nas observações 3 e 4 do desafio, uma vez que você acaba com um viés em direção aos pólos da esfera. Você pode corrigir isso aplicando o método mostrado na observação 4.
Jitse 10/09
Obrigado! Perdeu totalmente isso. Corrigido o erro e atualizei minha resposta
Saswat Padhi 10/09
1
Parece bom! Muito boa primeira resposta!
Jitse 10/09
Obrigado :) Consegui reduzi-lo para menos de 100 bytes!
Saswat Padhi 10/09