欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#算法設(shè)計與分析詳解

 更新時間:2022年04月15日 11:32:14   作者:Ruby_Lu  
本文詳細講解了C#的算法設(shè)計與分析,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

作為程序員,開發(fā)完一段代碼,實現(xiàn)了某個功能時,有必要知道:

我的程序需要多長時間?

是什么導(dǎo)致我的程序消耗很多內(nèi)存?

比如,統(tǒng)計或者處理了一大批數(shù)據(jù)。影響這些問題的因素很多,例如,電腦的性能,數(shù)據(jù)的性質(zhì)(值類型和引用類型的區(qū)別),使用的算法。想要為這些基礎(chǔ)問題提供答案需要通過科學(xué)方法。

1. 什么是科學(xué)方法??

它是科學(xué)家為獲取自然界知識所使用的一系列大家認同的方法。

  • 1. 細致地觀察真實世界的特點,通常還要精確的測量
  • 2. 根據(jù)觀察結(jié)果提出假設(shè)模型
  • 3. 根據(jù)模型預(yù)測未來的事件
  • 4. 繼續(xù)觀察并核實預(yù)測的準確性
  • 5. 如此反復(fù)直到確認預(yù)測和觀察一致

這里我們不需要深究它,我們會使用數(shù)學(xué)分析(科學(xué)方法的一種)為算法成本建立模型,并使用實驗數(shù)據(jù)驗證這些模型。

科學(xué)方法的一條關(guān)鍵原則是可重現(xiàn)的,可證偽的。

1.觀察

怎么定量測量程序的運行時間?
各種工具,谷歌瀏覽器...,計時器 Stopwatch
我們一般只需要近似值就可以了。
對大多數(shù)程序的第一個定量觀察就是計算性任務(wù)的困難程度可以用問題的規(guī)模來衡量。什么是問題的規(guī)模?可以是輸入的大小或是某個命令行參數(shù)的值(開發(fā)預(yù)估時間)。
另一個定量觀察是運行時長和輸入本身相對無關(guān),它主要取決于問題模型。就是說,同樣大小的輸入量,程序運行時間是差不多的。如果換一批同樣大小的數(shù)據(jù),運行時長差很多,就需要控制運行時間對輸入的敏感度(比如把實驗數(shù)據(jù)存起來)。

2.將問題規(guī)模和運行時間的關(guān)系量化

  • 1.生成實驗數(shù)據(jù)
  • 2. 數(shù)據(jù)分析

根據(jù)問題規(guī)模和運行時長的數(shù)據(jù)繪制圖表
猜想

舉例 ThreeSum 實驗

public static int ThreeSum(int[] a)
        {
            int N = a.Length;
            int cnt = 0;
            for (var i = 0; i < N; i++)
            {
                for (var j = i+1; j < N; j++)
                {
                    for (var k = j + 1; k < N; k++)
                    {
                        if(a[i]+a[j]+a[k] == 0)
                        {
                            cnt++;
                        }
                    }
                }
            }

            return cnt;
        }

lg(T(N)) = 3 lgN + lga --a是常數(shù)
T(N) = aN^3
這里猜想的時候用到冥次法則:T(N) = aN^b

2.數(shù)學(xué)模型

雖然有很多復(fù)雜的因素影響著程序的運行時間,但一個程序的運行的總時間主要有關(guān)的兩點是:

  • 1. 執(zhí)行每條語句的時長 (取決于計算機,編譯器和操作系統(tǒng))
  • 2. 執(zhí)行每條語句的頻率 (取決于程序本身和輸入)

計算執(zhí)行每條語句的時長可以通過各種工具測出。

重點是判斷執(zhí)行每條語句的執(zhí)行頻率,有的語句很好判斷,比如一些賦值語句;有些需要深入分析,比如ThreeSum 實驗中 if 語句的執(zhí)行次數(shù)為 N(N-1)(N-2) / 6 次(主要是要了解立方推導(dǎo)公式)。而且有些語句的執(zhí)行次數(shù)取決于輸入的數(shù)據(jù),例如 計算和為 0 的三元組的數(shù)量的語句(0 ~ N^3 / 6)。

近似

頻率分析可能會產(chǎn)生復(fù)雜冗長的數(shù)學(xué)表達式,例如:

N(N-1)(N-2) / 6 = N^3 / 6 - N^2 / 2 + N/3

