Nombres aléatoires à somme fixe


32

Votre tâche consiste à écrire un programme ou une fonction qui génère n des nombres aléatoires de l'intervalle [0,1] avec une somme fixe s.

Contribution

n, n≥1, nombre de nombres aléatoires à générer

s, s>=0, s<=n, somme des nombres à générer

Sortie

Un n-tuple aléatoire de nombres à virgule flottante avec tous les éléments de l'intervalle [0,1] et la somme de tous les éléments égaux à s, émis de toute manière pratique et sans ambiguïté. Tous les n-tuples valides doivent être également probables dans les limites des nombres à virgule flottante.

Cela équivaut à un échantillonnage uniforme à partir de l'intersection des points à l'intérieur du ncube d'unités dimensionnelles et de l' n-1hyperplan dimensionnel qui traverse (s/n, s/n, …, s/n)et est perpendiculaire au vecteur (1, 1, …, 1)(voir la zone rouge sur la figure 1 pour trois exemples).

Exemples avec n = 3 et des sommes de 0,75, 1,75 et 2,75

Figure 1: Le plan des sorties valides avec n = 3 et des sommes de 0,75, 1,75 et 2,75

Exemples

n=1, s=0.8 → [0.8]
n=3, s=3.0 → [1.0, 1.0, 1.0]
n=2, s=0.0 → [0.0, 0.0]
n=4, s=2.0 → [0.2509075946818119, 0.14887693388076845, 0.9449661625992032, 0.6552493088382167]
n=10, s=9.999999999999 → [0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999]

Règles

  • Votre programme doit se terminer en moins d'une seconde sur votre machine au moins avec n≤10et tout s valide.
  • Si vous le souhaitez, votre programme peut être exclusif sur l'extrémité supérieure, c'est-à s<n- dire et les numéros de sortie de l'intervalle semi-ouvert [0,1) (en cassant le deuxième exemple)
  • Si votre langue ne prend pas en charge les nombres à virgule flottante, vous pouvez simuler la sortie avec au moins dix chiffres décimaux après le point décimal.
  • Les failles standard sont interdites et les méthodes d'entrée / sortie standard sont autorisées.
  • Il s'agit de , donc l'entrée la plus courte, mesurée en octets, gagne.


Quand vous dites This is equal to uniformly sampling from the intersection- je peux voir un programme choisir aléatoirement à partir des coins de cette intersection. Serait-ce valable?
JayCe

2
@KevinCruijssen Non, ce n'est vrai que pour s==0 or s==3. Pour toutes les autres valeurs de s, le plan a une zone non nulle et vous devez choisir de manière uniforme et aléatoire un point sur ce plan.
user202729

3
Exiger que l'intervalle soit fermé ou semi-fermé (par opposition à ouvert) est une exigence théoriquement inobservable. De nombreux générateurs de nombres aléatoires donnent la sortie en (0,1). Comment tester que l'intervalle de sortie est [0,1) et non (0,1)? La valeur 0 "jamais" se produit quand même
Luis Mendo

2
Est-ce correct si notre code utilise l'échantillonnage de rejet, et prend donc très longtemps pour des cas de test comme s=2.99999999999, n=3? Pouvons-nous générer des réels aléatoires en multiples de, disons 1e-9?
xnor

Réponses:


1

Wolfram Language (Mathematica) , 92 90 octets

