Pergunta

Estou tentando pensar em uma maneira elegante de obter um subconjunto aleatório de um conjunto em f#

Alguma ideia sobre isso?

Talvez isso funcione: digamos que temos um conjunto de 2x elementos e precisamos escolher um subconjunto de elementos Y. Então, se pudéssemos gerar um número aleatório de tamanho X que contém exatamente 2n Poderes, efetivamente temos uma máscara aleatória com os furos. Poderíamos continuar gerando novos números aleatórios até obtermos o primeiro satisfazendo essa restrição, mas existe uma maneira melhor?

Foi útil?

Solução

Se você não quiser se converter em uma matriz, poderá fazer algo assim. Este é o O (n*m), onde m é o tamanho do conjunto.

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    

Outras dicas

Concordo com @Johannesrossel. Há um algoritmo F# Shuffle-an-Array aqui Você pode modificar adequadamente. Converta o conjunto em uma matriz e, em seguida, faça um loop até selecionar elementos aleatórios suficientes para o novo subconjunto.

Não tendo uma compreensão muito boa do F# e o que pode ser considerado elegante lá, você pode simplesmente fazer um shuffle na lista de elementos e selecionar o primeiro y. UMA Fisher-Yates Shuffle até ajuda você nesse aspecto, pois você também só precisa embaralhar y elementos.

O RND deve estar fora da função do subconjunto.

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

Você quer dizer um subconjunto aleatório de qualquer tamanho?

Para o caso de um subconjunto aleatório de um tamanho específico, há uma resposta muito elegante aqui:

Selecione n elementos aleatórios de uma listau003CT> em C#

Aqui está em pseudocode:

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

Usando seq.fold para construir usando uma avaliação preguiçosa subconfet aleatória:

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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top