C++ 由快排學習到的的隨機數等知識

来源:https://www.cnblogs.com/xhzg/archive/2022/09/07/16630883.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 最近在研究一個基於TP6的框架CRMEB,這裡分享下我的開發心得 首先要獲取原始項目文件 這裡是git地址 https://gitee.com/ZhongBangKeJi/CRMEB.git 項目環境的要求為Apache、MySQL、PH ...


  • 起:

力扣的912題 數組排序 ,想著先用快速排序來寫寫,在實際用c++編寫的時候,有一些之前沒註意到的細節問題造成了一些麻煩。

912. 排序數組 - 力扣(LeetCode)

 

 

 

  • 快排思想

  每次以數組最後一個數為基準,按照波蘭國旗問題對數組進行分層(partition)。假設最後一個數為P,則將數組分為小於P、等於P、大於P的3層。之後對小於P和大於P的層進行此過程的迭代。迭代完成後,目標數組即排列完成。

  1.   問題:最壞的結果的O(n^2),因為每次最後一個數當成分層基準,最壞的情況是左右兩層極度不平衡
  2.   改進:引入隨機數,每次進行分層之前,隨機將數組前面的一個數與最後一個數P進行swap,這樣分層就成了一個隨機事件。在數學證明中,可證明時間複雜度收斂於0(N*LogN)。
  • C++中的隨機數

  在c++中,獲取隨機數一般廣為人知的方法是 rand() ,但是這種方法獲得的隨機數是偽隨機數,其原理是從一個已經確定了的序列中按順序返回整數。

  在直接使用 rand()時,預設的 srand(1)。srand可以認為是種子。

    只使用rand()時

int main() {
    for (int i = 0; i < 10; i++) {
        cout << rand() << "\t";
    }
    return 0;
}

  輸出結果(因電腦而異):

    41      18467   6334    26500   19169   15724   11478   29358   26962   24464

  你每次運行,答案和該結果都一致,這是因為種子沒變,永遠輸出的是該序列前十個數字。

  所以有一個思路就是改變種子,想讓每次運行的種子發生變化,那麼就想到了時間,時間是每秒都在變化的,可以讓時間來充當種子參數

  

int main() {
    srand((int)time(NULL)); // #include<ctime> for time()
    for (int i = 0; i < 10; i++) {
        cout << rand() << "\t";
    }
    return 0;
}

  輸出結果:

    31937   9951    11817   1295    252     30339   22649   12202   9420    16246

  與之前結果不同了。似乎這達到了我們獲取真隨機數的目的。但是依舊有一個問題,那就是time 是以秒為單位的,如果你的項目要在一秒之內調用多次隨機數呢?這樣一來,種子在這一秒之內是不會發生變化的。

int getrd_1() {
    srand((int)time(NULL)); // #include<ctime> for time()
    return rand();
}

int main() {
    for (int i = 0; i < 10; i++) {
        cout << getrd_1() << "\t";
    }
    return 0;
}

   輸出結果:

      32753   32753   32753   32753   32753   32753   32753   32753   32753   32753

    果然是一樣的,因為這十次調用都是在1秒之內。

    這種情況下,可以使用random_device 來生成真隨機數

    

int getrd(const int &min, const int&max) {//範圍 [min,max)
    std::random_device rd;                //#include<random> for std::random_device
    return min + rd() % max;
}
int main() {
    for (int i = 0; i < 10; i++)
        std::cout << getrd(0, 10) << "\t";
    return 0;
}

    輸出結果:

        3       0       0       9       7       8       5       4       9       2

    達到了我們預期的要求。

    但是,需要註意,這個實現要看你的庫支持不支持,如果不支持,將繼續使用偽隨機數發生器。可以通過調用random_device的成員函數 entropy()來查看熵,如果是偽隨機發生器,熵恆為零

  • swap

    