我們使用波浪號逼近法,在其中我們丟棄使公式復(fù)雜化的低階項。我們寫?f(N)表示當(dāng)N增長時除以f(N)接近1的任何函數(shù)。我們寫g(N)?f(N)表示當(dāng)N增長時g(N)/ f(N)接近1。

所以N^3 / 6 - N^2 / 2 + N/3 的 近似函數(shù)是N^3 / 6 ,增長數(shù)量級 是N^3 。

近似運行時間

大部分情況下,執(zhí)行最頻繁的語句決定了程序執(zhí)行的總時間 - 內(nèi)循環(huán),ThreeSum 實驗中的內(nèi)循環(huán)就是 第三層循環(huán)和 if 語句。大部分程序的運行時間都只取決于某一小部分。

性質(zhì)(猜想):ThreeSum 的運行時間 ~ aN^3 (a 是常數(shù)),增長數(shù)量級是N^3。

成本模型

定義了所研究的算法的基本操作。

可以用一個成本模型來評估算法的性質(zhì)。在這個成本模型下,我們可以用數(shù)字說明算法而非某個性質(zhì)。

例如,ThreeSum 的成本模型是數(shù)組的訪問次數(shù)(無論讀寫)。

性質(zhì):該算法訪問了 ~ N^3 / 6 個整數(shù)三元組中的所有3個整數(shù)。

通過明確成本模型使給定程序所需的運行時間的增長數(shù)量級和程序算法真正成本的增長數(shù)量級相同。

總結(jié)

大多數(shù)程序,得到運行時間的數(shù)學(xué)模型所需的步驟:

  • 1. 確定輸入模型,定義問題的模型(該任務(wù)的困難程度)
  • 2. 識別內(nèi)循環(huán)
  • 3. 根據(jù)內(nèi)循環(huán)中的操作確定成本模型
  • 4. 對于給定的輸入,判斷這些操作的執(zhí)行頻率

3. 增長數(shù)量級的分類

我們使用一些結(jié)構(gòu)原語(普通語句,條件,循環(huán),嵌套和方法調(diào)用)來實現(xiàn)算法,因此,成本增長的數(shù)量級通常是問題規(guī)模N的幾個函數(shù)之一。

4. 倍率實驗

步驟:

1.循環(huán)執(zhí)行ThreeSum 方法,并且每次 N = 2 * N (2 是比例,可以調(diào)整)

2. 打印每次執(zhí)行ThreeSum 方法的運行時長和上一次的比

3. 直到該比值趨近于 2^b (b 是常數(shù))

public static void RatioTest()
        {
            Random rd = new Random();
            Stopwatch timer = new Stopwatch();
            int[] a = new int[125];
            for (var i = 0; i < 125; i++)
            {
                a[i] = rd.Next(-1000, 1000);
            }

            timer.Start();
            var res = ThreeSum(a);
            timer.Stop();
            var prev = timer.ElapsedMilliseconds;

            for (var N = 250; true; N = 2*N)
            {
                a = new int[N];
                for (var i = 0; i < N; i++)
                {
                    a[i] = rd.Next(-1000, 1000);
                }
                timer.Start();
                var _res = ThreeSum(a);
                timer.Stop();
                var time = timer.ElapsedMilliseconds;
                var ratio = (decimal)time / prev;
                //Console.WriteLine(a.Length + "\t" + time + "\t" + ratio);
                Console.WriteLine(ratio);
                prev = time;
                //Thread.Sleep(1000);
            }
        }

根據(jù)冥次法則公式可以推導(dǎo)出該比例是以 2 為底的對數(shù)。并且可以得出增長數(shù)量級的近似模型(N^b):

a 為 比例數(shù), c 為極限比值, b 為該算法增長數(shù)量級的指數(shù)。這里 b = 3。

這個實驗對于比值沒有極限的算法無效。

該方法可以簡單地預(yù)測程序地性能并判斷它們的運行時間大致的增長數(shù)量級。

使用該方法可以評估解決大型問題的可行性,比如可以預(yù)估一個大型問題的程序運行時間。同時也可以評估使用更快的計算機所運行的時間。

5.注意事項

在對程序的性能進行分析時,得到不一致或者有誤導(dǎo)性的結(jié)果的原因有很多:

1.大常數(shù)

在去近似時,我們一般會忽略低階項,但如果低階項的系數(shù)很大時(例如 10^3 或 10^6),該近似就是錯誤的。所以我們要對可能的大常數(shù)敏感。

2. 非決定性的內(nèi)循環(huán)

