↓ 快轉到主要內容

UVA583 - 質因數分解、黑心老闆演算法(自己發明的講法)、輸出格式小技巧

題目簡述
#

  給定一個數字,比如 200 要將 200 的質因數分解輸出:

200 = 2 x 2 x 2 x 5 x 5

  除了正整數,還需要處理負數,處理方法是在最前面加上「-1 x」,比如-192:

-192 = -1 x 2 x 2 x 2 x 2 x 2 x 2 x 3

質數篩
#

  先做一個質數篩的函數出來,我用的是線性篩,關於質數篩我有寫一篇介紹文章,這裡不過多贅述

const int MAX = 1e7+5;
bitset<MAX>isp;
vector<int>primes;

void sieve(int n){
	isp.set();
	isp[0]=isp[1]=0;
	
	for(int i=2;i<=n;i++){
		if(isp[i]){
			primes.push_back(i);
		}
		
		for(int p : primes){
			if(i*p > n)break;
			isp[i*p]=0;
			if(i%p==0)break;
		}
	}
}

黑心老闆演算法
#

  接下來展示的是用來質因數分解的模板,我把它稱為「黑心老闆演算法」,其程式片段如下:

cin>>n;
int x = abs(n);
vector<int>ciallo;

for(int p : primes){
    if(p*p > x) break;
    while(x%p==0){
        ciallo.push_back(p);
        x/=p;
    }
}
if(x > 1) ciallo.push_back(x);

  把要分解的數 x 想成一間公司,每個質數 p 代表一個派系。老闆(我們)從最小的派系開始清算:

  1. 只要 x 還能被 p 整除,就代表公司裡還有 p 派的員工。

  2. 把這位員工的名字記進名單 ciallo(push_back(p)),然後開除他(x /= p)。

  3. 同一派系可能有很多人,所以用 while 一直開除,直到 x 再也不能被 p 整除,才算把這個派系「斬草除根」。

  4. 換下一個更大的質數,重複以上步驟。

  每開除一人,公司(x)就變小一些。最後名單 ciallo 裡的所有數字相乘,就會等於原本的 x。

為什麼 p*p > x 可以停止

  先說迴圈的不變式:輪到質數 p 時,x 已經把所有比 p 小的質因數除乾淨了。

  此時若 \(p^2 > x\)(\(p > \sqrt(x)\)):

  • 假設 \(x\) 是合數,它必有:質因數 \(\le \sqrt{x} < p\)。

  • 但所有小於 \(p\) 的質因數都已經被除掉了,矛盾。

  • 所以 \(x\) 只能是 \(1\) 或質數。

  因此可以直接停止迴圈。若剩下的 x > 1,它就是最後一個質因數(公司裡剩下的最後一位大佬,自己一派),直接放進名單即可:

if(x > 1) ciallo.push_back(x);

  注意這裡的 x 是當前剩下的值,會隨著除法變小。

輸出格式小技巧
#

  題目要求質因數之間用 x 隔開,最後一個後面不能多出 x。做法是用 i > 0 判斷:只有從第二個數開始,才在前面補上 x。

cout<<n<<" = ";
if(n < 0) cout<<"-1 x ";

for(int i=0; i<ciallo.size(); ++i){
    if(i > 0) cout<<" x ";
    cout<<ciallo[i];
}
cout<<'\n';

  負數則是在印質因數之前,先額外輸出 -1 x ,這樣後面的迴圈完全不用特別處理負號。

程式
#

#include<iostream>
#include<vector>
#include<algorithm>
#include<bitset>
#include<cmath>
using namespace std;

const int MAX = 1e7+5;  //其實不用開到 1e7+5 這麼大
bitset<MAX>isp;
vector<int>primes;

void sieve(int n){
	isp.set();
	isp[0]=isp[1]=0;
	
	for(int i=2;i<=n;i++){
		if(isp[i]){
			primes.push_back(i);
		}
		
		for(int p : primes){
			if(i*p > n)break;
			isp[i*p]=0;
			if(i%p==0)break;
		}
	}
}

int main(){
	sieve(90000);
	
	int n;
	while(cin>>n&&n){
		vector<int>ciallo;
		int x = abs(n);
		
		for(int p :primes){
			if(p*p > x){
				break;
			}
			if(x%p==0){     //註記:if(x%p==0) 其實是多餘的,只是另一題類似的 UVA-10699 需要這個 if
				while(x%p==0){
					ciallo.push_back(p);
					x/=p;
				}
			}
		}
		if(x > 1)ciallo.push_back(x);
		cout<<n<<" = ";
		if( n <0)cout<<"-1 x ";
		
		for(int i=0;i<ciallo.size();++i){
			if(i > 0)cout<<" x ";
			cout<<ciallo[i];
		}
		
		cout<<'\n';
	}
}

相關文章