【算法】常用數學計算算法總結(C++代碼)


 

1、求最大公因數和最小公倍數

  利用輾轉相除法,我們可以很方便地求得兩個數的最大公因數(greatest common divisorgcd);
  將兩個數相乘再除以最大公因數即可得到最小公倍數(least common multiple, lcm)。

int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a% b);
}
int lcm(int a, int b) {
return a * b / gcd(a, b);
}

2、判定質數

bool is_prime2(unsigned long long n) { //middle
    long long stop = sqrt(n) + 1;
    if (n == 2) {
        return 1;
    }
    if (n % 2 == 0) {
        return 0;
    }
    for (int i = 3; i <= stop; i += 2) {
        if (n % i == 0) {
            return 0;
        }
    }
    return 1;
}

  求所有<n的質數

204. 計數質數 - 力扣(LeetCode) (leetcode-cn.com)

int countPrimes(int n) {
        if(n<=2) return 0; 
        vector<unsigned char> N(n,1);
        int ans=0;
        for(int i=2;i<n;++i)
        { 
            if(N[i]==1) 
            {
                ans++; 
                for(int j=2*i; j<n; j+=i) N[j]=0; 
            }
        } 
        return ans;
    }

 3.進制轉換

  處理符號-每次取余放到前面-原數除以基數

  10轉7

string convertToBase7(int num) {
    if (num == 0) return "0";
    bool is_negative = num < 0;
    if (is_negative) num = -num;
    string ans;
    while (num) {
        ans = to_string(num % 7) + ans;
num = num/7; } return is_negative ? "-" + ans : ans; }

遞歸寫法

string convertToBase7(int num) {
    if (num < 0) return "-" + convertToBase7(-1 * num);
    if (num < 7) return to_string(num);
    return convertToBase7(num / 7) + to_string(num % 7);
}

 4.打亂與抽獎

4.1打亂

  打亂一個數組的順序,如果采用真.隨機洗牌,對於n張牌組成的有序排列,經過了n次隨機選擇,漏掉1只牌從未選過的概率不等於0,而且,隨着牌的張數數量增加,這個概率非常可觀。

如果想要“徹底地”洗到每一張牌,可以采用經典的 Fisher-Yates洗牌算法原理是通過隨機交換位置來實現隨機打亂,有正向和反向的寫法。(就默認反向吧)

例如隨機1,2,3,4,5,6,7,8這個數組,反向遍歷順序如下:

 

 

 

 

 

 

 

 

 

 

 

  它利用了抽卡本身的順序,【保證照顧】到了每一張原本序列中的卡,而簡單粗暴隨機抽取存在出現重復位置的可能性,就等於浪費了一次排序的機會,換句話說,其等效抽卡次數因為出現了過去相同的洗法,有效洗牌次數下降,樣本空間縮小,無法充滿整個n!空間,所以有效性會下降。而Fisher–Yates算法在原理上保證了不會出現浪費次數,重復選擇的情況,導致樣本空間一直保持n!,沒有坍縮,這就是其在數學意義上優秀的原因。

洗牌算法 詳解 - 打亂數組 - 力扣(LeetCode) (leetcode-cn.com)

  C++代碼如下

vector<int> shuffle() {
    if (origin.empty()) return {};
    vector<int> shuffled(origin);
    int n = origin.size();
    // 可以使用反向或者正向洗牌, 效果相同。
    // 反向洗牌:
    for (int i = n - 1; i >= 0; --i) {
        swap(shuffled[i], shuffled[rand() % (i + 1)]);
    }
    // 正向洗牌:
    // for (int i = 0; i < n; ++i) {
    // int pos = rand() % (n - i);
    // swap(shuffled[i], shuffled[i+pos]);
    // }
    return shuffled;
}

4.2.1權重抽獎

C++ move()用法可參考C++ move()函數_chengjian168的博客-CSDN博客_c++ move()

給定一個數組,數組每個位置的值表示該位置的權重,要求按照權重的概率去隨機采樣。

528. 按權重隨機選擇 - 力扣(LeetCode) (leetcode-cn.com)

我們可以先使用 partial_sum 求前綴和(partial_sum 對於序列 a,b,c,d 產生序列 a,a+b,a+b+c,a+b+c+d。),這個結果
對於正整數數組是單調遞增的。每當需要采樣時,我們可以先隨機產生一個數字,然后使用二分
法查找其在前綴和中的位置,以模擬加權采樣的過程。這里的二分法可以用
lower_bound 實現。
以樣例為例,權重數組
[1,3]的前綴和為[1,4]。如果我們隨機生成的數字為1,那么 lower_bound
返回的位置為 0;如果我們隨機生成的數字是 234,那么 lower_bound 返回的位置為 1

class Solution {
    vector<int> sums;
   vector<int> SumsWeight;
public: Solution(vector<int> weights) : sums(std::move(weights)) { partial_sum(sums.begin(),sums.end(),back_inserter(SumsWeight));
}
int pickIndex() { int pos = (rand() % sums.back()) + 1; return lower_bound(sums.begin(), sums.end(), pos) - sums.begin(); } };

應用實例:

  • Spring Cloud Ribbon (客戶端負載均衡)策略中的 WeightedResponseTimeRule
    • 此題可簡述為「按權重,看作多個區間,按區間寬度越大,概率越大」
    • 在 Ribbon 相關架構中,服務端給客戶端一個服務列表,類似 Map<String, Set<String>> 結構。若客戶端想調用 key = serviceA,可選的具體服務端實例有 Set<String> 的 ["/svc/a1", "/svc/a2", "/svc/a3"],由客戶端自行決定
    • Ribbon 作為客戶端負載均衡來幫助客戶端選擇去哪個具體服務實例(a1 / a2 / a3),希望雨露均沾,又希望別運氣不好抽到響應慢的服務器,故有了一種根據權重的均衡策略
    • 權重是通過定時統計最近一段時間內,a1 / a2 / a3 各自的訪問響應時間如何,如 a1: 10msa2: 20msa3: 40ms
    • 通過算法(不贅述,有興趣可留言喔)計算得 a1: [0, 60]a2: (60, 110]a3: (110, 140] 的區間對應
    • 下次再需要訪問 serviceA 時,隨機一個數 [0, 140],看落在哪個區間,就選那個實例
  • RabbitMQ 的 Topic 交換器使用 Trie 匹配
  • MySQL 中的 IN 語法涉及二分算法

4.2.2流動抽獎

給定一個單向鏈表,要求設計一個算法,可以隨機取得其中的一個數字。

不同於數組,在未遍歷完鏈表前,我們無法知道鏈表的總長度。這里我們就可以使用水庫采樣:

遍歷一次鏈表,在遍歷到第 m 個節點時,有1/m的概率選擇這個節點覆蓋掉之前的節點選擇。

class Solution {
    ListNode* head;
public:
    Solution(ListNode* n) : head(n) {}
    int getRandom() {
        int ans = head->val;
        ListNode* node = head->next;
        int i = 2;
        while (node) {
            if ((rand() % i) == 0) {
                ans = node->val;
            }
            ++i;
            node = node->next;
        }
        return ans;
    }
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM