Point aléatoire sur une sphère

31

Le défi

Écrivez un programme ou une fonction qui ne prend aucune entrée et génère un vecteur de longueur 1 dans une direction aléatoire théoriquement uniforme .

Cela équivaut à un point aléatoire sur la sphère décrit par

x2+y2+z2=1

résultant en une distribution comme celle-ci

Distribution aléatoire de points sur une sphère de rayon 1.

Sortie

Trois flotteurs d'une distribution aléatoire théoriquement uniforme pour laquelle l'équation x2+y2+z2=1 est fidèle aux limites de précision.

Remarques du challenge

  • La distribution aléatoire doit être théoriquement uniforme . Autrement dit, si le générateur de nombres pseudo-aléatoires devait être remplacé par un vrai RNG à partir des nombres réels, il en résulterait une distribution aléatoire uniforme des points sur la sphère.
  • Générer trois nombres aléatoires à partir d'une distribution uniforme et les normaliser n'est pas valide: il y aura un biais vers les coins de l'espace tridimensionnel.
  • De même, générer deux nombres aléatoires à partir d'une distribution uniforme et les utiliser comme coordonnées sphériques n'est pas valide: il y aura un biais vers les pôles de la sphère.
  • Une bonne uniformité peut être obtenue par des algorithmes comprenant, mais sans s'y limiter:
    • Générez trois nombres aléatoires x , y et z partir d'une distribution normale (gaussienne) autour de 0 et normalisez-les.
    • Générez trois nombres aléatoires x , y et z partir d'une distribution uniforme dans la plage (1,1) . Calculez la longueur du vecteur par l=x2+y2+z2 . Ensuite, sil>1, rejetez le vecteur et générez un nouvel ensemble de nombres. Sinon, sil1, normalisez le vecteur et retournez le résultat.
    • Générez deux nombres aléatoires i et j partir d'une distribution uniforme dans la plage (0,1) et convertissez-les en coordonnées sphériques comme suit:
      θ=2×π×iϕ=cos1(2×j1)
      pour quex,yetzpuissent être calculés par
      x=cos(θ)×sin(ϕ)y=sin(θ)×sin(ϕ)z=cos(ϕ)
  • Fournissez dans votre réponse une brève description de l'algorithme que vous utilisez.
  • En savoir plus sur la sélection de points de sphère sur MathWorld .

Exemples de sortie

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

Remarques générales

Jitse
la source
Est-il acceptable de choisir 3 réels uniformément dans [-1, 1], puis de les rejeter (et de répéter) si la somme de leurs carrés n'est pas 1?
Grimmy
6
@Grimy J'aime cette faille. Non, ce n'est pas autorisé, car il n'y a théoriquement aucune chance de sortie.
Jitse
La suggestion de @ Grimy n'est-elle pas similaire au deuxième exemple d'implémentation que vous avez mentionné? Cette solution n'a également théoriquement aucune chance de produire une sortie
Saswat Padhi
2
@SaswatPadhi Non, cela a une chance pi/6 ≈ 0.5236de produire une sortie. C'est l'aire de la sphère inscrite dans le cube unité-surface
Luis Mendo
1
@LuisMendo je vois, à droite. La probabilité est de ~ 0,5 dans ce cas, comme vous l'avez mentionné. Pour la proposition de Grimy, c'est ~ 0.
Saswat Padhi

Réponses:

24

R , 23 octets

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

Essayez-le en ligne!

Génère 3 réalisations de la distribution N(0,1) et normalise le vecteur résultant.

Parcelle de 1000 réalisations:

entrez la description de l'image ici

Robin Ryder
la source
2
Pouvez-vous justifier 3 axes distribués normalement résultant en une distribution uniforme sur la sphère? (Je ne le vois pas)
Jeffrey soutient Monica le
4
X,YN(0,1)fX(x)=Ke12x2fY(y)=Ke12y2fXY(x,y)=K2e12(x2+y2)=fZ(z)=K2e12z2z=(x,y)zz
1
Ainsi, la distribution normale nous donne des points uniformément répartis autour du cercle, et la division par la magnitude garantit que les points se trouvent sur le cercle
Giuseppe
23

Code machine x86-64 - 63 62 55 49 octets

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  

Utilise le deuxième algorithme, modifié. Renvoie le vecteur de [x, y, z, 0]dans xmm0.

