2016-12-11 5 views
2

Ich versuche, einen Zufallszahlengenerator zu machen, der mehr "gleichmäßig" zwischen drei- und vierstelligen Bereichen auswählt. Wenn ich es einfach mache:Zufällige Auswahl zwischen 2 oder mehr Bereichen

Ich bin mir bewusst, dass in den meisten Fällen eine 4-stellige Nummer ausgewählt wird. Ich mag 3-stellige Zahlen von mehr Chance geben, ausgewählt zu werden, so dass ich tat dies:

rand_3_digit_num = (100..999) 
rand_4_digit_num = (1000..9999) 

rand([rand_3_digit_num, rand_4_digit_num].sample) 

Gibt es einen anderen Ansatz, dies zu tun? Mein Ziel ist es, nur dreistelligen Zahlen eine größere Chance zu geben, ausgewählt zu werden als mit einem normalen Rand. Dieses Problem wird noch schlimmer, wenn ich 5-stellige oder 6-stellige Zahlen einstelle, die Wahrscheinlichkeit, dass 3-stellige oder 4-stellige Zahlen schnell gewählt werden, nimmt schnell ab.

+0

Ihr Code ist eigentlich die beste Antwort. Es ist kurz, klar und es funktioniert gut. –

+0

Auch, warum willst du das tun? Es wird fair sein, aber es wird unfair gegenüber den Elementen sein. 101 erscheint zum Beispiel 10 mal so viel wie 1001. –

Antwort

0

Ich denke, deine Idee ist gut. Was Sie erreichen möchten, ist die einheitlich zufällig N finden, wobei N die Anzahl der Ziffern in der Zahl darstellt, dann finden Sie die zufällige Anzahl der Länge N.

Sie könnten, dass aufgeteilt in zwei Funktionen:

randomSelection(lengths): 
    K = A random number from the array lengths 
    return randomNumberForLength(K) 

randomNumberForLength(K): 
    lower_bound = 10^K 
    upper_bound = 10^(K+1) - 1 
    return rand(lower_bound, upper_bound) 

Wenn Sie eine Zufallszahl zwischen 100 - 9999 gibt gleiche Wahrscheinlichkeit auf beiden 2-Länge und 3-Länge Zahlen finden möchten, können Sie einfach anrufen randomSelection([2,3])

+0

Welche Sprache soll das genau sein? Es ist nicht genau Python und sicherlich nicht Ruby. "10^5" ist übrigens "15" (10 XOR 5). Du willst '10 ** k'. –

+0

Dies ist nur ein Pseudocode. '10^15' steht für Power 10 bis 15. Die Implementierung unterscheidet sich je nach Sprache, aber yeah, Sie haben Recht, wenn es python wäre, wäre das "10 ** k" –

0

Es hängt ganz davon ab, wie Sie die Ergebnisse verzerren möchten. Zum Beispiel, wenn Sie eine noch Chance wollen, dass Sie eine drei- oder vierstellige Zahl zu bekommen, können Sie etwas so einfach wie (Pseudo-Code) verwenden:

def getRand(): 
    if rand(0, 1) == 0:  // assume inclusive both ends. 
     return rand(100, 999) 
    return rand(1000, 9999) 

Obwohl die Tatsache, dass Sie anrufen rand zweimal kann Verteilungen für wirklich zufällige Anforderungen stopfen, es ist wahrscheinlich gut genug für die meisten Zwecke.

Um es in einem einzigen Telefonieren, die Verteilung erhalten daher wahrscheinlich ist, können Sie nur Werte abbilden:

def getRand(): 
    num = rand(1000, 18999) 
    if num > 9999: 
     num = (num - 10000) % 900 + 100 

Dies würde zwei gleich große Gruppen, 1000-9999 und 10000-18999 erzeugen und die Karte würde Werte in der oberen Gruppe 100-999 (daher gleich wahrscheinlich eine drei- oder vierstellige Zahl zu erhalten) zu werden:

10000 - 10899 -> 100 - 999 
10900 - 11799 -> 100 - 999 
11800 - 12699 -> 100 - 999 
12700 - 13599 -> 100 - 999 
13600 - 14499 -> 100 - 999 
14500 - 15399 -> 100 - 999 
15400 - 16299 -> 100 - 999 
16300 - 17199 -> 100 - 999 
17200 - 18099 -> 100 - 999 
18100 - 18999 -> 100 - 999 

Es gibt keine Zweifel, andere Wege zu Mach es aber alles hängt von der gewünschten Verteilung ab.

2

Brute Lösung:

list = (100..999).to_a*10 + (1000..9999).to_a 
=> [100, ..., 9999] 
list.size 
=> 18000 
list.count { |e| e < 1000 } 
=> 9000 
list.count { |e| 999 < e && e < 10000 } 
=> 9000 

Jetzt list.sample gleiche Wahrscheinlichkeiten von 3- und 4-stellige Zahlen geben soll.

+0

Es entspricht der OP-Lösung, aber es ist viel langsamer. –

+0

