Come generare numeri casuali con una distribuzione in pendenza (negativa)?

1

Dato un valore intero massimo M, voglio generare N valori interi che sono distribuiti con una frequenza più bassa quando ci si avvicina a M (preferibilmente M dovrebbe avere ancora una probabilità diversa da zero). Non mi interessa molto della funzione di probabilità, assumiamo (metà) una distribuzione normale.

Come potrei farlo se non voglio mantenere una cronologia?

per i: = 1 a N fa GetNextTestValue (M)

Che aspetto potrebbe avere GetNextTestValue?
FWIW Sto facendo questo in Delphi

    
posta Jan Doggen 04.06.2013 - 10:23
fonte

3 risposte

10

Compilazione di commento del GrandmasterB sulla seconda scelta e seleziona la più bassa, ho scritto un rapido script perl per tracciare i numeri casuali e vedere le distribuzioni - Scegli la più bassa delle due fornisce una distribuzione, ma altre forniscono altre distribuzioni ...

Innanzitutto, il codice per vedere come funziona e riprodurre i risultati se lo desideri.

#!/usr/bin/perl

use strict;
my $count = shift @ARGV;
my $nth = shift @ARGV;

my @nums = ();

for(my $i = 0; $i < 10_000; $i++) {
    my @rnd;
    for(my $j = 0; $j < $count; $j++) {
        push @rnd, int(rand(100));
    }
    @rnd = sort { $a <=> $b } @rnd;
    $nums[$rnd[$nth]]++;
}

for(my $i = 0; $i < 100; $i++) {
    print "$i\t" . (defined $nums[$i] ? $nums[$i] : 0) . "\n";
}

Il prelievo min(rand(100),rand(100)) produce una distribuzione lineare in pendenza verso il basso.

Semin(rand(100),rand(100),rand(100)),puoivedereunpo'diunacurva.

Esplorando questo ancora un po '... Scegliere il numero medio di 7 produce qualcosa che assomiglia più ad una bella curva a campana.

Mentreil2°numerodi7produceunostorto.

Quindi, da questo e dall'applicazione di alcune statistiche per decidere quale forma vuoi (o buttare fuori campioni che non soddisfano l'intervallo desiderato per ottenere la forma e l'inclinazione desiderate)

    
risposta data 06.06.2013 - 02:16
fonte
1

Ho trovato una soluzione.

Il blog di Controul di Hristo Dachev descrive diversi metodi, di cui Ho implementato il Marsaglia polar method .
Il codice Delphi per un'implementazione rapida e sporca con una distribuzione normale standard (moltiplicata per contare i valori integer) è inferiore.

Il blog fa riferimento al articolo di Wikipedia che ha uno pseudo codice per qualsiasi media e deviazione standard.

Distribuzione di esempio generata dal programma (10000 punti):

File.pasDelphiXE2:

unituMarsaglia;//Basedonhttp://blog.controul.com/2009/04/standard-normal-distribution-in-as3///Referstohttp://en.wikipedia.org/wiki/Marsaglia_polar_methodinterfaceusesWinapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,System.Math,Vcl.Controls,Vcl.Forms,Vcl.Dialogs,VCLTee.TeEngine,Vcl.ExtCtrls,VCLTee.TeeProcs,VCLTee.Chart,Vcl.StdCtrls,VCLTee.Series;typeTFrmMarsaglia=class(TForm)Chart1:TChart;Series1:TBarSeries;procedureFormShow(Sender:TObject);privateFReady:Boolean;FCache:Real;functionStandardNormal:Real;publicend;varFrmMarsaglia:TFrmMarsaglia;implementation{$R*.dfm}functionLn(R:Real):Real;beginResult:=Log10(R)/Log10(2.7182818);end;procedureTFrmMarsaglia.FormShow(Sender:TObject);constcTestMax=1000000;cScale=1000;cMaxScale=5*cScale;//Wedon'tcountgeneratednumbershigherthanthatvarA:Array[1..cMaxScale]ofInteger;i:integer;lMax,Nr:Integer;rScale:Real;beginforNr:=1tocMaxScaledoA[Nr]:=0;rScale:=cScale;forNr:=1tocTestMaxdobegini:=Trunc(rScale*StandardNormal);ifi<=cMaxScalethenInc(A[i]);end;lMax:=0;forNr:=1tocMaxScaledoifA[Nr]>lMaxthenlMax:=A[Nr];Series1.Clear;forNr:=1tocMaxScaledoSeries1.Add(A[Nr]);end;functionTFrmMarsaglia.StandardNormal:Real;//ReturnsrealnumbersbetweenN(0,1)thatwhenrepeatedhaveanormaldistributionvarx,y,w,l:Real;beginifFReadythenbegin//Returnacachedresultfromapreviouscallifavailable.FReady:=false;Result:=FCache;Exit;end;//Repeatextractinguniformvaluesintherange(-1,1)until0<w=x*x+y*y<1repeatx:=Random;y:=Random;w:=x*x+y*y;until(w>0)and(w<1);l:=Ln(w);w:=sqrt(-2*l/w);FReady:=true;FCache:=x*w;//CacheoneoftheoutputsResult:=y*w;//andreturntheother.end;end.

Delphi.frmfilecheutilizzaunTChart( TeeChart ) che mostra i risultati:

object FrmMarsaglia: TFrmMarsaglia
  Left = 0
  Top = 0
  Caption = 'Marsaglia polar method'
  ClientHeight = 626
  ClientWidth = 964
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object Chart1: TChart
    Left = 0
    Top = 0
    Width = 964
    Height = 626
    BackWall.Visible = False
    BottomWall.Visible = False
    Foot.Visible = False
    LeftWall.Visible = False
    Legend.Visible = False
    Title.Text.Strings = (
      'TChart')
    Title.Visible = False
    View3D = False
    Align = alClient
    BevelOuter = bvNone
    TabOrder = 0
    ExplicitLeft = 272
    ExplicitWidth = 692
    ColorPaletteIndex = 13
    object Series1: TBarSeries
      Marks.Arrow.Visible = True
      Marks.Callout.Brush.Color = clBlack
      Marks.Callout.Arrow.Visible = True
      Marks.Visible = False
      XValues.Name = 'X'
      XValues.Order = loAscending
      YValues.Name = 'Bar'
      YValues.Order = loNone
    end
  end
end

Si noti che 'non tenere una cronologia' non è soddisfatto: c'è un booleano e un numero reale da ricordare stato. Questo è abbastanza buono per me; Avevo paura di dover mantenere matrici di numeri già generati.

    
risposta data 04.06.2013 - 15:42
fonte
1

Se rotolare due e il minimo è troppo;) puoi ridimensionare una singola uniforme in modo casuale in modo che la distribuzione favorisca i piccoli risultati (pseudocodice):

def scaled_random(M):
    r = random()
    return M * r * r
  • random () produce un numero casuale uniforme da 0..1

  • Quando r è piccolo, r * r sarà ancora più piccolo, quindi ci sono molti modi per ottenere numeri vicini allo zero.

  • Mentre r si avvicina a 1, allora r * r si avvicina a 1, quindi il valore di ritorno è più vicino a M. Ma pochissimi numeri diventeranno così alti.

Nota che questo sarà molto più veloce del minimo di 2 numeri casuali, perché la moltiplicazione (r * r) è molto più veloce di generare un secondo casuale. La generazione di numeri casuali è lenta rispetto alle operazioni di base - anche i generatori di numeri casuali "veloci" sono relativamente "veloci" relativamente.

...

Inoltre, alcune librerie matematiche supportano una distribuzione "potente" per numeri casuali - credo che farebbe anche il trucco.

    
risposta data 20.05.2014 - 19:39
fonte

Leggi altre domande sui tag