Explication:

push 4Fh
push 3f800000h

Pousse la valeur de 1 et 2 ^ 31 en tant que flottant à la pile. Les données se chevauchent en raison de l'extension de signe, économisant quelques octets.

vbroadcastss xmm1,dword ptr [rsp+5] Charge la valeur de 2 ^ 31 dans 4 positions de xmm1.

rdrand      eax  
jnc         rand  
movd        xmm0,eax

Génère un entier 32 bits aléatoire et le charge au bas de xmm0.

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

Génère un entier aléatoire de 32 bits, le convertit en float (signé) et divise par 2 ^ 31 pour obtenir des nombres entre -1 et 1.

vdpps xmm2,xmm0,xmm0,7Fhajoute les carrés des 3 flotteurs inférieurs en utilisant lui-même un produit scalaire, masquant le flotteur supérieur. Cela donne la longueur

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

Compare la longueur au carré avec 1 et rejette les valeurs si elle n'est pas égale à 1. Si la longueur au carré est un, alors la longueur est également un. Cela signifie que le vecteur est déjà normalisé et enregistre une racine carrée et divise.

pop         rax  
pop         rax 

Restaurez la pile.

ret renvoie la valeur en xmm0

Essayez-le en ligne .

moi'
la source
7
+1 Utiliser aesencpour produire 128 bits "aléatoires" est tout simplement magnifique.
DocMax
13

Python 2 , 86 octets

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

Essayez-le en ligne!

Génère uniformément la coordonnée z de -1 à 1. Ensuite, les coordonnées x et y sont échantillonnées uniformément sur un cercle de rayon (1-z*z)**.5.

Il n'est peut-être pas évident que la distribution sphérique soit en facteur uniforme sur la coordonnée z (et donc sur chaque coordonnée). C'est quelque chose de spécial pour la dimension 3. Voyez cette preuve que la surface d'une tranche horizontale d'une sphère est proportionnelle à sa hauteur. Bien que les tranches près de l'équateur aient un rayon plus grand, les tranches près du pôle sont plus orientées vers l'intérieur et il s'avère que ces deux effets s'annulent exactement.

Pour générer un angle aléatoire sur ce cercle, nous élevons l'unité imaginaire 1jà une puissance uniformément aléatoire entre 0 et 4, ce qui nous évite d'avoir besoin de fonctions trigonométriques, pi ou e, dont chacune aurait besoin d'une importation. Nous extrayons ensuite la partie réelle imaginaire. Si nous pouvons sortir un nombre complexe pour deux des coordonnées, la dernière ligne pourrait être print a,z.


86 octets

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

Essayez-le en ligne!

Génère trois normales et met le résultat à l'échelle.


Python 2 avec numpy, 57 octets

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

Essayez-le en ligne!

sum(a*a)**.5est plus court que linalg.norm(a). On pourrait aussi faire dot(a,a)pour la même durée que sum(a*a). En Python 3, cela peut être raccourci en a@autilisant le nouvel opérateur @.

Xnor
la source
1
J'aime ta première approche. J'ai du mal à comprendre comment un biais vers l'équateur est évité si z, à partir d'une distribution uniforme, n'est pas modifié.
Jitse
2
@Jitse La distribution sphérique est en facteur uniforme sur chaque coordonnée. C'est quelque chose de spécial pour la dimension 3. Voir par exemple cette preuve que la surface d'une tranche de sphère est proportionnelle à sa hauteur. En ce qui concerne l'intuition selon laquelle cela est biaisé vers l'équateur, notez que tandis que les tranches près de l'équateur ont un rayon plus grand, celles près du pôle sont plus vers l'intérieur, ce qui donne plus de surface, et il s'avère que ces deux effets s'annulent exactement.
xnor
Très agréable! Merci pour la clarification et la référence.
Jitse
@Jitse Merci, je l'ai ajouté au corps. Cependant, je me suis rendu compte que je n'échantillonnais que positivement z, et je l'ai corrigé pour quelques octets.
xnor
1
@Jitse En effet, la surface d'une sphère est égale à la surface latérale du cylindre englobant!
Neil
13

Octave , 40 33 22 octets

Nous échantillonnons une distribution normale standard 3D et normalisons le vecteur:

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

Essayez-le en ligne!

flawr
la source
Pour Octave uniquement (c'est-à-dire pas MATLAB), vous pouvez enregistrer un octet avec cela
Tom Carpenter
1
@TomCarpenter Merci! Dans ce cas, car ce n'est qu'une expression, nous pouvons même omettre le disp:)
flawr
10

Unity C # , 34 octets

f=>UnityEngine.Random.onUnitSphere

Unity a une fonction intégrée pour les valeurs aléatoires de la sphère unitaire, j'ai donc pensé la publier.

Draco18s
la source
Bonne utilisation d'un +1 intégré, vous pouvez simplement soumettre une fonction pour être un peu plus courtef=>Random.onUnitSphere
LiefdeWen
@LiefdeWen Je connaissais les lambdas, je ne savais tout simplement pas si c'était suffisant (en termes de validité sur Code Golf) car il ne déclare pas fle type de; l'utilisation varne fonctionne qu'à l'intérieur d'une méthode et System.Func<Vector3>était plus longue.
Draco18s
1
Dans codegolf, le retour d'une fonction est parfaitement correct, et vous n'avez pas non plus à compter la déclaration, ce qui signifie que vous pouvez faire des choses sournoises avec des paramètres dynamiques. Vous ne comptez pas non plus le dernier point-virgule. Vous comptez toutefois toutes les instructions que vous ajoutez. donc votre nombre d'octets doit inclure l'utilisation. Mais f=>Random.onUnitSphereest une soumission parfaitement valable
LiefdeWen
@LiefdeWen Ouais, je ne savais tout simplement pas comment la déclaration a été gérée et je ne me sentais pas vraiment à la recherche de méta.
Draco18s
f=>UnityEngine.Random.onUnitSpherevous sauve leusing
Orace
6

MATL , 10 octets

1&3Xrt2&|/

Essayez-le en ligne!

Explication

Cela utilise la première approche décrite dans le défi.

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
la source
6

Rubis , 34 50 49 octets

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

Essayez-le en ligne!

Renvoie un tableau de 3 nombres [z,y,x].

xet ysont générés en augmentant i(racine carrée de -1) à une puissance aléatoire entre 0 et 4. Ce nombre complexe doit être mis à l'échelle de manière appropriée en fonction de la zvaleur conformément au théorème de Pythagore:(x**2 + y**2) + z**2 = 1.

La zcoordonnée (qui est générée en premier) est simplement un nombre uniformément réparti entre -1 et 1. Bien que cela ne soit pas immédiatement évident, dA / dz pour une tranche à travers une sphère est constant (et égal au périmètre d'un cercle de même rayon que toute la sphère.).

Cela a apparemment été découvert par Archimède qui l'a décrit d'une manière très peu semblable au calcul, et il est connu sous le nom de théorème d'Archimède Hat-Box. Voir https://brilliant.org/wiki/surface-area-sphere/

Une autre référence des commentaires sur la réponse de xnor. Une URL étonnamment courte, décrivant une formule étonnamment simple: http://mathworld.wolfram.com/Zone.html

Level River St
la source
@Jitse J'ai oublié de réduire l'échelle x et y à des valeurs élevées de z. En effet, les points définissaient un cylindre. C'est réparé maintenant mais ça coûte beaucoup d'octets! Je pourrais en enregistrer quelques-uns si la sortie peut être exprimée avec un nombre complexe. [z, x+yi]Je vais le laisser tel quel, sauf si vous dites que c'est OK.
Level River St
Cela semble bon! J'aime vraiment cette approche. Par souci de cohérence, la sortie requise est de trois flottants, donc je suggère de le laisser comme ça.
Jitse
Pourquoi ne pas utiliser à la z*zplace de z**2?
Value Ink
@ValueInk ouais merci j'ai réalisé que j'avais raté ça z*z. Je l'ai édité maintenant. L'autre chose que je pourrais faire est de remplacer rand*4par quelque chose comme z*99ou x*9E9(limitant effectivement les valeurs possibles à une spirale très fine sur la sphère) mais je pense que cela réduit la qualité du hasard.
Level River St
4

05AB1E , 23 22 octets

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

Implémente le 2ème algorithme.

Essayez-le en ligne ou obtenez quelques sorties aléatoires supplémentaires .

Explication:

REMARQUE: 05AB1E n'a pas de fonction intégrée pour obtenir une valeur décimale aléatoire dans la plage . Au lieu de cela, je crée une liste par incréments de et je choisis des valeurs aléatoires dans cette liste. Cette augmentation pourrait être modifiée pour en changeant le pour dans le code (bien qu'il deviendrait plutôt lent ..).[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
la source
1
L'utilisation de est tout aussi valide que . Le seul critère pour est que . Vous pouvez tout aussi bien accepter des vecteurs avec si cela permet d'économiser des octets. Toute valeur inférieure ou égale à supprime le biais. l<1l1lx0<x1l<0.51
Jitse
@Jitse Ok, implémenté la normalisation dans mes réponses Java et 05AB1E. J'espère que tout va bien maintenant.
Kevin Cruijssen
@Jitse A effectivement enregistré un octet en vérifiant comme , au lieu de . Mais merci pour la clarification que seulement est une exigence, et il n'y a pas d' exigence stricte sur , tant que c'est . v1v==1v<10<x1l1
Kevin Cruijssen
4

TI-BASIC, 15 octets *

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

En utilisant l'algorithme "générer 3 valeurs normalement distribuées et normaliser ce vecteur".

La fin d'un programme avec une expression imprime automatiquement le résultat sur l'écran d'accueil une fois le programme terminé, de sorte que le résultat est réellement affiché, pas seulement généré et noirci.

*: randNorm(est un jeton de deux octets , les autres sont des jetons d'un octet . J'ai compté l'initiale (inévitable) :, sans que ce soit 14 octets. Enregistré en tant que programme avec un nom à une lettre, il prend 24 octets de mémoire, ce qui inclut les 9 octets de surcharge du système de fichiers.

Harold
la source
3

JavaScript (ES7),  77 76  75 octets

Implémente le 3 ème algorithme, en utilisant .sin(ϕ)=sin(cos1(z))=1z2

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

Essayez-le en ligne!

Commenté

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 octets

Implémente le 2 ème algorithme.

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

Essayez-le en ligne!

Commenté

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
la source
3

Traitement de 26 octets

Programme complet

print(PVector.random3D());

Il s'agit de la mise en œuvre 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
la source
2
Vous voudrez peut-être préciser que l'implémentation ne fait pas partie de votre nombre d'octets. Je l'ai raté en première lecture, puis j'ai fait une double prise.
Level River St
J'aime que la mise en œuvre utilise essentiellement la même approche que moi, cependant
Level River St
2

Python 2 , 86 octets

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

Essayez-le en ligne!

Implémente le premier algorithme.


Python 2 , 107 103 octets

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

Essayez-le en ligne!

Implémente le deuxième algorithme.

TFeld
la source
2
@RobinRyder Cette implémentation rejette les vecteurs avec une longueur initiale> 1, qui est valide comme spécifié dans le défi.
Jitse
@Jitse Bien, désolé. J'ai mal lu le code.
Robin Ryder
2

Haskell , 125 123 119 118 octets

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

Essayez-le en ligne!

Fait trois uniformes aléatoires et échantillonnage de rejet.

Angs
la source
Il semble que vos aléas proviennent de la distribution (0,1) au lieu de (-1,1), de sorte que seulement 1/8 de la sphère est couverte.
Jitse
@Jitse gotcha, merci de l'avoir remarqué.
Angs
2

JavaScript, 95 octets

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)

Vous n'avez pas besoin de ne pas entrer a.

Naruyoko
la source
Wow, j'ai complètement raté ça. Fixé.
Naruyoko
2

Julia 1.0 , 24 octets

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

Essayez-le en ligne!

Dessine un vecteur de 3 valeurs, tiré d'une distribution normale autour de 0 avec l'écart-type 1. Il suffit ensuite de les normaliser.

utilisateur3263164
la source
randn(), à partir de quelques tests rapides, ne semble pas lié à la plage requise. En outre, cela n'inclut pas une vérification pour hypot()renvoyer une valeur >1, qui doit être rejetée.
Shaggy
3
@Shaggy il semblerait randnsimuler à partir d'une distribution normale standard plutôt que d'une distribution uniforme (0,1), donc cette approche est identique à celle de R.
Giuseppe
@Giuseppe Oui, exactement!
user3263164
@ Giuseppe, je pense que je n'ai peut-être pas une bonne compréhension des mathématiques derrière ce défi, mais, si je vous comprends bien, vous dites que si l'un des flotteurs est en dehors des limites de la [-1,1)division par eux par le hypoténuse, qui sera >1, compense cela? Cela m'amène à me demander si le ternaire dans ma solution est nécessaire ...
Shaggy
@Shaggy non, la distribution normale / gaussienne a certaines propriétés (en particulier, l'invariance rotationnelle) que l'uniforme n'a pas, voir ce commentaire , par exemple
Giuseppe
2

MathGolf , 21 19 18 octets

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

Implémentation du 2ème algorithme.

Essayez-le en ligne ou voyez quelques sorties supplémentaires en même temps .

Explication:

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

Java 8 ( 3e algorithme modifié @Arnauld ), 131 126 119 111 111 109 octets

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

Port de la réponse JavaScript de @Arnauld , alors assurez-vous de voter pour lui!
-2 octets grâce à @ OlivierGrégoire .

Ceci est implémenté comme:

k=N[1,1)
t=1k2
u=2π×(N[0,1))
x,y,z={k,cos(u)×t,sin(u)×t}

Essayez-le en ligne.

Ancienne implémentation du 3ème algorithme ( 131 126 119 octets):

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

Mis en œuvre en tant que:

k=N[1,1)
t=2π×(N[0,1))
x,y,z={k,cos(t)×sin(arccos(k)),sin(t)×sin(arccos(k))}

Essayez-le en ligne.

Explication:

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 (2ème algorithme), 153 143 octets

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

Essayez-le en ligne.

2ème algorithme:

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
la source
L'utilisation sqrt(1-k*k)économise en fait plus d'octets en Java qu'en JS. :)
Arnauld
@Arnauld Yep. Au lieu de 3x M.sin, 1x M.coset 1x M.acos, votre approche utilise 2x M.sinet 1x M.sqrt, d'où proviennent principalement les octets enregistrés supplémentaires. :)
Kevin Cruijssen
108 octets Utilise un 2ème algorithme modifié où je n'autorise que les valeurs où s == 1 (au lieu de s <= 1 puis normalisant). Il donne parfois une réponse, mais ne le fait généralement pas à cause du délai d'attente. Edit: Oups, j'ai oublié de Math.sqrt le résultat
Olivier Grégoire
En fait, non, pas besoin de sqrt car sqrt (1) == 1. Je maintiens donc ma suggestion de golf.
Olivier Grégoire
1
109 octets (vous pouvez utiliser votre sortie de chaîne au lieu de double[]car cela ne change pas le nombre d'octets.)
Olivier Grégoire
1

Japt , 20 octets

Port d' implémentation d' Arnauld du 2ème algorithme.

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

Essaye-le

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
Hirsute
la source
1

Pyth , 24 octets

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

Essayez-le en ligne!

Utilise l'algorithme # 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
la source
1

OCaml , 110 99 95 octets

(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: a réduit certains octets en insérant et , en remplaçant le premier par a , et en profitant de l'associativité des opérateurs pour éviter certaines parens .ijlet ... infun()

Essayez-le en ligne


Solution originale:

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

Je définis d'abord:

a=arccos,  c=cos,  s=siniunif(0,4),  junif(0,2)

La Random.floatfonction d'OCaml inclut les limites. Ensuite,

t=ia(0)=iπ2,  p=a(j1)

Ceci est très similaire à l'implémentation du 3ème exemple (avec et ) sauf que je choisis et dans des intervalles plus grands pour éviter la multiplication (avec 2) plus tard.ϕ=pθ=tij

Saswat Padhi
la source
1
Je ne connais pas très bien ce langage, mais il semble que vous utilisiez les flotteurs aléatoires entre 0et 1directement comme coordonnées sphériques. Ceci est incorrect, comme le montrent les remarques 3 et 4, car vous vous retrouvez avec un biais vers les pôles de la sphère. Vous pouvez corriger cela en appliquant la méthode indiquée dans la remarque 4.
Jitse
Merci! Ça m'a complètement manqué. Correction du bug et mise à jour de ma réponse
Saswat Padhi
1
Cela semble bon! Très belle première réponse!
Jitse
Merci :) J'ai pu le réduire à moins de 100 octets!
Saswat Padhi,