Zufallszahlen mit fester Summe


32

Ihre Aufgabe ist es, ein Programm oder eine Funktion zu schreiben , die Zufallszahlen aus Intervall [0,1] mit fester Summe ausgibt .ns

Eingang

n, n≥1, Anzahl der zu generierenden Zufallszahlen

s, s>=0, s<=n, Summe der zu generierenden Zahlen

Ausgabe

Ein zufälliges nTupel von Gleitkommazahlen mit allen Elementen aus dem Intervall [0,1] und der Summe aller Elemente, die gleich sind s, wird auf eine bequeme, eindeutige Weise ausgegeben. Alle gültigen nTupel müssen innerhalb der Grenzen von Gleitkommazahlen gleich wahrscheinlich sein.

Dies ist gleichbedeutend mit einer gleichmäßigen Abtastung vom Schnittpunkt der Punkte innerhalb des nWürfels mit den Maßeinheiten und der n-1eindimensionalen Hyperebene, die durch (s/n, s/n, …, s/n)den Vektor verläuft und senkrecht zum Vektor verläuft (1, 1, …, 1)(siehe roter Bereich in Abbildung 1 für drei Beispiele).

Beispiele mit n = 3 und Summen 0,75, 1,75 und 2,75

Abbildung 1: Die Ebene der gültigen Ausgaben mit n = 3 und den Summen 0,75, 1,75 und 2,75

Beispiele

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]

Regeln

  • Ihr Programm sollte auf Ihrem Computer in weniger als einer Sekunde beendet sein, zumindest mit n≤10und allen gültigen s.
  • Wenn Sie möchten, kann Ihr Programm am oberen Ende exklusiv sein, dh s<nund die Ausgabenummern aus dem halboffenen Intervall [0,1) (unterbrechen des zweiten Beispiels)
  • Wenn Ihre Sprache keine Gleitkommazahlen unterstützt, können Sie die Ausgabe mit mindestens zehn Dezimalstellen nach dem Komma fälschen.
  • Standard-Regelungslücken sind nicht zulässig, und Standard-Eingabe- / Ausgabemethoden sind zulässig.
  • Das ist , also gewinnt der kürzeste Eintrag, gemessen in Bytes.


Wenn Sie sagen This is equal to uniformly sampling from the intersection- ich kann ein Programm sehen, das zufällig nur aus den Ecken dieser Kreuzung auswählt. Wäre das gültig?
JayCe

2
@ KevinCruijssen Nein, das gilt nur für s==0 or s==3. Für alle anderen Werte von shat die Ebene eine Fläche ungleich Null und Sie müssen einen Punkt auf dieser Ebene gleichmäßig zufällig auswählen.
user202729

3
Die Anforderung, dass das Intervall geschlossen oder halb geschlossen sein muss (im Gegensatz zu offen), ist eine theoretisch nicht beobachtbare Anforderung. Viele Zufallsgeneratoren geben die Ausgabe in (0,1) aus. Wie prüfe ich, ob das Ausgabeintervall [0,1] und nicht (0,1) beträgt? Der Wert 0 "nie" kommt sowieso vor
Luis Mendo

2
Ist es in Ordnung, wenn unser Code Ablehnungsstichproben verwendet, was für Testfälle wie z. B. sehr lange dauert s=2.99999999999, n=3? Können wir zufällige Reals in Vielfachen von beispielsweise generieren 1e-9?
xnor

Antworten:


1

Wolfram Language (Mathematica) , 92-90 Bytes

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

Probieren Sie es online!

Code ohne 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       *)

Hier ist eine Lösung, die in 55 Bytes funktioniert, aber im Moment (Mathematica Version 12) beschränkt ist auf, n=1,2,3weil es RandomPointabgelehnt wird, Punkte aus höherdimensionalen Hyperebenen zu zeichnen (in TIOs Version 11.3 schlägt dies ebenfalls fehl n=1). Es könnte jedoch nin Zukunft für höhere Versionen funktionieren :

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