Sie haben recht, ich habe OPs Lösung zunächst nicht ganz bekommen. Mein Code ist auf meinem Computer mindestens 400 langsamer. Wie auch immer, in absoluten Zahlen ist der Unterschied nicht merklich, und auch dieser Ansatz gibt mehr Flexibilität. –

+0

Auch ist es nur zum ersten Mal langsamer. Zweite und andere Aufrufe sind bis zu 2-mal schneller (wieder auf meinem Rechner), weil ein Array bereits existiert und wir nur einen sample() gegen sample() + rand() aufrufen müssen. –

1

Zuerst eine Wahrscheinlichkeitsverteilung über Bereiche angeben, sagen:

range_prob = { (100..999)  => 0.2, 
       (1000..9999) => 0.5, 
       (10000..43562) => 0.3 } 

diese Wahrscheinlichkeiten Da kann ein Bereich thusly zufällig ausgewählt werden:

def select_random_range(range_prob) 
    rnd_cum_prob = rand 
    cum_prob = 0.0 
    range_prob.each_with_object({}) do |(rng, prob),h| 
    cum_prob += prob 
    h[rng] = cum_prob 
    end.find { |rng, cum_prob| rnd_cum_prob <= cum_prob }.first 
end 

Was ich hier getan ist ein kumulatives konstruieren Verteilungsfunktion ("cdf") von der diskreten Wahrscheinlichkeitsdichtefunktion ("pdf") range_prob. (Siehe die Grafik unten.) Um eine zufällige Zufallsvariable zu erhalten, erzeugen wir eine Pseudozufallszahl zwischen null und eins, zeichnen auf der vertikalen Achse, bestimmen, wo eine horizontale Linie die cdf schneidet und wählen den zugehörigen Wert auf der horizontalen Achse aus.

cdf

Für range_prob oben

select_random_range(range_prob)  #=> 10000..43562 
select_random_range(range_prob)  #=> 100..999 
select_random_range(range_prob)  #=> 1000..9999 
select_random_range(range_prob)  #=> 100..999 
select_random_range(range_prob)  #=> 10000..43562 

einen zufälligen Wert in einem zufälligen Bereich Auswählen klein zusätzlicher Schritt.

rand select_random_range(range_prob) #=> 6467 
rand select_random_range(range_prob) #=> 16689 
rand select_random_range(range_prob) #=> 2282 
rand select_random_range(range_prob) #=> 1317 
rand select_random_range(range_prob) #=> 9015 

Siehe Kernel#rand.

+1

Schön, und interessant, wenn das OP mehr Kontrolle will. 'rnd_cum_prob' könnte zwischen 0 und der Summe der Wahrscheinlichkeiten ausgewählt werden (falls sie nicht zu 1 addiert werden), und Sie könnten Ihr cdf zwischenspeichern. –

+0

@Eric, ja Caching wäre sinnvoll (z. B. "@cdf" berechnen, bevor irgendwelche Zufallsvariablen erzeugt werden). Ich würde geneigt sein, eine Ausnahme zu erheben, wenn die Wahrscheinlichkeiten nicht 1.0 ergeben würden. –

+0

Float-Vergleich ist schwierig. Ich würde Gewichte benutzen und es einen Tag nennen. –

0

Für das Problem, das Sie beschrieben haben, ist Ihre Lösung gut genug.

999 erscheint 10-mal häufiger als 1000, obwohl. Wenn Sie einen reibungsloseren Übergang zwischen den Bereichen möchten, könnten Sie verwenden:

# Defines a distribution for random numbers between min and max. 
# Smaller numbers have a higher probably to appear. 
class BiasedGenerator 
    def initialize(min, max) 
    @range = (Math.log(min)..Math.log(max)) 
    end 

    def self.digit_range(min_digit, max_digit) 
    new(10**(min_digit - 1), 10**max_digit - 1) 
    end 

    def rand 
    Math.exp(Kernel.rand(@range)).round 
    end 
end 

Sie müssen nur um es initialisieren einmal:

generator = BiasedGenerator.digit_range(3, 4)

und verwenden generator.rand so oft wie Sie wollen:

random_numbers = (1..1_000_000).map do 
    generator.rand 
end 

puts 'Min :' 
puts random_numbers.min 
puts 'Max :' 
puts random_numbers.max 
puts 
random_numbers.group_by { |n| n.to_s.size }.sort_by(&:first).each do |digits, numbers| 
    puts "#{digits} digits : #{numbers.size}" 
end 

es Ausgänge:

Min : 
100 
Max : 
9999 

3 digits : 500061 
4 digits : 499939 

Die Verteilung sieht wie folgt aus: Millions values with bias

Der grüne Bereich zwischen 100 und 999 sollte als eine fast gleich sein auch zwischen 1000 und 9999.

Ihr Generator diese Eigenschaft hat:

enter image description here

Zum Vergleich hier ist Kernel.rand:

enter image description here

Mit BiasedGenerator.digit_range(3, 6):

Min : 
100 
Max : 
999998 

3 digits : 250342 
4 digits : 250714 
5 digits : 249814 
6 digits : 249130 
Verwandte Themen