內(nèi)循環(huán)是決定性因素的假設(shè)并不總是正確的。錯誤的成本模型可能無法得到真正的內(nèi)循環(huán),問題的規(guī)模也許沒有大到對指令的執(zhí)行頻率的首項大大超過其他低階項并可以忽略他們的程度。有些程序在內(nèi)循環(huán)之外也有大量指令需要考慮。換句話說,成本模型需要改進。

3. 指令時間

由于大多數(shù)計算機系統(tǒng)都會使用緩存技術(shù),所以每條執(zhí)行所需的時間并不是總是相同。

4. 系統(tǒng)因素

如果計算機同時運行很多程序,會產(chǎn)生爭奪資源的情況,這會影響實驗結(jié)果。

5. 對輸入的強烈依賴

在研究程序的運行時間的增長數(shù)量級時,我假設(shè)運行時間和輸入相對無關(guān)。當(dāng)這個條件不滿足時,會得到不一致或者錯誤的結(jié)果。例如,ThreeSum 返回的不是三個整數(shù)為0的對數(shù),而是是否存在。如果前三個整數(shù)就滿足,該程序的運行時間的增長數(shù)量級為常數(shù);如果輸入不含有這樣的三個整數(shù),程序的運行時間的增長量級為立方級別。

6. 多個問題參量

ThreeSum 性能分析是僅需要一個參量的函數(shù)來衡量程序的性能,參量一般是輸入的規(guī)模。但是,多個參量也是有可能的。例如白名單(一個整數(shù)列表 M 個,一個白名單整數(shù)列表 N個,返回整數(shù)列表中有多少個整數(shù)在白名單中存在),運行時間一般和 M logN 成正比。

6. 處理對于輸入的依賴

輸入模型: 我們可以仔細模擬要處理的輸入的種類。這種方法具有挑戰(zhàn)性,因為該模型可能是不現(xiàn)實的。

最壞情況下的性能保證:不管輸入什么,程序的運行時間都小于一定的范圍(取決于輸入大小)。這種保守的方法可能適用于運行核反應(yīng)堆或心臟起搏器或汽車制動器的軟件。

隨機算法:提供性能保證的一種方法是引入隨機性,例如快速排序和哈希。每次您運行算法時,都會花費不同的時間。這些保證不是絕對的,但是它們無效的機會小于您的計算機被閃電擊中的機會。因此,這種保證在實踐中與最壞情況的保證一樣有用。

操作序列:對于許多應(yīng)用程序,算法輸入可能不僅是數(shù)據(jù),還包括客戶端執(zhí)行的操作順序。

均攤分析: 提供性能保證的另一種方法是通過記錄所有操作的總成本并除以操作總數(shù)來將成本均攤。

7.內(nèi)存

計算程序?qū)?nèi)存的使用情況和運行時間一樣重要。計算機中電路的很大一部分作用就是幫助應(yīng)用程序保存一些值并在使用時取出。保存的值越多,需要的電路越多,需要的內(nèi)存也越多。

.net 內(nèi)存分配系統(tǒng)已經(jīng)幫我們解決了內(nèi)存問題。

.net 使用 8 位(64位)表示字節(jié),每個基本類型需要的內(nèi)存:

1. 對象

一個對象所使用的內(nèi)存,需要將所有實例變量使用內(nèi)存與對象本身的開銷(一般是16字節(jié),這些開銷包括一個指向?qū)ο蟮念惖囊?,垃圾回收信息和同步信息)以?個填充字節(jié)相加。

當(dāng)我們說一個引用所占的內(nèi)存時,指的是它所指向的對象所占的內(nèi)存。對象的引用通常是一個內(nèi)存地址,因此使用8個字節(jié)的內(nèi)存(在64位計算機上)。

2. 鏈表

嵌套的非靜態(tài)類需要額外的8字節(jié)。例如,如果 Node 類是嵌套類,基于鏈表的棧中一個Node對象需要 40 字節(jié)(16字節(jié)的對象開銷,兩個對象引用各需8字節(jié),還有8字節(jié)的額外開銷。為什么不需要填充字節(jié))。

因為一個 Integer 對象需要24字節(jié),所以一個含有 N 個整數(shù)的基于鏈表的表需要 32 + 64 N 字節(jié)(Stack 對象開銷 16 字節(jié),引用類型實例變量 8 字節(jié),int 類型實例變量 4 字節(jié),4個填充字節(jié),每個元素64字節(jié)(一個 Node 對象40字節(jié)和一個Integer 對象 24 字節(jié)))

3. 數(shù)組