Probieren Sie es online!

Code ohne 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 Bytes

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)

Probieren Sie es online!


  • -20 Bytes, danke an Kevin Cruijssen

@ LuisMendo Sollte jetzt behoben sein
TFeld

sie scheinen immer noch nicht einheitlich
14m2

@ l4m2 Ich bin g(4, 2.0)1.000 Mal gelaufen , um 4.000 Punkte zu bekommen, und die Ergebnisse sehen so aus, als wären sie ziemlich einheitlich.
Ingenieur Toast



4

Java 8, 194 188 196 237 236 Bytes

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-Bytes (188 → 196 und 196 → 237), um die Geschwindigkeit von Testfällen nahe 1 sowie den Algorithmus im Allgemeinen zu korrigieren.

Probieren Sie es online aus

Erläuterung:

Verwendet den Ansatz in dieser Stackoverflow Antwort , in einer Schleife solange eines des Elements ist noch größer als 1.
Auch wenn 2*s>n, swird in geändert werden n-s, und ein Flag gesetzt , um anzuzeigen , sollten wir verwenden , 1-diffanstatt diffim Ergebnis-Array (danke für den tipp @soktinpk und @ 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

Auszeit fürtest(10, 9.99);
l4m2

@ l4m2 Ja, habe das auch gleich mit bemerkt, 10, 9.0nachdem ich den n=10, s=9.999999999999Testfall bearbeitet habe. Ich bin mir nicht sicher, ob es einen Fix in Java gibt, während ich seine einheitliche Zufälligkeit beibehalte. Muss eine Weile darüber nachdenken. Im Moment bearbeite ich es so, dass es eine Zeitüberschreitung anzeigt.
Kevin Cruijssen

Wenn n-s<1Sie anrufen können f(n,n-s)jede Zahl über und Flip 1/2(dh ersetzen xmit 1-xtat) wie l4m2. Dies könnte das Problem bei Zahlen in der sNähe von lösen n.
Soktinpk

@soktinpk Danke für den Tipp. Es ist eigentlich s+s>nstatt n-s<1, aber als ich mir die anderen JavaScript-Antworten ansah, ergab es in der Tat Sinn. Alles ist jetzt behoben, einschließlich eines weiteren Fehlers, der noch vorhanden war. Bytes sind ziemlich gestiegen, aber jetzt funktioniert alles. Arbeitet den Byte-Countdown von hier aus ab. :)
Kevin Cruijssen

Ich kenne keinen allgemeinen Beweis, aber ich glaube, dieser Algorithmus funktioniert, weil ein N-dimensionaler Hyperwürfel in N N-dimensionale Hyperpyramiden geschnitten werden kann.
Neil


3

C ++ 11, 284 267 Bytes

-17 Bytes dank Zacharý
Verwendet C ++ - Zufallsbibliothek, Ausgabe auf der Standardausgabe

#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]<<' ';}

Um anzurufen, müssen Sie nur das tun:

g<2>(0.0);

Wobei der Schablonenparameter (hier 2) N ist und der tatsächliche Parameter (hier 0,0) S ist


Ich denke, Sie können das Leerzeichen zwischen <z>und entfernenu
Zacharý

Ich habe es weiter nach unten: 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]<<' ';}. Eine neue Zeile muss nicht das Trennzeichen zwischen Elementen sein
Zacharý

1
Vorschlagen loszuwerden dvollständig durch eine Änderung d=s/Nauf s/=NVorschlagen die zweite Schleife Nachbearbeitung for(z c;i<N;a[++i%N]-=c)a[i]+=c=u(e);statt for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}( man beachte die hinzugefügt %N, um das Programm berechnet die erste Zahl richtig zu machen)
Max Yekhlakov

2

Sauber , 221 201 Bytes

Clean, Code-Golf oder Zufallszahlen. Suche dir zwei aus.

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

