C#Shuffle算法(洗牌算法、抽樣算法)

Fisher-Yates Shuffle算法

1.創(chuàng)建一個新的list
2.隨機取出當前0-list.Count其中一個數
3.把老list當前隨機數位置添加到新list
4.老list刪除這個數
5.直到老list.Count=0結束返回

    public void FisherYatesShuffle<T>(List<T> list)
    {
        List<T> cache = new List<T>();
        int currentIndex;
        while (list.Count > 0)
        {
            currentIndex = Random.Range(0, list.Count);
            cache.Add(list[currentIndex]);
            list.RemoveAt(currentIndex);
        }
        for (int i = 0; i < cache.Count; i++)
        {
            list.Add(cache[i]);
        }       
    }

隨機性 可驗證每一個數的概率都是相等的

一個元素m被放入第i個位置的概率P = 前i-1個位置選擇元素時沒有選中m的概率 * 第i個位置選中m的概率


image.png

時間復雜度為O(n^2)空間復雜度為O(n)

Knuth-Durstenfeld Shuffle算法

是上面板的升級版本 不用new新的list 在原list進行交換
1.隨機取出當前0-list.Count-i的數(就是相當于不移除,要從后每次遍歷都要從后往前空出一個位置給隨機完的數交換到(最后一個-i)這個位置)
比如一共1234
你在前四個隨機一個2 2和4交換 成了1432
然后在前三個隨機 只能隨機到143其中一個
就按3來說 他和自己交換就是位置不變 成了1432
然后在前兩個交換 比如隨機了一個1 和4交換 成了4132
最后一個不交換不然會數組越界
2.把當前隨機的數和最后空出來的數交換
這樣可以確保不會重復隨機

   public void KnuthDurstenfeldShuffle<T>(List<T> list)
    {
        //隨機交換
        int currentIndex;
        T tempValue;
        for (int i = 0; i < list.Count; i++)
        {
            currentIndex = Random.Range(0, list.Count - i);
            tempValue = list[currentIndex];
            list[currentIndex] = list[list.Count - 1 - i];
            list[list.Count - 1 - i] = tempValue;
        }
    }

然后用forr優(yōu)化 因為for循環(huán)每次都獲取list.count比較 而forr是反向 只用獲取一次
不過有局限因為只有開始獲取了長度而不是每次獲取 導致必須一開始知道長度 長度未知的話只能用第一種

    public void KnuthDurstenfeldShuffle<T>(List<T> list)
    {
        //隨機交換
        int currentIndex;
        T tempValue;      
        for (int i = list.Count - 1; i >= 0; i--)
        {
            currentIndex = Random.Range(0, i+1);
            tempValue = list[currentIndex];
            list[currentIndex] = list[i];
            list[i] = tempValue;
        }
    }

隨機性


image.png

也是相等的
時間復雜度為O(n)空間復雜度為O(1)
是最佳的洗牌算法

Inside-Out Algorithm算法

上面算法是內部打亂算法 如果我們想保留數據需要另外開辟一個List保存 我覺得跟上面的差不多就用個list保存 數組是struct可以直接賦值時間復雜度可能是O(n)

public List<T> InsideOutAlgorithm<T>(List<T> list)
    {
        List<T> cache = new List<T>();
        for (int i = 0; i < list.Count; i++)
        {
            cache.Add(list[i]);
        }
        //隨機交換
        int currentIndex;
        T tempValue;
        for (int i = cache.Count - 1; i >= 0; i--)
        {
            currentIndex = Random.Range(0, i + 1);
            tempValue = cache[currentIndex];
            cache[currentIndex] = cache[i];
            cache[i] = tempValue;
        }
   
        return cache;
    }

時間復雜度為O(n^2)空間復雜度為O(n)

ReservoirSampling蓄水池抽樣算法

如果我們想從一個從N個元素中隨機的抽取k個元素,其中N無法確定 就要用到這個算法
其實還有一種算法一開始會想到等概率抽取法,每次在(0,n-1)隨機一個數,隨機m次如果有相同的重新隨機。如果N較大也可能不確定大小就只能用蓄水池抽樣了
1.先把讀到的前k個對象放入“水庫”
2.對于第k+1個對象開始,以k/(k+1)的概率選擇該對象,以k/(k+2)的概率選擇第k+2個對象,以此類推,以k/m的概率選擇第m個對象(m>k)。
3.如果m被選中,則隨機替換水庫中的一個對象。最終每個對象被選中的概率均為k/n,證明如下。

第m個對象被選中的概率=選擇m的概率(其后元素不被選擇的概率+其后元素被選擇的概率不替換第m個對象的概率)

image.png

每個元素被抽中概率相等

  public List<T> ReservoirSampling<T>(List<T> list, int m)
    {
        List<T> cache = new List<T>(m);
        for (int i = 0; i < m; i++)
        {
            cache.Add(list[i]);
        }
        int currentIndex;
        for (int i = m; i < list.Count; i++)
        {
            currentIndex = Random.Range(0, i + 1);
            if (currentIndex < m)
            {
                 cache[currentIndex] = list[i];
            }
        }
        return cache;
    }
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 導語: 如果你已經加入了iOS攻城獅隊伍,那么我們由衷地祝賀您正式成為一名終身學習的程序猿;有人覺得這句話...
    超人猿閱讀 2,547評論 3 19
  • 2018年10月8日 /*本節(jié)主要內容:1、 時間復雜度2、冒泡排序3、選擇排序4、插入排序5、對數器概念和使用6...
    須臾之北閱讀 819評論 0 0
  • 子曰:“詩三百,一言以蔽之,曰‘思無邪’?!边@句話是最恰當不過了。在周代尚未受到封建禮教的毒害時,女子都是無所拘束...
    沉無閱讀 396評論 2 4
  • 云竹嶺的童子雞上架之后,家里就開啟了沒完沒了的辣子雞盛宴,邊寫邊流口水!童子雞燒辣子雞真是超級鮮嫩!超級入味!超級...
    賣土雞的小不拉拉閱讀 1,220評論 3 51
  • 祝賀劉以林兄演講成功,特作詩以應 大??彰鳎斚绿焯?。 長天馭風,蒼穹明凈。 島戀長灘,海境勝殊。 洞闊清朗,氣清...
    曉霞_e49d閱讀 239評論 0 3

友情鏈接更多精彩內容