若是咱們將小於10的全部是3或5倍數的天然數列出來,咱們獲得3,5,6和9,它們的和是23。與之相似,計算1000如下全部是3或5的倍數的天然數的和。html
分析:此題至少有兩種解法,第一種解法較爲直接,將1000如下全部3或5的倍數列出再求便可,在python中使用列表推導式只須要一行代碼便可。第二種思路是使用求和公式,分別求出1000如下全部三的倍數和五的倍數的和再減去十五的倍數的和,即:
\[ s=\sum_{i=1}^{333}3i+\sum_{i=1}^{199}5i-\sum_{i=1}^{66}15i=\frac{3}{2}\cdot333(333+1)+\frac{5}{2}\cdot199(199+1)-\frac{15}{2}\cdot66(66+1) \]
第一種思路的實現代碼以下:python
def main(): ans = sum([x for x in range(1,1000) if x%3==0 or x%5==0]) return ans
斐波那契序列中的數都是由前兩項加總得出,假設第一與第二項爲1與2,則前十項分別爲:
\[ 1,2,3,5,8,13,21,34,55,89 \]
考慮不超過四百萬的斐波那契數,計算其中偶數斐波那契數的和。算法
分析:此題至少有三種解法:第一種咱們能夠編寫一個計算斐波那契數的函數,而後篩選出不超過四百萬的數並對其中的偶數求和;第二種思路是咱們能夠直接獲得一個求偶數的斐波那契數的公式,避免篩選的過程;第三種思路咱們可使用斐波那契數列的通項公式,從而能夠不使用循環來求和。這裏我同時簡單介紹下三種思路。編程
計算斐波那契數是一個經典的編程問題,可使用多種方法求解,從最簡單的遞歸函數解法,到迭代計算和動態規劃的解法,以致矩陣乘法的解法,固然還能夠直接用通項公式求解。以上方法中,遞歸函數的方法效率最低,一般不會使用。而通項公式的算法因爲涉及到無理數,存在浮點計算偏差的問題,這裏咱們也不涉及。至於迭代計算、動態規劃和矩陣乘法這三種方法在問題規模較小時運行時間沒有太大的差異,因此這裏我只介紹迭代計算的方法,其它方法相關的詳細資料你們能夠上網查詢,很容易就能夠找到。app
首先看第一種思路。咱們都知道斐波那契數列的定義,在設定了第一項和第二項後(題目假設這兩項分別是一和二),以後每一項都等於其前兩項之和,所以咱們使用臨時變量來保存中間的計算過程,並經過迭代計算獲得最後的結果。假設開始時\(a=1,b=2\),則在下一步將\(b\)賦值給\(a\),將\(a+b\)的值賦值給\(b\),如此迭代計算實際上就是斐波那契數列的生成過程。要解決題目中的問題,只須要生成一個足夠長的斐波那契數列,篩選其中小於四百萬的偶數再加總便可。函數
第二種思路則是考慮到題目只要求對偶數斐波那契數進行加總,這時候求出全部的斐波那契數顯得不太經濟,有沒有辦法只求偶數斐波那契數?觀察一下上面的斐波那契數列,咱們能夠發現第二項是偶數,間隔兩個奇數第五項也是偶數,再間隔兩項第八項也是偶數。之因此會表現出這樣的規律,是由於加法的奇偶性法則致使的。咱們知道一個奇數加上一個奇數一定是一個偶數,一個偶數加上一個偶數也是一個偶數,然而一個奇數加上一個偶數則一定是一個奇數。在上面的數列中,第一項是奇數,第二項是偶數,則第三項是奇數,第四項等於第二項加上第三項,也就是一個奇數加上一個偶數,因此第四項也是奇數;第五項等於第三項加第四項,這兩項都是奇數,因此第五項一定是偶數。依次類推,第8、11、十四等等項也是偶數。根據題意咱們設\(F_1=1,F_2=2\),則有:
\[ \begin{aligned} F_n&=F_{n-1}+F_{n-2}\\ &=F_{n-2}+F_{n-3}+F_{n-3}+F_{n-4}\\ &=2F_{n-3}+F_{n-2}+F_{n-4}\\ &=2F_{n-3}+(F_{n-3}+F_{n-4})+(F_{n-5}+F_{n-6})\\ &=3F_{n-3}+F_{n-4}+F_{n-5}+F_{n-6}\\ &=4F_{n-3}+F_{n-6} \end{aligned} \]
顯然,由咱們上面的推理可知,\(F_n,F_{n-3},F_{n-6}\)都爲偶數,則咱們能夠把它們表示成爲一個新的數列,稱爲偶數斐波那契數列,第一項爲二,第二項爲八,遞推公式以下:
\[ EF_n=4EF_{n-1}+EF_{n-2}\quad(EF_1=2,EF_2=8) \]
則咱們能夠根據這個遞推公式,使用上面的迭代計算方法計算全部的偶數斐波那契數,省略一半的計算量。優化
第三種思路是直接使用斐波那契數列的通項公式計算各項斐波那契數,並篩選其中不超過四百萬的偶數並求和。這種方法的好處是其時間複雜度爲\(O(1)\),是效率最高的算法。壞處是斐波那契數列的通項公式涉及到無理數,從而會出現浮點計算影響精度的問題,當N越大,這種浮點偏差就會越嚴重。雖然咱們可使用更高精度有浮點數或者專門的外部庫來解決這個問題,但無疑引入了更多的複雜度,因此這裏咱們再也不詳細介紹這種方法。感興趣的同窗能夠參見維基百科。es5
第二種思路的實現代碼以下:spa
def main(N=4e6): a,b = 2,8 arr = [a,b] while True: a,b = b,4*b+a arr.append(b) if b > N: return sum(arr[:-1])
13195的質因數分別爲5,7,13與29,600851475143最大的質因數是多少?code
分析:求解質因數的方法較多,最經常使用的方法爲短除法,即對於任意大於2的天然數N,先用N除以2,再用所得之商即(N/2)再除以2,直到商不能爲2所整除,此時將被除數加一併比較其平方是否小於被除數,若是小於則再用商除以3,如不能整除,則除以5(由於全部2的倍數即偶數因數已在第一輪不斷除以2時被排除了,因此以後都要加二)。這樣循環下去直到除數的平方再也不小於被除數,則退出循環,最後獲得的N即爲最大的質因數。實際執行中,要分兩層循環,外層循環判斷除數的平方是否小於被除數,內層循環判斷被除數是否能夠整除除數。
def main(n=600851475143): i = 2 while i * i < n: while n%i == 0: n /= i i += 2 if i>2 else 1 return n
迴文數即從正反兩邊讀都是同樣的數,兩個二位數的乘積中最大的迴文數爲\(9009=91*99\),尋找兩個三位數乘積中最大的迴文數。
分析:題目說要找到兩個三位數相乘獲得的最大回文數,因此這個數最小隻能是\(100^2\),最大隻能是\(999^2\)了,也就是說處於區間\([10000,998001]\)之間,這個區間中絕大部分是六位數,並且題目也要求最大的迴文數,因此咱們先假設所求數是一個六位數,看能不能找到一個符合要求的數。假設這個六位數是\(abccba\)的形式,則有:
\[ \begin{aligned} 'abccba'&=10^5a+10^4b+10^3c+10^2c+10b+a\\ &=100001a+10010b+1100c\\ &=11(9091a+910b+100c) \end{aligned} \]
能夠很明顯的看出知足要求的六位數必然是11的倍數,所以在檢測六位數是不是迴文數以前,咱們能夠先檢測它是不是11的倍數,這樣能夠加快驗證的速度。咱們可使用列表推導式從大到小生成兩個三位數的乘積,再檢測這個乘積是不是11的倍數以及是不是一個迴文數,最後對這個列表求最大值即爲所求。須要注意,這裏用Lambda表達式定義了一個判斷特定數字是不是迴文數的函數,方法就是把數字轉化爲字符串,再判斷這個字符串是否與其翻轉字符串相等。
def main(): r = range(999,99,-1) is_palindrome = lambda x : str(x) == str(x)[::-1] ans = max([i*j for i in r for j in r if (i*j)%11==0 and is_palindrome(i*j)]) return ans
2520是能夠被從一到十全部天然數整除的最小的數,即爲從一到十的天然數的最小公倍數,求從一到二十全部天然數的最小公倍數。
分析:這道題至少有兩種解題思路,第一種思路是編寫一個計算最小公倍數的函數,而後對一至二十的全部整數求最小公倍數。在python的math庫中有一個計算最大公約數的函數,咱們能夠根據歐幾里德公式,從兩個數最大公約數推出兩個數的最小公倍數,即對於任意天然數\(a,b\),設兩個數的最大公約數爲\(gcd(a,b)\),則兩個數的最小公倍數
\[ lcm(a,b)=\frac{ab}{gcd(a,b)} \]
據此,咱們能夠先求出兩個數的最小公倍數,再用這個最小公倍數與第三個數求最小公倍數,這裏能夠利用reduce()
函數迭代求出多個數的最小公倍數。這種思路在問題規模較小時表現良好,對題目中的問題也能夠在幾微秒內給出答案。可是咱們能夠更深刻的分析問題,能夠發現一種更爲高效的方法。
咱們來看一個簡化的問題,題目中說一到十的全部整數的最小公倍數是2520,這個數能夠如何求出來?咱們先對二到十的全部數進行質因數分解,結果以下:
\[ [2:2,3:3,4:2^2,5:5,6:2\times3,7:7,8:2^3,9:3^2,10:2\times5] \]
要想找到一個最小的整除上面全部數的數,咱們能夠從十之內的全部質數開始尋找,首先看第一個質數二,上面列表中二的最大指數是三,也就是八的質因數分解結果,若是一個數可以被八也就是二的三次方整除,那它必然能夠被二和四整除,也就是二的二次方和一次方整除。再來看三,表中三的最大指數是二,那麼能夠被三的平方整除的數必然能夠被三整除。下一個素數是五,表中五的最大指數是一,因此這個數應該整除五。最後一個素數是七,最大指數也是一,因此這個數也應該被七整除。綜上,整除表中全部數的最小數應該就是全部數的質因數分解中,各個素數的最高次冪相乘的結果,也就是\(2^3\times3^2\times5\times7=2520\)。
顯然咱們直接利用這個算法來求解題目中的問題,可是咱們仍然能夠對這個算法作進一步的改進。按照這個算法,咱們須要對全部數進行質因數分解來求取各個質數的最大次冪,這個計算至關耗費時間,有沒有更快的方法?由於咱們只關心各個素數的最大次冪,而不關心各個數的具體質因數分解方式,顯然任何素數的最大次冪是使得其不超過終點數的數,好比求一至十的最小公倍數,終點數是十,則二的最高次冪只能是三,而不能是四,由於二的四次方等於十六大於十;同理,三的最高次冪只能是二,由於三的三次方也會大於十。依次類推,五和七的指數都只能是一,因這它們的平方都超過十。在這裏咱們看到另外一個優化技巧,也就是咱們只須要求那些其平方不大於終點數的素數的最大次冪,由於那些平方大於終點數的素數,其指數必然是一。最後,根據上面的推理,要求素數的最高次冪,只須要求以該素數爲底,以終點數爲真數的對數並下取整便可,即有:
\[ p_i^e\le N \Rightarrow e=\lfloor ln(N)/ln(p_i)\rfloor \]
對於題目中的問題規模,第二種算法相對於第二種算法的優點並不明顯。在個人電腦上,第一種算法耗時5.5微秒,第二種算法耗時33.6微秒;但當\(N=1000\)時,第一種算法耗時938微秒,第二種算法耗時618微秒,第二種算法時間已經更短。進一步增長問題規模,當\(N=10^4\)時,第一種算法耗時63.5毫秒,第二種算法耗時7.22毫秒;當\(N=10^5\)時,第一種算法耗時5.85秒,第二種算法耗時198毫秒,二者的效率差距已經差異很大。整體而言,第一種算法的時間複雜度接近\(O(n^2)\),第二種算法的時間複雜度接近\(O(nlogn)\)。兩種算法的實現代碼以下:
# approach 1, time complexity = O(n^2) from math import gcd from functools import reduce def lcm(n): def lcm(a, b): return (a * b) // gcd(a, b) return reduce(lcm, range(1,n+1)) # approach 2, time complexity = O(nlog(n)) from sympy import primerange from math import sqrt,log,floor def main(n=20): primes = list(primerange(1,n)) i,ans = 0,1 while primes[i] < sqrt(n): e = floor(log(n)/log(primes[i])) ans *= (primes[i])**e i += 1 for p in primes[i:]: ans *= p return ans
前十個天然數的平方的和爲:
\[ 1^2+2^2+3^2+\cdots+10^2=385 \]
而前十個天然數和的平方爲:
\[ (1+2+3+\cdots+10)^2=55^2=3025 \]
二者的差爲\(3025-385=2640\),求前一百個天然數的和的平方與平方的和之間的差值。
分析:此題能夠直接使用求和公式求解,則:
\[ S(n)=(\frac{1}{2}n(n+1))^2-\frac{1}{6}n(n+1)(2n+1)=\frac{1}{12}n(n-1)(n+1)(3n+2) \]
代入\(S(100)\)能夠直接算出結果,代碼以下:
def main(n=100): ans = n*(n-1)*(n+1)*(3*n+2)/12 return int(ans)
列出前六個質數,咱們能夠發現第六個質數爲13,那麼第10001個質數是多少?
分析:通常而言,對於第\(n\)個素數\(p_n\),知足如下不等式(參見素數計數函數):
\[ ln(nln(n))-1<\frac{p_n}{n}<ln(nln(n)) \]
在這裏咱們只關注上界,則能夠獲得\(p_n<nln(nln(n))=nln(n)+nln(ln(n))\),咱們先篩選出從2到上界的全部質數,再取其中的第10001個質數,即獲得結果。
from sympy import primerange from math import log,ceil def main(n=10001): upper_bound = ceil((log(n)+log(log(n))) * n) primes = list(primerange(1,upper_bound)) return primes[n-1]
在下面1000位數中,連續四個數的最大乘積爲\(9*9*8*9=5832\):
尋找連續十三數的最大乘積,這個乘積是多少?
分析:此題解題思路較爲直接,分爲如下步驟:1)將以上數字存入到TXT文件中,用python導入並存入到一個LIST中;2)從LIST中第0位開始直到第987位,依次求連續十三位數的乘積並存入到結果LIST中;3)求結果LIST的最大值獲得結果。
from functools import reduce def main(): with open('euler/ep08.txt','r') as f: data = '' for line in f.readlines(): data = data + line.strip() res = [] for i in range(988): sub = [int(x) for x in data[i:i+13]] prod = reduce(lambda x,y:x*y, sub) res.append(prod) return max(res)
畢達哥拉斯三元數是指一類三個天然數的集合,其中\(a<b<c\)且\(a^2+b^2=c^2\),例如\(3^2+4^2=5^2\)。僅存在一組畢達哥拉斯三元數使得\(a+b+c=1000\),求\(abc\)。
分析:此題至少有兩種解題思路,第一種思路以下:爲了讓解題思路更加通常化,設三角形的周長爲\(p\),則有\(a+b+c=p\),故\(c=p-a-b\),則有:
\[ a^2+b^2=c^2=(p-a-b)^2=p^2+a^2+b^2-2pa-2pb+2ab \]
則能夠獲得:
\[ b=(p^2-2pa)/(2p-2a) \]
則使得\(b\)爲整數的\(p\)與\(a\)均可以知足題目的要求,從而能夠獲得一組符合要求勾股三元數。此外,不失通常性,咱們假設\(a\le b<c\),又由於\(a+b+c=p\),則應有\(a<p/3\),這能夠縮小咱們須要篩選的數\(a\)的範圍。由於題目已經說明在\(p=1000\)時只存在一組勾股三元數,則咱們只須要編寫一個函數尋找一個合適的\(a\)值使得\(b\)爲整數,一旦找到就能夠計算\(c\)並返回三者的乘積。
第二種思路須要用到歐幾里德公式,對於\(m>n>0\),咱們知道如下三個數能夠構成勾股三元數:
\[ a=k(m^2-n^2),b=2kmn,c=k(m^2+n^2) \]
則有:
\[ a+b+c=k(m^2-n^2+2mn+m^2+n^2)=k(2m^2+2mn)=2km(m+n)=1000 \]
則咱們能夠獲得\(m(m+n)=500/k\),顯然\(m<m+n\),同時\(m,n\)都是\(500/k\)的因數,則咱們能夠獲得\(m<\sqrt{500/k}\)。同時咱們有\(n=500/k\cdot m-m<m\),則有\(m>\sqrt{250/k}\),即咱們能夠是獲得\(m\)的取值範圍是\([\sqrt{250/k},\sqrt{500/k}]\)。如今咱們假設\(k=1\),則\(m\in[15.81,22.36]\),這其中的整數只有16,17,18,19,20,21,顯然只有20是500的因子,則有\(m=20,n=5\),則有:
\[ a=20^2-5^2=375,b=2\times20\times5=200,c=20^2+5^2=425 \]
將\(a,b,c\)三者相乘即爲題目所求。顯然第二種思路能夠直接筆算出結果,因此這裏咱們只列示第一種思路的代碼:
def main(p=1000): for a in range(1,p//3): n,d = p**2-2*p*a,2*p-2*a if n % d == 0: b = n/d c = p-a-b return int(a*b*c)
\(10\)如下的質數的和爲\(2+3+5+7=17\),求全部兩百萬如下的質數的和。
分析:對此題的詳細分析請參見個人另外一篇文章。