最多約數問題答案解析與勘誤

2021-06-12 08:18:32 字數 4570 閱讀 7196

問題描述: 

正整數x的約數是能整除x的正整數。正整數x 的約數個數記為div(x)。例如,1,2,5,10 都是正整數10 的約數,且div(10)=4。設a 和b 是2 個正整數,a≤b,找出a和b之間約數個數最多的數x。 

程式設計任務: 

對於給定的2個正整數a≤b,程式設計計算a 和 b 之間約數個數最多的數。 

資料輸入: 

輸入資料由檔名為input.txt的文字檔案提供。檔案的第1 行有2 個正整數 a和 b。 

結果輸出: 

程式執行結束時,找到a 和b之間約數個數最多的那個數及最多約數個數。

測試資料:【只給出最多約數個數, time limit: 1s】

問題分析

本題的要求是,求出乙個給定區間內的含約數最多的整數。

首先明確一下如何求乙個數的約數個數:若乙個數n滿足:n = a1

n1 * a2

n2 * a3

n3 * …… * am

nm,則n的約數個數為(n1 + 1) (n2 + 1) (n3 + 1) …… (nm + 1)。這是可以用乘法原理證明的。

最淺顯的演算法是,列舉區間內的每個整數,統計它們的約數個數。這個演算法很容易實現,但是時間複雜度卻相當高。因為題目約定區間的最大寬度可以達到10^9的數量級,相當龐大。因此,在極限規模時,時間是無法忍受的。所以,我們需要盡量的優化時間。

分析一下列舉的過程就會發現,如果我們列舉到兩個數n和m*n(m為一相對於n較大的質數),那麼我們將重複計算n的約數兩次。據此,我們發現了列舉效率低的根本所在。為了解決這一重複,我們可以選取另一種搜尋方法——以質因子為物件進行深度搜尋。

初始時,令number := 1,然後從最小的質數2開始列舉,列舉包含乙個2、兩個2……n個2的情況……直至number * 2

n大於區間的上限(max)。對於每種「2^k的情況」,令number := number * 2

n,再列舉3的情況,然後,列舉5的情況、7的情況……方法相同。整個過程是乙個深度搜尋的過程。當number大於等於區間下限(min)時,我們就找到了乙個區間內的數,根據前面介紹的方法,可以得到它的約數個數。所有的區間內的數的約數個數的最大值就是我們要求的目標。

為什麼這種深度搜尋可以減少常規列舉過程中的重複問題呢?請看下面的乙個例子

設給定的區間為[6,30],6,18,30為區間內的數,按照常規列舉方法,計算18,30,的時候分別計算了因子6的約數個數,重複計算2次。如果使用上述所說的深度搜尋方法,求這3個數的因數個數的路徑有一條公共部分,2*3,這一部分只計算了一次,求18只需再乘個3,求30只需再乘個5,相對於常規列舉減少了兩次計算2*3的時間。但這種深度搜尋也有問題,就是number有可能是無用的,下面的分析便是對這種深搜方法進行無用資料剪枝。

值得注意的是,我們列舉過程中得到的number可能無用的,即無論用number去乘以多少,都無法得到區間內的數。這樣的number如果繼續列舉下去,無疑會大大降低效率。那麼,能否通過簡單的判斷,將其剪去呢?答案是可以的。很容易證明,如果(min – 1) div number < max div number,則區間內存在可以被number整除的數。因為,如果區間[min, max]內存在可以被number整除的數,也即是從min到max中至少有乙個數能被number整除,那麼區間[min – 1, max]內的數被number除得的商肯定不止一種,所以(min – 1) div number必然小於max div number。

反過來,如果(min-1)div number=max div number,則[min,max]內不存在可以被number整除的數。

證明如下:

假設[min,max]內存在可以被number整除的數

如果(min-1) div number=max div number,則min div number = max div number,那麼①min等於max,且min和max均可以被number整除,或者②max>min,min可以被number整除,①的情況下可推出,(min-1) div nunber②的情況下也可以推出(min-1) div number我們只需列舉符合要求的number;至於不符合的,可以剪去

。此外,我們列舉的質數可能會達到很大。因為給出的整數最大可以達到4,000,000,000,它的質因數自然最大也可以到100,000,000的數量級。如果按上面的方法列舉,顯然無法承受時間的壓力。但是,我們又可以看到,對於區間內的任一整數,它包含的大於sqrt(4,000,000,000) = 2*31623的質因數最多隻可能有乙個。因此,我們只需列舉小於2*31623的質數。如果對乙個符合要求的number(即,可以證明區間內至少存在乙個數可以被number整除),無法找到乙個小於2*31623的質數p使得number * p * x ∈ [min, max](x為一正整數)。那麼,可知number需要乘以乙個大於31623的質數才能得到number』,使得number』 ∈ [min, max]。根據前面介紹的乘法原理,只需將number包含的約數個數乘以2,即得number』包含的約數個數。