Probieren Sie es online!

Teilfunktionsliteral :: (Int Real -> [Real]). Erzeugt nur einmal pro Sekunde neue Ergebnisse.
Genauigkeit bis zu mindestens 10 Dezimalstellen.


2

R , 99 Bytes (mit gtoolsPaket)

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}

Probieren Sie es online!

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 107 Bytes

-2 Bytes dank @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);}

Probieren Sie es online!


Leider entspricht diese Antwort nicht der Herausforderungsspezifikation. Die ausgegebenen Zufallszahlen sind nicht beschränkt [0,1]und ihre gemeinsame Verteilung ist nicht einheitlich.
Nitrodon

@Nitrodon hey, könntest du bitte einen Eingang angeben, für den der Ausgang nicht auf [0,1] beschränkt ist? Ich habe ein paar verschiedene Beispiele ausprobiert und sie schienen alle richtig zu sein, es sei denn, ich habe das Ziel falsch verstanden.
OverclockedSanic

Wenn der RNG-Status auf TIO eingestellt ist und Sie ihn beibehalten n=4, werden Werte s=3.23und s=0.89Ausgaben außerhalb des Bereichs ausgegeben. Genauer gesagt, die Verteilung von X-s/nsollte davon abhängen s, tut es aber nicht.
Nitrodon

@Nitrodon Hoppla, meine schlechte. Ich habe einige Teile aus der obigen C ++ - Antwort konvertiert und vergessen, etwas hinzuzufügen. Es sollte jetzt behoben werden? Auch ein paar Bytes in den Prozess golfen.
OverclockedSanic

1

Haskell , 122 217 208 Bytes

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

Probieren Sie es online!

Manchmal sind die Antworten aufgrund von Gleitkommafehlern etwas falsch. Wenn es ein Problem ist, kann ich es zu einem Preis von 1 Byte beheben. Ich bin mir auch nicht sicher, wie einheitlich das ist (ziemlich sicher, dass es in Ordnung ist, aber ich bin nicht so gut in so etwas), also beschreibe ich meinen Algorithmus.

Die Grundidee ist, eine Zahl zu generieren, sie xdann zu subtrahieren sund so lange zu wiederholen, bis wir nElemente haben, die wir dann mischen. Ich generiere xmit einer Obergrenze von 1 oder s(je nachdem, welcher Wert kleiner ist) und einer Untergrenze von s-n+1oder 0 (je nachdem, welcher Wert größer ist). Diese Untergrenze ist vorhanden, sodass sie bei der nächsten Iteration simmer noch kleiner oder gleich ist n(Herleitung: s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x).

EDIT: Danke an @ michi7x7 für den Hinweis auf einen Fehler in meiner Uniformität. Ich glaube, ich habe es durch Mischen behoben, aber lass es mich wissen, wenn es ein anderes Problem gibt

EDIT2: Verbesserte Byteanzahl plus feste Typeinschränkung


3
Die Verkettung einheitlicher Samples führt niemals zu einer gleichmäßigen Verteilung (die letzte Koordinate ist in Ihrem Beispiel fast immer größer als 0,99)
michi7x7

@ michi7x7 Ich verstehe deinen Standpunkt. Was ist, wenn ich die Reihenfolge der Liste nach dem Generieren gemischt habe? Ich hätte mehr Statistikkurse
belegen sollen

Die Zahlen sehen nicht sehr einheitlich aus. Hier sind 8 Ergebnisse> 0,99, 1 ist 0,96 und das letzte ist 0,8. So sieht es aus.
Stewie Griffin

@ user1472751 Hier gibt es mehrere gute Antworten: stackoverflow.com/q/8064629/6774250
michi7x7

1
Die Gleichmäßigkeit hat immer noch ein Problem, siehe hier - es wurden zu viele Nullen generiert (Plot der sortierten Werte von 1000% 500)
Angs

1

Haskell , 188 Bytes

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

Probieren Sie es online!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.