明火煙霧目標檢測專案部署(YoloV5+Flask)
數論總結
本來是想按照代教 PPT 的順序總結的,但是發現我做題的順序好像從開始除了第一道題之外寫的都是跟質數篩,gcd,lcm相關的題目,再加上藍書的順序也是如此,所以就從這裡開始總結吧。
-
質數
藍書上質數章節,分為質數判定,質數篩選,質因數分解三個板塊。但是由於指數判定太簡單,況且到了這個程度了是個人都會吧這裡只總結篩法和質因數分解。
篩法
問題原型:給定一個整數 N,求出 1-N 之間所有的質數。
就不寫暴力篩了吧
埃氏篩
埃氏篩,又稱 Eratosthenes 篩法。顯然是 Eratosthenes 發明的。
-
基本思想:從 2 開始,從小到大列舉每個數 x,把它的倍數 2x,3x,… 標記為合數。當掃到一個數時,若它未被標記,則這個數就是質數。
-
優化:由基本思想可知,我們在標記合數時,會經常把一個合數標記兩次及以上,我們能不能通過優化來減少標記次數從而提高時間效率呢?完全可以。舉個例子,2 和 3 兩個質數都會把 6 標記為合數。所以其實所有小於 x^2 的合數在 x 之前已經有更小的數把它標記過了。(像上面的 3 \times 2 和 2 \times 3),所以我們可以對埃氏篩進行優化:對於每一個 x 標記合數時,只需要從 x^2 開始掃到 \biggl\lfloor\frac n x\biggr\rfloor \times x即可。
-
時間複雜度:O(N \log \log N)
程式碼實現:
memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) { if(vis[i])continue;//若被標記,則說明不是質數。 cout<<i<<endl;//輸出質數 for(int j=i;j*i<=n;j++)vis[i*j]++;//將被數標記為合數。 }
線性篩(尤拉篩)
-
我們會發現,埃氏篩仍然會有一些地方,重複標記合數。比如,12 同時被 2 和 3 標記。其根本原因就是我們沒有確定出唯一的產生 12 的方式。
-
那麼我們是否能對每一個合數,通過某種順序,確定產生這種合數的唯一方式呢?
-
想想我們學過的跟 “唯一”“確定”有關的定理——算術基本定理(唯一分解定理),
呃呃現在沒學無所謂下面很快會提到,我們可以想辦法把一個合數通過類似“分解質因數”的辦法把它們“拆”成唯一的分解形式。比如,我們讓 12 只能通過 3 \times 2 \times 2 得到,而不通過其他方式得到。而這個唯一分解形式的關鍵就在於:質因子!!!那麼我們就可以創造一種類似於逆向分解質因數 -
基本思想:從小到大列舉 i ,如果 i 沒被標記過,就標記為質數。每次列舉不大於 i 的質數 x ,將 x \times i 標記為合數,然後如果 x 不是 x \times i 最小的質數,那麼break即可。
-
兩種實現方法:
-
第一種:用 vis[] 表示是否被標記為質數。
判斷是否為最小質因子的方法:if(!(i%p[j]))break;原理:感覺模板題題解的一部分地方說的挺好的。這裡截過來:
-
for(int i=2;i<=n;i++)
{
if(vis[i]==0)p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
vis[p[j]*i]++;
if(!(i%p[j]))break;
}
}
- 第二種:用 vis[] 儲存最小質因子。對於一個數 x 的 vis,若vis[x]<p[j] 說明 i 有比 p[j] 更小的質因子,直接break;
for(int i=2;i<=n;i++)
{
if(vis[i]==0){vis[i]=i;p[++cnt]=i;}
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
if(p[j]>v[i])break;
vis[i*p[j]]=p[j];
}
}
- 時間複雜度:由於每個質數只會被它的最小質因子篩一次,所以時間複雜度 O(n)。
質因數分解
唯一分解定理(算數基本定理)
-
任何一個大於 1 的正整數都可以唯一分解為有限個質數的乘積,可寫作:
-
N={p_1}^{c_1} {p_2}^{c_2}…{p_m}^{c_m}
-
其中 c_i 都是正整數,p_i 都是質數,且滿足 p_1
試除法
這個很簡單,首先根據質數的判定是從 1-\sqrt n 來掃,那麼我們也只需要試除 1-\sqrt n 的質數,即可完成對 n 的質因數分解。
如何試除從 1-\sqrt n 的質數呢?我們只需要從 1 列舉到 \sqrt n ,每次遇到一個能被 n 整除的數就不斷對 n/=i,直到 n%i 不為 0 即可。
為什麼這麼做是對的呢?因為一個合數的影子一定在掃描到這個合數之前就從 n 中被除去了,所以上述過程中能整除 n 的一定是質數,多次 n/=i 不僅是為了排除之後 i 的倍數對 n 的干擾,還能夠起到累計計算 i 的個數的作用。
時間複雜度:顯而易見的 O(\sqrt n)
程式碼實現:
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
p[++cnt]=i;c[cnt]=0;
while(n%i==0)n/=i,c[cnt]++;
}
}
if(n>1) p[++cnt]=n,c[cnt]=1;//注意一下這裡,如果這時候 n 還沒被除到1,則說明 n 是質數。
同餘
這塊有好多%意義下的運算,由於我太菜,這塊很迷惑,所以先把這塊總結一下吧。
定義
若整數 a 和整數 b 除以正整數 m 的餘數相同,則稱 a,b 模 m 同餘,記為 a \equiv b\pmod {m}。
同餘系與剩餘系
同餘類:對於 {\forall} a \in [0,m-1],集合 \{a+km\}(k \in \mathbb Z)