我們還能看到,如果當前搜尋狀態為(from, number, total),其中from是指當前列舉到的質因子(按從小到大列舉),total是指number中包含的約數個數。那麼剩下的因子數最多為q = [log

from(max / number)],這些因子組成的約數個數(即上述求約數個數時用到的一串乘積)最大為2

q。當前所能取到的(理想情況)最大約數個數就是total * 2

q,如果這個數仍然無法超過當前最優解,則這一分支可以剪去。

深度搜尋的過程,從表面上看是乙個指數級的複雜度。其實不然,更準確的說,複雜度應為o(p

logn)。因為,它的指數增長速度是隨n成對數級增長,本質上說,還是多項式級的演算法。而且我們還進行了一些剪枝,由於列舉中的分支多數為非法的,因此通過剪枝可以去掉絕大多數的分支。這樣就大大提高了程式的效率。儘管資料規模很大,但對於極限資料仍然可以做到「一閃即出」。

根據以上分析給出以下**

#include

using namespace std;

void primes(); //用篩選法產生質數存於prim陣列中

void search(long from, long total, long num, long low, long up);

const long maxp = 100000;

long prim[maxp];

//long max, numb ,pcount; //max存放最多約數個數,numb存放約數個數最多的數

long max_num;

long numb,pcount;

int main()

else

cout << max_num << endl << numb << endl;

return 0;

}//求小於100000的素數表

void primes() 

}long ii, j;

for (ii = 2, j = 0; ii <= maxp; ii++)

if (get[ii]) prim[++j] = ii;

pcount = j;

}// 區間[low,up]上,tot為當前約數最多個數,num為約數個數最多的數,

//from表示現在是第幾個質數。

void search(long from, long total, long num, long low, long up)

*///這是我修改後的**

if ( (total >=max_num&&num>=numb) )

}/**

此處**較為簡練,但包含的內容很多,較難理解.

因為所給區間[low,up]內有可能包含乙個數a,a的某個因子大於100000,超出了素數表的搜尋範圍

有可能搜尋不到a,怎麼解決這個問題?可以看出來,如果a含有大於100000的因子,則a的因數個數為當前搜尋到的因數個數乘以2

如果,乙個區間內包含了乙個和a含有除了大於100000的因子的數以外的其他因子的數b,那麼b的因數個數一定多於a.因此對於

含有a的區間,我們只考慮[a,a]這種可能,其他的區間情況,a不可能是含有因數最多的數。

此處給出的判斷條件是low==up,low>num。就包含了[a,a]這種情況,當然此處會有多餘的情況,如low=up=b,b可以分解為若干個較小的因子的乘積

但並不影響最終的結果。

*/if ((low == up) && (low > num)) 

search(from, total*2, num*low, 1, 1);

/*此處就是深度搜尋的** ,有乙個地方需要補充說明一下,在一條搜尋路徑上,區間邊界不斷除以乙個素數,

會越來越小,因為每次使用的都是除法取整,會有誤差。但這裡可以證明,累積誤差不會導致的除法結果的整數部分不同。

即:a/(x1*x2*..xn)=a/x1*1/x2*1/x3*..*1/xn,證明:設a=c(x1*x2*..*xn)+r(r為餘數),則a/x1*1/x2*..1/xn=c+r/x1*1/x2*..*1/xn,整數部分仍然為c

*/for (long i = from; i <=pcount; i++)

/*此處也是剪枝處理

*/m = 1 << m;

if (total < max_num / m) return;

}//end else

}//end for}

最多約數問題

問題描述 正整數x的約數是能整除x的正整數。正整數x的約數個數記為div x 例如,10有4個約數 1 2 5 10。設a和b是兩個正整數,試計算a和b之間約數個數最多的數x。演算法設計 對於給定的2個正整數a b,程式設計計算a 和 b 之間約數個數最多的數。資料輸入 輸入資料由檔名為input....

最多約數問題

問題描述 正整數x的約數是能整除x的正整數。設a和b是兩個正整數,a b,找出a和b之間約數個數最多的數x。輸入輸出樣例 input 1 36 output 9還是列舉求a,b之間每個數的約數個數,算約數個數採用分解質因數的方法。質數p1 p2 p3 pn,正整數a分解質因數的結果是 a p1 m1...

最多約數問題

題目分析 正整數 x的約數是能整除x的正整數,其約數的個數記為div x 例如div 10 4。設 a 和 b 是兩個正整數,找出 a 和 b 之間約數個數最多的數 x的約數個數。1 暴力法 依次記錄區間範圍內每乙個數的約束個數,取最大值 int prime int a,int b 2 質因子分解 ...