簡體   English   中英

如何從K個對象中隨機選擇少於N個對象?

[英]How to pick less than N objects randomly from K objects?

1,2,3,4 4個對象開始。 想隨機選擇2個對象,但也可以不選擇任何對象,也可以只選擇1個對象。 (僅考慮組合。無順序。)

因此,可能的狀態為以下11種狀態:

[(empty)],[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]

如何以11次出現一次的方式生成上述狀態之一?

需要寫這個的通用版本。 從K個對象中隨機選擇少於N個對象。

您首先需要確定要拾取的對象數。 在您的示例中,您有11個可能的子集,大小為1的1個,大小1的4個,大小2的6個。因此,應根據加權分布1:4:6選擇大小0、1或2。 可視化的一種方法是想象11個大小相等,間距相等的插槽:1用0標記,4用1標記,6用2標記。現在,將一個球隨機放到其中一個插槽中,並注意標簽。 每個插槽都有一個接收球的相等概率,但是獲得帶有標簽0、1或2的插槽的概率為1:4:6。

通常,大小為n的集合中k對象的組合數由n!/(k!*(nk)!) 我們可以使用此公式來確定我們的加權分布。 請注意,我遵循通常的慣例,即使用k代表n可能性中被拾取的對象的數量-您以相反的方式使用它們,這有點令人困惑。

一旦確定了選擇數量p ,就可以使用Fisher-Yates隨機播放的Durstenfeld變體從輸入中隨機選擇p元素。

這是一些Java代碼來說明:

static <E> List<E> randomPick(List<E> in, int k)
{   
    int n = in.size();

    // determine number of elements to pick using a random selection
    // weighted by the number of subsets of each size, 0..k
    Random r = new Random();
    NavigableMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
    int total = 0;
    for(int i=0; i<=k; i++)
    {
        total += fact(n)/(fact(i)*fact(n-i));
        map.put(total, i);
    }       
    int p = map.higherEntry(r.nextInt(total)).getValue();

    // Use Durstenfeld shuffle to pick p random elements from list
    List<E> out = new ArrayList<>(in);
    for(int i=n-1; i>=n-p; i--)
    {
        Collections.swap(out, i , r.nextInt(i + 1));
    }       
    return out.subList(n-p, n);
}

static int fact(int n)
{
    int f = 1;
    while(n > 0) f *= n--;
    return f;
}

測試:

public static void main(String[] args)
{
    List<Integer> in = Arrays.asList(1, 2, 3, 4);       
    for(int i=0; i<10; i++)
        System.out.println(randomPick(in, 2));
}

輸出:

[]
[2, 1]
[4]
[3, 2]
[1]
[1, 4]
[2, 1]
[2, 3]
[4]
[1, 4]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM