這是一道網上流傳的題目,解這道題給了我2個感悟:
第一,看上去簡單的事情,往往十分復雜,其復雜程度甚至遠遠超過了你的想象;
第二,你絕望的時候,不代表沒有希望。
希望,往往就在遠方,
雖然可能很遠,很遠。
一、聲明
首先聲明,這個題目的解是由 Quora 上的作者 Alon Amit 給出的。 這里按照這個思路進行了轉譯,同時增加了很多內容,其目的是讓初級數學水平的人都盡量能夠理解。
第一次看到這個題目的時候,我以為是一個小學生題目,甚至以為是幼兒園的智力題。然而當試圖算出答案的時候,發現問題不簡單。試驗了一個又一個數字,得出的結論是這道題大概率沒有正整數解。當然這是猜的。最終在網上找到了答案,卻驚奇地發現,其中涉及到的數學知識令人發指,這哪里是小學生的題目,用 Alon Amit 的原話說:
大概99.99995%的人解不開這道題,甚至一大幫頂尖學府里的數學家,如果他正好不是數論專家的話,他們也不會做。
Roughly 99.999995% of the people don’t stand a chance at solving it, and that includes a good number of mathematicians at leading universities who just don’t happen to be number theorists.
如果你對數學不感興趣,你可以直接跳到文章第八段的最后看一眼答案了。
不過如果你感一點興趣,雖然它很難,但我相信你能看得懂。
二、幾個初步結論
書歸正傳,開始解題。
首先,為了方便,我們把原題描述為下面的形式:
(1)
要求的是a、b、c這3個變量的正整數解(因為蘋果鳳梨香蕉的個數總不至於為負數嘛,這也符合一般表述的常識)。
注意,從這個式子中我們可以得出幾個初步結論:
初步結論一:只要有一個解,就有無窮多個解。
為什么呢?我們在第一個分式的分子和分母同時乘上一個正整數t,可以發現:
那么也就是說,只要(a, b, c)是原來方程的解,那么(7a, 7b, 7c)也必然是這個方程的解(這是t=7的情況,t還可以等於其他正整數)。這樣也就等於有了無窮多個解。
初步結論二:如果有解,(a,b,c) 三個未知數的解是對稱的。
也就是說調換a、b、c的位置,不影響結果。
這個是顯而易見的,如果我們得到一個解(a, b, c) ,那么(b, a ,c)也一定是它的解,(c, b ,a)也一定是它的解。
初步結論三:3個變量中不能有0。
這個我們來證明一下。假設某一個變量是0,不妨是a吧(因為是對稱的嘛,誰是0都一樣)。那么原式(1)就變成:
這里顯然b和c不能再有0了,否則等式沒有意義了。
這個方程等同於
這是初中的一元二次方程題目,解出來
很明顯,這是無理數,而兩個整數b和c相除等於一個有理數,並不是無理數。
所以假設不成立,也就是說a、b、c中任何一個數字都不能為0。
初步結論四:求解(a,b,c)的正整數解,實際是求這三個未知數的正有理數解。
這是因為:有理數都可以表現為分數的形式,如果我們得到a、b、c的正有理數的解,那么我們同時把這3個數字乘以它們的公分母,那么我們就得到了3個整數。根據前面的初步結論一,我們很容易知道,這3個整數就是原方程(1)的解。
好了,研究完一些初步結論,我們來開始解剖原題。如果我們在原方程(1)的兩邊同時乘以他們的公分母(b+c)(a+b)(a+c),然后再移項、合並同類項,雖然有點繁瑣,但我們可以得到下面這個方程:
(2)
這是與原方程(1)等價的,我們求它的解就可以了。
根據前面的結論四,我們只需要求出他們的有理數解。
麻煩來了,這個方程看起來很復雜,而且幾乎無從下手啊。
解高次方程有多難呢?
如果我們碰到一個一次方程,比如,這個非常容易,這個難度打個比方大約相當於一碗水;
如果我們碰到一個二次方程,比如前面碰到的這個,這個也有初等數學的方法去解決,也比較簡單,這個難度大約相當於一個小池塘;
但如果我們碰到的是一個3次方程,比如這個樣子的,不好意思,你一下子就來到了馬里亞納海溝,你面對的是一個非常深奧的題目,而且這個領域有無窮的問題等着去解決;
如果再高次方,那真是。。。太難了。有興趣的話去看看費馬大定理吧(對,就是那個困擾了人類350多年的超級大難題)。
本題,就是一個三次方的方程。
而且,並不是簡單地求解,而是要求出有理數解。
有理數解,是什么意思?怎么個求法?
三、不定方程的基本知識——什么叫特解與通解
求一個方程的有理數解或者整數解,往往是數論方面的題目。而要解出本題,還要用到橢圓曲線。
我們先了解一些簡單的數論知識。
打個比方說,我想求出下面這個方程的解:
(3)
你肯定會說,這有無窮多組解啊, x 也好, y 也好,可能是任何數字。
沒錯。但如果是求整數解呢?似乎也是有無窮多組解。但是,你有沒有什么辦法把所有的解都描述出來呢?
有的。首先我們可以很容易看出(x=2,y=4)是它的一組解,這個呢,叫做“特解”。當然,特解有很多個。
然后我們利用這個“特解”,我們設計一個式子:
,其中t是任意整數。
我們可以很容易地驗證,首先上面這個解是滿足方程(3)的,另外我們還可以發現,上面的這個式子也包括了方程(3)的所有整數解,所以這個解就叫做“通解”。
這就是一次不定方程,又叫丟番圖方程(Diophantine Equation)。
我們不需要太深入了解數論的知識,只要知道“特解”、“通解”的概念就可以了,並且求取這樣方程的“通解”,常常就是從一個“特解”入手的。
剛才解的是一次的丟番圖方程,然而我們現在面對的方程(2),是3次的。
四、神奇的“降維”操作——3元變2元,曲面變曲線
不得不說,后面的變化,確實有點復雜,也很有些難度。但總的來說,如果暫時不考慮其“所以然”的話,“知其然”還是大致可以做到的。
在現代數學,有一個分支,叫”代數幾何學“(algebraic geometry),注意這個不是中學學習的代數和幾何,而是研究的代數和幾何之間相互關聯對應問題的學科。
其中,就有一個重要的理論,是發現了代數問題在幾何上的對應性,從而可以用”幾何“的方法來解決”代數“的問題。說得直白一點,就是我們遇到一個無法下手的代數方程的時候,我們或許可以用”幾何“的方式來解決。
今天,我們正好碰到的就是這個問題。我們再看一眼方程(2):
(2)
這是一個有着3個變量的方程。它是3維的,對應的是一個曲面;
現在有一個好消息,就是我們可以通過某一種變換,將其變為一個只有x和y兩個變量的方程,一種叫做維爾斯特拉斯典范型(Weierstrass form)的方程,像下面這種形式的:
或是
(注意,此處的a、b、c非方程(2)的變量,這里是方程系數)
並且我們可以描述出(a, b, c) 和(x, y)之間的關系。
,
那么方程(2)就轉化成了:
(4)
而方程(2)中的未知數a, b, c則分別為:
(5)
這樣的好處呢,是把有a、b、c3個變量的方程,轉化成了x、y兩個變量的方程。(用專業術語說,其實是把一個三維平面“映射”到了一條二維曲線上)
還有一個重大的好處,就是方程(4)是一個橢圓曲線的方程。
現在的問題轉化為:我們只需要解方程(4)的有理數解,然后就可以根據上述公式對應地計算出a、b、c。
你看到這里可能覺得像是在變戲法,但其實真不是。推導的過程非常復雜,這里就暫時不寫出來了。好在我們驗證它對不對還是比較方便的。
運用一些技巧,我們可以發現(x= -100, y=260)是方程(4)的一個“特解”。雖然這個過程也有點復雜,但好歹我們驗證它還是容易的。我們把這兩個數代入方程(4),首先發現它是成立的;再代入到上述等式(5),可以求出
,
,
同時乘以14,得到 a=4, b=-1, c=11,代入到,發現果然等於4。
不過這個里面有個負數,肯定是不行的。
那么我們有沒有辦法通過這樣一個“特解”,來找到更多的“特解”呢?如果我們找到更多的特解,會不會最終就能夠讓a、b、c變為正數呢?
我們試一下吧。
五、雙有理等價——通過2個有理數解尋找3次方程的第3個有理數解
我們先來看一下3次方程,如果一個有理數系數的3次方程,我們知道其中的2個有理數解,我們有沒有辦法找出它的第3個有理數解?
那當然是可以的。假設我有一個3次方程,其中p、q、r都是有理數,我有它的2個有理數解。
那么它一定可以表示為,其中x1、x2、x3,分別是3個方程的根。我在知道x1和x2的情況下,是可以通過用多項式除法來求出x3。怎么個操作法呢?我們來看個例子:
假設我們有這樣一個方程,我們知道x1 = 1,以及x2 = 2是它的兩個解,那么等號左邊實際是包含了(x-1)和(x-2)這兩個因式,所以我們用類似除法的方法來計算一下(僅僅用每一項的系數即可):
等號左邊的多項式對應的系數是(1 -6 11 -6),(x-1)對應的系數是(1, -1)
除下來我們就可以得到,繼續拿右邊括號的多項式除以(x-2)
於是我們得到,這樣第三個根就是 x = 3 。
通過上面的操作我們可以看出,所有的操作都是加減乘除的操作,在系數和已知的兩個根都是有理數的情況下,在沒有無理數參與、也沒有開根號、取對數等操作的前提下,這第三個根必然也是有理數。
回到我們的原題。
沒錯,你一定發現了,我們現在並沒有2個根,我們只有一個根(x = -100,y = 260),那怎么辦呢?我們可以把它當作是2個根(也就是“重根”,在幾何上可以理解為兩個點無限接近),然后通過它來找出第三個根。再通過其中兩個根,再找出不同的其他根,如此往復。
要理解這個事情,我們得回到圖形上來,也就是幾何方法。
六、目標函數——橢圓曲線
我們現在要解的是方程(4),我們要找出它的有理數點,並且我們要讓a、b、c成為整數,現在我們已知(x = -100, y = 260)是一個特解。
我們先將它畫出來,看看是什么樣子。由於左邊是y2,這個圖顯然是關於x軸對稱的。畫出來的圖形近看這樣的,就像一條小魚:
不過注意了,這條“魚”的身體跟尾巴有一點點分離,因為在原點的左邊有一小塊區域y2為負值,在實數范圍內沒有意義,所以也就沒有y值。
當然這是近看,如果我們拉遠一點,發現它更像一個”繩結“:
再遠看,其實這個曲線整體是兩根“辮子”:
當然這兩根“辮子”是伸向了無窮遠。
至於這個近看像小魚、中看像繩結、遠看像辮子的曲線為什么叫“橢圓曲線”,這是因為它的一些數學性質跟橢圓、拋物線等曲線有相同的地方,所以一起被歸到了橢圓曲線的類別。這是題外話,我們不需要太關注。
七、雙有理等價的幾何表示
那么我們前面所提到的雙有理等價(就是通過2個有理點,尋找第三個有理點),在圖形上面是什么意思呢?
我們來看下圖:假設P1、P2是曲線上的兩個有理點(即橫豎坐標都是有理數),我們通過這兩點做連線,那么有兩種情況,一種是跟這個橢圓曲線相交到了第三點(如圖P3),另一種是比如正好跟y軸平行,因而無論如何也不會跟曲線有第三個交點。后一種情況屬於特殊情況,我們只需要適當規避。我們考慮前一種情況,這時注意了:
因為P1(x1, y1) 、P2(x2, y2)是有理點,所以其斜率必然也是有理數。也就是說P1、P2所定義的直線
,其系數都是有理數。
我們將代入到
,整理后我們可以發現,這就是一個關於x的3次方程。而它的三個根就對應於P1、P2、P3的3個橫坐標。
既然x1、x2都是有理數,這個方程的系數也都是有理數,根據前面第五塊雙有理等價的結論,可以斷定x3也是有理數,從而y3也是有理數,從而P3也是有理點。
巧妙的是,由於曲線關於x軸的對稱性,P3關於x軸的鏡像點,必然也是有理點(因為僅僅是y坐標乘以-1)。而在代數幾何領域,這個點被稱為“P1+P2”。我們首先不用糾結於加法的重新定義。我們只需要知道這個新的有理點意義非凡,因為用這個“P1+P2"點,我們與P1連線的話(圖中黃線),我們又得到了一個有理點A!也就是說,我們又找到了一個(x, y)的有理數解,也就是又找到了(a, b, c)的整數解。雖然(a, b, c)中仍然有可能有負數,但只要我們不停地迭代下去,就可能可以找到全部為正數的解!
回到我們的題上來。現在我們只有一個有理點(-100,260),我們將其想象為P1與P2無限接近,產生了“重根”,那么由P1、P2所確定的直線將會是橢圓曲線在該點處的切線,而其切線的斜率將是該處的導數(此處對中學的小朋友可能略微有一些超綱)。我們推導一下這個導數的計算過程:
對兩邊求導,得到
,進而得到
,這個就是斜率k。
將x=-100,y=260代入,得到斜率。從而經過P點的直線函數為
我們將此y值代入到橢圓曲線中,得到一個看起來有點復雜的方程:
合並同類項,得到這樣一個關於x的三次方程:
這個方程的系數看起來有點大,好在我們知道它有兩個根都是x=-100,我們運用上面提到的多項式除法,兩次除以(x+100)項,最終得到的另一個交點的x值:
代入到直線方程,從而得出該交點為
該點關於X軸的鏡像點即為P+P=2P, 其坐標為
,如下圖。
我們將得到的x和y值代入上述式(5),計算出a、b、c的值,並乘以它們的公分母,得到的整數值分別為:
a = 9499
b = -8784
c = 5165
經過驗算,確實滿足
不過,還是有負數!工作還得繼續。
下面要做的,是把點P和2P連線,再去尋找3P的有理點,再算出a、b、c,再去檢查是否全部正整數。
不過你一定發現,式子越來越復雜,靠手算已經行不通了。
八、尋找終極答案——計算的事情交給PYTHON
用計算機來計算,還是比較方便的。下面是迭代計算的python代碼。
""" Created on Sun Dec 6 13:21:48 2020 @author: jacky """ from fractions import Fraction import math # 多項式的輾轉相除法 def duc(p, poly_nrt): rv = [1, -p[0]] if len(poly_nrt) == 4: result = [1] result.append(poly_nrt[1] - rv[1]) result.append(poly_nrt[2] - result[1] * rv[1]) result.append(poly_nrt[3] - result[2] * rv[1]) elif len(poly_nrt) == 3: result = [1] result.append(poly_nrt[1] - rv[1]) result.append(poly_nrt[2] - result[1] * rv[1]) else: print('Error! Length of coefficients does not match.') return(0) if result[-1] != 0: print('Error! Result is Wrong.') return(0) return result[:-1] #定義加法 def add(p1, p2): #如果p1、p2是同一個點,那么該處斜率是該點的導數。如果不是同一個點,那么通過坐標可以計算出斜率 if p1 == p2: slope = Fraction(1,2) / p1[1] * (3*p1[0]**2 + 218*p1[0] + 224) else: slope = Fraction((p2[1] - p1[1]) / (p2[0] - p1[0])) #Function: y - p1[1] = slope * (x - p1[0]) #直線方程的系數(y = kx + b),k、b即為系數 coe_line = [slope, p1[1] - slope * p1[0]] #直線方程代入橢圓方程,計算方程系數 coe_ellipse = [1, 109 - coe_line[0] **2, 224 - 2 * coe_line[0] * coe_line[1], -coe_line[1]**2] res_1 = duc(p1, coe_ellipse) res_2 = duc(p2, res_1) x = -res_2[1] y = (slope * (x +100) +260) * (-1) return ([x,y]) p1 = [-100, 260] p2 = [-100, 260] a=0 b=0 c=0 #循環數 k=2 #尋找到a、b、c全部大於零為止 while not (a>0 and b>0 and c>0): p2 = add(p1,p2) x = p2[0] y = p2[1] a = Fraction(56-x+y, 56-14*x) b = Fraction(56-x-y, 56-14*x) c = Fraction(-28-6*x, 28-7*x) a/(b+c) + b/(a+c) + c/(a+b) #公分母 common_deno = a.denominator * b.denominator * c.denominator a = a * common_deno b = b * common_deno c = c * common_deno #分子最大公約數 gcd_numerator = math.gcd(math.gcd(a.numerator, b.numerator), math.gcd(a.numerator, c.numerator)) a = a / gcd_numerator b = b / gcd_numerator c = c / gcd_numerator print('循環數 k = ',k) print('a = ',a) print('b = ',b) print('c = ',c) print('驗算結果:',a/(b+c) + b/(a+c) + c/(a+b)) k+=1
得出的結果是這樣的:
循環數 k = 2
a = 9499
b = -8784
c = 5165
驗算結果: 4
循環數 k = 3
a = 679733219
b = -375326521
c = 883659076
驗算結果: 4
循環數 k = 4
a = 6696085890501216
b = -6531563383962071
c = 6334630576523495
驗算結果: 4
循環數 k = 5
a = 5824662475191962424632819
b = -2798662276711559924688956
c = 5048384306267455380784631
驗算結果: 4
循環數 k = 6
a = 287663048897224554337446918344405429
b = -399866258624438737232493646244383709
c = 434021404091091140782000234591618320
驗算結果: 4
循環數 k = 7
a = 3386928246329327259763849184510185031406211324804
b = -678266970930133923578916161648350398206354101381
c = 1637627722378544613543242758851617912968156867151
驗算結果: 4
循環數 k = 8
a = 343258303254635343211175484588572430575289938927656972201563791
b = -2054217703980198940765993621567260834791816664149006217306067776
c = 2110760649231325855047088974560468667532616164397520142622104465
驗算結果: 4
循環數 k = 9
a = 154476802108746166441951315019919837485664
325669565431700026634898253202035277999
b = 368751317941299998271978115652254748254929
79968971970996283137471637224634055579
c = 437361267792869725786125260237139015281653
7558161613618621437993378423467772036
驗算結果: 4
當算到9P的時候,所有的a、b、c的結果為正。
至此,我們得到了最終的結果。
不過,a值已經高達80位數(b值79位,c值78位)。80位數是什么概念呢, 就是每8位是1億,要連續說出9個“億”字才能表達,億億億億億億億億億。
這個數字大到什么程度呢,就是假如我跟你的距離是1米,那么這個數字所代表的距離在1053個宇宙之外(一個宇宙的直徑據估算是930億光年)。注意,是10的53次方個。
如果不是用數學方法,即使是世界上最快的計算機,也完全不可能用暴力的方法(就是一個數字一個數字地嘗試)得到最終的解。
九、進一步的探索
上面所用的方法就叫“弦切法”,好比你綳了一條弦,不停地在曲線上找第三個交點(有理點)。我們從上面的程序結果可以看出,隨着過程的推進,a、b、c的數值快速增長,而這個80位數的結果,確實也是最小的結果。
此外,想要3者全部為正也並不是容易的事,這當然是跟x、y的取值有關系的。事實上,只有下圖的綠色部分才是a、b、c可以取到正值的位置。看樣子,我們能在第8次迭代(9P)的時候進入這個區域還算運氣不錯了。
你可能會覺得,80位數的結果令人發指。但如果我們把原方程的4替換為178,我們的結果將是幾億位(這意味者屏幕遠遠不能滿足顯示需求);而如果把4替換為896,那么結果將是萬億位的。
我們上面用到的計算橢圓曲線kP的算法,在現代密碼學方面有着極其重要的應用(也就是正向的計算很容易,反向的計算非常困難)。橢圓曲線加密算法稱為三大加密算法之一,著名的、穩定運行十余年的比特幣就是采用的橢圓曲線加密算法。
十、說明及感悟
最后,我想讀者中可能還是有不少存疑,即:
1、為什么我們可以在第四部分做那樣的變換,把a、b、c的方程變換成了x、y的橢圓曲線?
2、到底通過什么方式找到(-100,260)這個橢圓曲線的特解?
3、為什么我們最終這個80位的解就是最小解?
這些都是本文沒有解決的問題。因為這些都是很龐大的問題,本人爭取以后能給出容易理解的答案。
但幸好,就本題而言,首先我們找到了解(這是根本性的),其次我們確實了解了整個求解過程,並對其內在邏輯有了全面的認識。
最后的感悟:
解這個題目,有一種以為到樓下打個醬油,結果卻到大海上繞了一圈的感覺,差一點找不到回來的路。
然而數學之美,正在於她以極其精確的方式,告訴了你正確的方向。
最后,恭喜你,你看完了。
Quora上Alon Amit的英文解答鏈接在此:https://www.quora.com/How-do-you-find-the-positive-integer-solutions-to-frac-x-y+z-+-frac-y-z+x-+-frac-z-x+y-4/answer/Alon-Amit,你可能需要一點科學的辦法才能打開。
本人才疏學淺,解題中可能有不少不夠嚴謹的地方,歡迎指正。