简体   繁体   中英

How to Generate A Specific Number of Random Indices for Array Element Removal F#

So sCount is the number of elements in the source array, iCount is the number of elements I want to remove.

let indices = Array.init iCount (fun _ -> rng.Next sCount) |> Seq.distinct |> Seq.toArray |> Array.sort

The problem with the method above is that I need to specifically remove iCount indices, and this doesn't guarantee that.

I've tried stuff like

while indices.Count < iCount do
    let x = rng.Next sCount
    if not (indices.Contains x) then
        indices <- indices.Add x

And a few other similar things...

Every way I've tried has been extremely slow though - I'm dealing with source arrays of sizes up to 20 million elements.

I won't give you F# code for this (because I don't know F#...), but I'll describe the approach/algorithm that you should use.

Basically, what you want to do is pick n random elements of a given list list . This can be done in pseudocode:

chosen = []
n times:
    index = rng.upto(list.length)
    elem = list.at(index)
    list.remove-at(index)
    chosen.add(elem)

Your list variable should be populated with all possible indices in the source list, and then when you pick n random values from that list of indices, you have random, distinct indices that you can do whatever you want with, including printing values, removing values, knocking yourself out with values, etc...

is iCount closer to the size of the array or closer to 0? That will change the algorithm which you will use.

If closer to 0, then keep track of the previously generated numbers and check if additional numbers have already been generated.

If closer to the size of the array then use the method as described by @feralin

let getRandomNumbers =
  let rand = Random()
  fun max count -> 
    Seq.initInfinite (fun _ -> rand.Next(max))
    |> Seq.distinct
    |> Seq.take count

let indices = Array.init 100 id
let numToRemove = 10

let indicesToRemove = getRandomNumbers (indices.Length - 1) numToRemove |> Seq.toList
> val indicesToRemove : int list = [32; 38; 26; 51; 91; 43; 92; 94; 18; 35]

What you're doing should be fine if you need a set of indices of negligible size compared to the array. Otherwise, consider doing a variation of a Knuth-Fisher-Yates shuffle to get the first i elements in a random permutation of 1 .. n:

let rndSubset i n =
    let arr = Array.zeroCreate i
    arr.[0] <- 0
    for j in 1 .. n-1 do
        let ind = rnd.Next(j+1)
        if j < i then arr.[j] <- arr.[ind]
        if ind < i then arr.[ind] <- j
    arr

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM