2009-07-14 5 views
4

ich eine elegante Art und Weise zu denken, ich versuche, von einem Satz in F # eine zufällige Teilmenge des ErhaltensHolen Sie sich einen zufällig ausgewählten Untergruppe von einem Satz in F #

Irgendwelche Gedanken dazu?

Vielleicht würde das funktionieren: sagen wir haben einen Satz von 2 x Elemente und wir müssen eine Teilmenge von y-Elemente auswählen. Wenn wir dann eine x-große Bit-Zufallszahl erzeugen könnten, die genau y 2 n enthält, haben wir effektiv eine zufällige Maske mit y-Löchern darin. Wir könnten weiterhin neue Zufallszahlen generieren, bis wir die erste haben, die diese Einschränkung erfüllt, aber gibt es einen besseren Weg?

Antwort

2

Wenn Sie nicht in ein Array konvertieren möchten, könnten Sie so etwas tun. Dies ist O (n * m), wobei m die Größe der Menge ist.

open System 

let rnd = Random(0); 
let set = Array.init 10 (fun i -> i) |> Set.of_array 

let randomSubSet n set = 
    seq { 
     let i = set |> Set.to_seq |> Seq.nth (rnd.Next(set.Count)) 
     yield i 
     yield! set |> Set.remove i 
     } 
    |> Seq.take n 
    |> Set.of_seq 

let result = set |> randomSubSet 3 

for x in result do 
    printfn "%A" x  
+1

Da dieser Ansatz schlecht abschneidet, sollten Sie besser in ein Array konvertieren, mischen und dann die ersten m Ergebnisse als Set verwenden - es ist wahrscheinlich einfacher zu booten. Und wenn Sie _really_ nicht möchten, Ihre ursprüngliche Menge in ein Array zu konvertieren, könnten Sie immer noch eine zufällige boolesche Maske mit einem entsprechend gemischten Array (mit m True und nm false) generieren, und dann einfach das Array mit setzen, Filtere die Masken und mappe zurück in die Menge - ohne jemals die ursprüngliche Menge in ein Array zu konvertieren und trotzdem die O (n) -Leistung beizubehalten. –

+0

Ihr Code-Snippet enthält normalerweise denselben Satz von Elementen außer dem letzten. Ich bin 4 Mal gelaufen: '[0; 1; 5], [0; 1; 6], [0; 1; 2], [0; 1; 4] '. Offensichtlich ** es ist nicht zufällig ** Teilmenge. Und es passiert wegen dieser Linie 'Ausbeute! Set |> Set.remove i' –

+0

'yield!' liefert alle Elemente einer Sequenz, in diesem Fall die ursprüngliche Menge mit einem Element entfernt ('set |> Set.remove i'), was falsch ist. Die Funktion sollte rekursiv sein ('lass rec randomSubSet n set = ...') und du solltest "nachgeben"! set |> Set.entfernung i |> randomSubSet (n-1) '. –

2

Da Sie F # nicht wirklich gut verstehen und was dort als elegant betrachtet werden könnte, können Sie einfach die Liste der Elemente mischen und die erste y auswählen. Ein Fisher-Yates shuffle hilft Ihnen sogar in dieser Hinsicht, da Sie auch nur y Elemente mischen müssen.

+0

F # Sätze nicht zulassen für die Zufallszugriffszeit O (1) wäre dieser Algorithmus unwirksam, ohne dass der Satz in ein Array umgewandelt wird. – gradbot

+0

Ok, würde in der gleichen Komplexität wie deine Lösung liegen, wenn ich das richtig sehe. Aber du hast Code, ich habe es bisher nicht geschafft, F # zu lernen, also bekommst du meine +1 :) – Joey

2

Stimmen Sie mit @JohannesRossel überein. Es gibt einen F # Shuffle-a-Array-Algorithmus here, den Sie entsprechend modifizieren können. Konvertieren Sie das Set in ein Array und wiederholen Sie die Schleife, bis Sie genügend zufällige Elemente für die neue Teilmenge ausgewählt haben.

+0

Es scheint, dass @usernameIdentifier hier eine allgemeine Konvention ist, um auf andere zu verweisen (von denen ich glaube, dass sie von anderen übernommen wurden) Medium), also versuche ich nur mit dem Fluss zu gehen. – Brian

1

rnd muss aus der Teilmengenfunktion sein.

let rnd = new Random() 
let rec subset xs = 
    let removeAt n xs = (Seq.nth (n-1) xs, Seq.append (Seq.take (n-1) xs) (Seq.skip n xs)) 
    match xs with 
    | [] -> [] 
    | _ -> let (rem, left) = removeAt (rnd.Next(List.length xs) + 1) xs 
      let next = subset (List.of_seq left) 
      if rnd.Next(2) = 0 then rem :: next else next 
0

Seq.fold Verwendung mit lazy evaluation zufällig ausgewählten Untergruppe zu konstruieren:

let rnd = new Random() 

let subset2 xs = let insertAt n xs x = Seq.concat [Seq.take n xs; seq [x]; Seq.skip n xs] 
       let randomInsert xs = insertAt (rnd.Next((Seq.length xs) + 1)) xs 
       xs |> Seq.fold randomInsert Seq.empty |> Seq.take (rnd.Next(Seq.length xs) + 1) 
1

Sie eine zufällige Teilmenge jeder Größe bedeuten Sie?

Für den Fall einer zufälligen Teilmenge einer bestimmten Größe, gibt es eine sehr elegante Antwort hier:

Select N random elements from a List<T> in C#

es hier in Pseudo-Code ist:

RandomKSubset(list, k): 
    n = len(list) 
    needed = k 
    result = {} 
    for i = 0 to n: 
    if rand() < needed/(n-i) 
     push(list[i], result) 
     needed-- 
    return result