C# 中數(shù)組是引用類型,一般都會因為記錄長度需要額外的內(nèi)存。一個基礎(chǔ)類型的數(shù)組一般需要24字節(jié)的頭信息(16字節(jié)的對象開銷,4字節(jié)用于保存長度以及4填充字節(jié))再加上保存值所需的內(nèi)存。例如一個 含有 N個 int 值的數(shù)組需要 24 + 4N (會被填充為 8 的倍數(shù))

4. 字符串對象

String 的標準實現(xiàn)含有4個實例變量:一個指向字符數(shù)組的引用(8字節(jié))和三個 int 值(各4字節(jié))。第一個 int 值描述的是字符數(shù)組中的偏移量,第二個 int 值是字符串的長度。第三個值是一個散列值,它在某些情況下可以節(jié)省計算??偣残枰?0字節(jié),這是除字符數(shù)組之外字符串所需的內(nèi)存空間,加上字符數(shù)組的話是 40 + 24 + 2N = 64 + 2N。

但是,字符數(shù)組常常是在多個字符串之間共享的。因為 String 對象是不可變的,這種設(shè)計使 String 的實現(xiàn)能夠在多個String對象中都含有相同的字符數(shù)組時節(jié)省內(nèi)存。

當(dāng)使用 SubString 創(chuàng)建了一個新的 String 對象(40字節(jié)),但它仍重用了相同的字符數(shù)組。

當(dāng)涉及到函數(shù)調(diào)用時, 內(nèi)存的消耗是一個復(fù)雜的動態(tài)過程。當(dāng)調(diào)用一個方法時,系統(tǒng)會從內(nèi)存中的一個特定區(qū)域為方法分配所需的內(nèi)存(用于保存局部變量),這個區(qū)域叫做棧。當(dāng)方法返回時,占用的內(nèi)存將被返回給棧,所以,在遞歸調(diào)用中不要創(chuàng)建大型的對象。當(dāng)使用 new 創(chuàng)建對象時,系統(tǒng)會在堆中分配所需的內(nèi)存,而且對象會一直存在,知道沒有任何對它的引用。

到此這篇關(guān)于C#算法設(shè)計與分析的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#使用UdpClient類進行簡單通信的實例

    C#使用UdpClient類進行簡單通信的實例

    本文主要介紹了C#使用UdpClient類進行簡單通信的實例,具有很好的參考價值,需要的朋友可以看下
    2016-12-12
  • c# 實現(xiàn)發(fā)送郵件到指定郵箱

    c# 實現(xiàn)發(fā)送郵件到指定郵箱

    這篇文章主要介紹了c# 如何實現(xiàn)發(fā)送郵件到指定郵箱,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • c# 循環(huán)語句的使用方法

    c# 循環(huán)語句的使用方法

    這篇文章主要介紹了c# 循環(huán)語句的使用方法,文中代碼非常詳細,供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • c#獲取客戶端IP地址(考慮代理)

    c#獲取客戶端IP地址(考慮代理)

    這篇文章主要介紹了c#獲取客戶端IP地址(考慮代理),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 一文帶你了解C#中的協(xié)變與逆變

    一文帶你了解C#中的協(xié)變與逆變

    這篇文章介紹了C#中協(xié)變和逆變的相關(guān)知識,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • Unity3D啟動外部程序并傳遞參數(shù)的實現(xiàn)

    Unity3D啟動外部程序并傳遞參數(shù)的實現(xiàn)

    這篇文章主要介紹了Unity3D啟動外部程序并傳遞參數(shù)的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • c#并行編程示例分享

    c#并行編程示例分享

    這篇文章主要介紹了c#并行編程示例,大家直接看下面的代碼吧
    2014-01-01
  • unity中點擊某一個按鈕播放某一個動作的操作

    unity中點擊某一個按鈕播放某一個動作的操作

    這篇文章主要介紹了unity中點擊某一個按鈕播放某一個動作的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • c#調(diào)用winrar解壓縮文件代碼分享

    c#調(diào)用winrar解壓縮文件代碼分享

    這篇文章主要介紹了c#調(diào)用winrar解壓縮文件的方法,大家參考使用吧
    2013-12-12
  • C# 中DateTime 的使用技巧匯總

    C# 中DateTime 的使用技巧匯總

    本文主要介紹了DateTime的使用技巧,包括獲取本周、上周、下周、本月、上個月、下個月、本季度、上季度、下季度、本年度、上年度、下年度等多個方法,分享給大家,希望對大家有所幫助。
    2016-04-04

最新評論