If[2#2>#,1-#0[#,#-#2],While[Max[v=Differences@Sort@Join[{0,#2},RandomReal[#2,#-1]]]>1];v]&

Essayez-le en ligne!

Code non golfé:

R[n_, s_] := Module[{v},
  If[s <= n/2,             (* rejection sampling for s <= n/2:                        *)
    While[
      v = Differences[Sort[
            Join[{0},RandomReal[s,n-1],{s}]]];         (* trial randoms that sum to s *)
      Max[v] > 1           (* loop until good solution found                          *)
    ];
    v,                     (* return the good solution                                *)
    1 - R[n, n - s]]]      (* for s > n/2, invert the cube and rejection-sample       *)

Voici une solution qui fonctionne en 55 octets mais pour l'instant (Mathematica version 12) est limitée à n=1,2,3parce qu'elle RandomPointrefuse de dessiner des points à partir d'hyperplans de dimension supérieure (dans la version 11.3 de TIO, elle échoue également n=1). Cela peut fonctionner pour plus nà l'avenir cependant:

RandomPoint[1&~Array~#~Hyperplane~#2,1,{0,1}&~Array~#]&

Essayez-le en ligne!

Code non golfé:

R[n_, s_] :=
  RandomPoint[                           (* draw a random point from *)
    Hyperplane[ConstantArray[1, n], s],  (* the hyperplane where the sum of coordinates is s *)
    1,                                   (* draw only one point *)
    ConstantArray[{0, 1}, n]]            (* restrict each coordinate to [0,1] *)


6

Python 2 , 144 128 119 119 octets

from random import*
def f(n,s):
 r=min(s,1);x=uniform(max(0,r-(r-s/n)*2),r);return n<2and[s]or sample([x]+f(n-1,s-x),n)

Essayez-le en ligne!


  • -20 octets, merci à Kevin Cruijssen

@LuisMendo devrait être corrigé maintenant
TFeld

ils semblent toujours pas uniformes
l4m2

@ l4m2 J'ai couru g(4, 2.0)1 000 fois pour obtenir 4 000 points et les résultats ressemblent à ceci qui semble assez uniforme.
Ingénieur Toast



4

Java 8, 194 188 188 196 237 236 octets

n->s->{double r[]=new double[n+1],d[]=new double[n],t;int f=0,i=n,x=2*s>n?1:0;for(r[n]=s=x>0?n-s:s;f<1;){for(f=1;i-->1;)r[i]=Math.random()*s;for(java.util.Arrays.sort(r);i<n;d[i++]=x>0?1-t:t)f=(t=Math.abs(r[i]-r[i+1]))>1?0:f;}return d;}

+49 octets (188 → 196 et 196 → 237) pour fixer la vitesse des cas de test près de 1, ainsi que corriger l'algorithme en général.

Essayez-le en ligne

Explication:

Utilise l'approche de cette réponse StackoverFlow , dans une boucle tant que l'un des éléments est toujours plus grand que 1.
De plus, si 2*s>n, ssera changé en n-s, et un indicateur est défini pour indiquer que nous devons utiliser à la 1-diffplace de diffdans le tableau de résultats (merci pour l'astuce @soktinpk et @ l4m2 ).

n->s->{              // Method with integer & double parameters and Object return-type
  double r[]=new double[n+1]
                     //  Double-array of random values of size `n+1`
         d[]=new double[n],
                     //  Resulting double-array of size `n`
         t;          //  Temp double
  int f=0,           //  Integer-flag (every item below 1), starting at 0
      i=n,           //  Index-integer, starting at `n`
      x=             //  Integer-flag (average below 0.5), starting at:
        2*s>n?       //   If two times `s` is larger than `n`:
         1           //    Set this flag to 1
        :            //   Else:
         0;          //    Set this flag to 0
  for(r[n]=s=        //  Set both the last item of `r` and `s` to:
       x>0?          //   If the flag `x` is 1:
        n-s          //    Set both to `n-s`
       :             //   Else:
        s;           //    Set both to `s`
      f<1;){         //  Loop as long as flag `f` is still 0
    for(f=1;         //   Reset the flag `f` to 1
        i-->1;)      //   Inner loop `i` in range (n,1] (skipping the first item)
      r[i]=Math.random()*s;
                     //    Set the i'th item in `r` to a random value in the range [0,s)
    for(java.util.Arrays.sort(r);
                     //   Sort the array `r` from lowest to highest
        i<n;         //   Inner loop `i` in the range [1,n)
        ;d[i++]=     //     After every iteration: Set the i'th item in `d` to:
          x>0?       //      If the flag `x` is 1:
           1-t       //       Set it to `1-t`
          :          //      Else:
           t)        //       Set it to `t`
      f=(t=Math.abs( //    Set `t` to the absolute difference of:
            r[i]-r[i+1])) 
                     //     The i'th & (i+1)'th items in `r`
        >1?          //    And if `t` is larger than 1 (out of the [0,1] boundary)
         0           //     Set the flag `f` to 0
        :            //    Else:
         f;}         //     Leave the flag `f` unchanged
  return d;}         //  Return the array `d` as result

Temps mort pourtest(10, 9.99);
l4m2

@ l4m2 Oui, j'ai remarqué la même chose 10, 9.0juste après avoir édité pour corriger le n=10, s=9.999999999999cas de test .. Je ne sais pas s'il y a un correctif en Java tout en conservant son uniformité aléatoire .. Il faudra y penser pendant un certain temps. Pour l'instant, je vais le modifier pour qu'il expire.
Kevin Cruijssen

Si n-s<1vous pouvez appeler f(n,n-s)et retourner chaque numéro 1/2(c'est-à-dire le remplacer xpar 1-x) comme l4m2 l'a fait. Cela pourrait résoudre le problème des nombres sproches n.
soktinpk

@soktinpk Merci pour l'astuce. C'est en fait s+s>nau lieu de n-s<1, mais quand j'ai regardé les autres réponses JavaScript, cela avait effectivement du sens. Tout est maintenant corrigé, y compris un autre bug qui était toujours présent. Les octets ont augmenté un peu, mais tout fonctionne maintenant. Fonctionnera le décompte d'octets à partir d'ici. :)
Kevin Cruijssen

Je ne connais pas de preuve générale mais je crois que cet algorithme fonctionne car un hypercube à N dimensions peut être découpé en N hyperpyramides à N dimensions.
Neil


3

C ++ 11, 284 267 octets

-17 octets grâce à Zacharý
Utilise la bibliothèque aléatoire C ++, sortie sur la sortie standard

#include<iostream>
#include<random>
typedef float z;template<int N>void g(z s){z a[N],d=s/N;int i=N;for(;i;)a[--i]=d;std::uniform_real_distribution<z>u(.0,d<.5?d:1-d);std::default_random_engine e;for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}for(;i;)std::cout<<a[--i]<<' ';}

Pour appeler, il vous suffit de le faire:

g<2>(0.0);

Où le paramètre de modèle (ici, 2) est N, et le paramètre réel (ici, 0,0) est S


Je pense que vous pouvez supprimer l'espace entre <z>etu
Zacharý

Je l'ai obtenu plus bas: typedef float z;template<int N>void g(z s){z a[N],d=s/N;int i=N;for(;i;)a[--i]=d;std::uniform_real_distribution<z>u(.0,d<.5?d:1-d);std::default_random_engine e;for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}for(;i;)std::cout<<a[--i]<<' ';}. Une nouvelle ligne ne doit pas nécessairement être le séparateur entre les éléments
Zacharý

1
Suggérez de vous débarrasser dcomplètement en changeant d=s/Nen s/=NSuggérez de retravailler la deuxième boucle for(z c;i<N;a[++i%N]-=c)a[i]+=c=u(e);au lieu de for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}(notez l'ajout %Nafin que le programme calcule correctement le premier nombre)
Max Yekhlakov

2

Nettoyer , 221 201 octets

Chiffres propres, codés au golf ou aléatoires. Choisis en deux.

import StdEnv,Math.Random,System._Unsafe,System.Time
g l n s#k=toReal n
|s/k>0.5=[s/k-e\\e<-g l n(k-s)]
#r=take n l
#r=[e*s/sum r\\e<-r]
|all((>)1.0)r=r=g(tl l)n s

g(genRandReal(toInt(accUnsafe time)))

Essayez-le en ligne!

Littéral de fonction partielle :: (Int Real -> [Real]). Ne produira de nouveaux résultats qu'une fois par seconde.
Précisez jusqu'à au moins 10 décimales.


2

R , 99 octets (avec gtoolspackage)

f=function(n,s){if(s>n/2)return(1-f(n,n-s))
while(any(T>=1)){T=gtools::rdirichlet(1,rep(1,n))*s}
T}

Essayez-le en ligne!

A~={w1,,wn:i,0<wi<1;wi=s}wisA={w1,,wn:i,0<wi<1s;wi=1}

s=1Dirichlet(1,1,,1) s1<1/ss

s>n/2


2

C, 132 127 125 118 110 110 107 octets

-2 octets grâce à @ceilingcat

i;f(s,n,o,d)float*o,s,d;{for(i=n;i;o[--i]=d=s/n);for(;i<n;o[++i%n]-=s)o[i]+=s=(d<.5?d:1-d)*rand()/(1<<31);}

Essayez-le en ligne!


Malheureusement, cette réponse ne répond pas aux spécifications du défi. Les nombres aléatoires de sortie ne sont pas limités à [0,1], et leur distribution conjointe n'est pas uniforme.
Nitrodon

@Nitrodon hey, pourriez-vous s'il vous plaît fournir une entrée pour laquelle la sortie n'est pas limitée à [0,1]? J'ai essayé quelques exemples différents et ils semblaient tous être corrects, à moins que j'aie mal compris l'objectif.
OverclockedSanic

Avec l'état RNG sur TIO, et en gardant votre n=4, les valeurs s=3.23et s=0.89donne des sorties en dehors de la plage. Plus précisément, la distribution de X-s/ndevrait dépendre s, mais ce n'est pas le cas.
Nitrodon

@Nitrodon Oups, ma mauvaise. Je convertissais certaines parties de la réponse C ++ ci-dessus et j'ai oublié d'ajouter quelque chose. Il devrait être corrigé maintenant? Golfé également quelques octets dans le processus.
OverclockedSanic

1

Haskell , 122 217 208 octets

import System.Random
r p=randomR p
(n#s)g|n<1=[]|(x,q)<-r(max 0$s-n+1,min 1 s)g=x:((n-1)#(s-x)$q)
g![]=[]
g!a|(i,q)<-r(0,length a-1)g=a!!i:q![x|(j,x)<-zip[0..]a,i/=j]
n%s=uncurry(!).(n#s<$>).split<$>newStdGen

Essayez-le en ligne!

Parfois, les réponses sont légèrement décalées en raison, je suppose, d'une erreur en virgule flottante. Si c'est un problème, je peux le résoudre au prix d'un octet. Je ne sais pas non plus à quel point c'est uniforme (je suis sûr que c'est bien mais je ne suis pas très bon dans ce genre de chose), donc je vais décrire mon algorithme.

L'idée de base est de générer un nombre, xpuis de le soustraire set de revenir jusqu'à ce que nous ayons des néléments, puis de les mélanger. Je génère xavec une limite supérieure de 1 ou s(la plus petite des deux) et une limite inférieure de s-n+1ou 0 (la plus grande des deux). Cette borne inférieure est là de sorte que lors de la prochaine itération ssera toujours inférieure ou égale à n(dérivation: s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x).

EDIT: Merci à @ michi7x7 d'avoir signalé une faille dans mon uniformité. Je pense que je l'ai corrigé en mélangeant mais faites-moi savoir s'il y a un autre problème

EDIT2: nombre d'octets amélioré et restriction de type fixe


3
Le chaînage d'échantillons uniformes ne conduira jamais à une distribution uniforme (la dernière coordonnée est presque toujours supérieure à 0,99 dans votre exemple)
michi7x7

@ michi7x7 Je vois votre point. Que faire si j'ai mélangé l'ordre de la liste après l'avoir générée? J'aurais dû prendre plus de cours de statistiques
user1472751

Les chiffres ne semblent pas très uniformes. Ici , 8 résultats sont> 0,99, 1 est 0,96 et le dernier est 0,8. Voilà à quoi ça ressemble.
Stewie Griffin

@ user1472751 il y a plusieurs bonnes réponses ici: stackoverflow.com/q/8064629/6774250
michi7x7

1
L'uniformité a toujours un problème, voir ici - il y a trop de zéros générés (tracé de valeurs triées de 1000% 500)
Angs

1

Haskell , 188 octets

import System.Random
import Data.List
n!s|s>n/2=map(1-)<$>n!(n-s)|1>0=(zipWith(-)=<<tail).sort.map(*s).(++[0,1::Double])<$>mapM(\a->randomIO)[2..n]>>= \a->if all(<=1)a then pure a else n!s

Ungolfed:

n!s
 |s>n/2       = map (1-) <$> n!(n-s)       --If total more than half the # of numbers, mirror calculation 
 |otherwise   = (zipWith(-)=<<tail)        --Calculate interval lengths between consecutive numbers
              . sort                       --Sort
              . map(*s)                    --Scale
              . (++[0,1::Double])          --Add endpoints
              <$> mapM(\a->randomIO)[2..n] --Calculate n-1 random numbers
              >>= \a->if all(<=1)a then pure a else n!s   --Retry if a number was too large

Essayez-le en ligne!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.