//通過一個臨時變數來儲存b的值,完成交換
void swap(int &a, int &b) {
    int tmp(b);
    b = a;
    a = tmp;
}
//通過異或^來完成交換
void swap(int &a, int &b) {
    if (a == b)
        return;
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

    第一種交換司空見慣了,第二種則用到了位操作 異或 ^ 的性質:

            a ^ 0 等於 a                           a ^ a 等於 0 

            滿足結合律,交換律

    因此:

        第一句:a = a ^ b ;   第二句:b = a ^ b,此時 b 等於 a^b^b,結合性質 結果是 a  ; 第三句 : a = a ^ b ,此時 a等於 a ^ b ^ a,結果是b ,交換完成

    需要註意的是,如果a 與  b 的地址相同 則 a^b 等於0。

最後貼上我的快排:

 

class Solution {
private:
    void swap(int& a, int& b) {
        if (a == b)
            return;
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
    }
    int getrd(const int &min,const int& max) {
        random_device rd;
        return min + rd() % (max - min);
    }
public:
    //快排
    int* Mypartition(vector<int>& nums, int L, int R) {
        int less(L - 1), more(R);
        int i(L);
        while (i < more) {
            if (nums[i] < nums[R]) {
                swap(nums[++less], nums[i++]);
            }
            else if (nums[i] > nums[R]) {
                swap(nums[i], nums[--more]);
            }
            else
                i++;
        }
        swap(nums[more], nums[R]);
        return new int [2]{ less, more+1 };
    }
    void process(vector<int>& nums, int L, int R) {
        if (L < R) {
            // cout<<"L ="<<L<<"\t R="<<R<<endl;
            swap(nums[getrd(L,R)], nums[R]);
            int *p = Mypartition(nums,L,R);
            process(nums, L, p[0]);
            process(nums, p[1], R);
        }
    }
    vector<int> sortArray(vector<int>& nums) {
        process(nums, 0, nums.size()-1);
        return nums;
    }
};

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原型模式(Prototype Pattern)是用於創建重覆的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。 ...
  • Python自學第六天:實戰練習——機選雙色球 我是一個編程小白,目前從事運維工作。 對於運維相關的技術,基本上都是瞭解點皮毛。 因為最近接觸自動化運維工具,看到很多工具都需要用到Python來寫腳本。 於是,利用業餘時間,開始自學Python。 目的並不是要學到很精通,而是希望大致看明白別人寫的代 ...
  • 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 特效 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> O ...
  • Qt中MVC的M(Model)簡單介紹 Qt有自己的MVC框架,分別是model(模型)、view(視圖)、delegate(委托),這篇文章,簡單的介紹以下Qt中有關model(模型)的類以及一些基本的使用。 Qt官方的文檔已經很詳細了,如果想要詳細的去瞭解,建議花點精力去看官方文檔。 @ 類繼承 ...
  • 說明 這是關於Qt5(Qt5.1.4.2),QWidget編程使用Qt虛擬鍵盤(qtvirtualkeyboard) Tag: QT5,Qt,軟體盤、虛擬鍵盤,Widget程式,QML 作者:[email protected] 關鍵代碼 啟用虛擬鍵盤模塊 在QApplication對象創建之前插入代碼 ...
  • JavaGUI-坦克大戰04 7.線程的應用03 7.3坦克大戰4.0版 7.3.4功能3:敵方坦克自由移動 功能3:讓敵人的坦克也可以自由隨機地上下左右移動 思路: 因為要求敵人的坦克自由移動,因此需要將敵人坦克當做線程使用,EnemyTank類實現Runnable介面 線程的run方法的具體操作 ...
  • JavaGUI-坦克大戰03-2 7.線程的應用02 7.3.坦克大戰4.0版 坦克大戰4.0版 增加功能: 功能1.讓敵人的坦克也能夠發射子彈(可以有多個子彈) 功能2.當我方坦克集中敵人坦克時,敵人的坦克就消失,如果能做出爆炸的效果更好 功能3.讓敵人的坦克也可以自由隨機地上下左右移動 功能4. ...
  • 1.什麼是模板層 模板層可以根據視圖中傳遞的字典數據動態生產相應的HTML頁面 2.模板層的配置 1.在項目下創建一個與同名文件夾平行的templates文件夾 2.在settings.py中的TEMPLATES配置項中 BACKEND:指定模板的引擎 DIRS:模板的搜索目錄(可以是一個或者多個) ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...