您當前的位置:首頁 > 娛樂

從九章算術開始的開方法

作者:由 知乎使用者Lj8vHt 發表于 娛樂時間:2018-06-07

一,推導

今有積五萬五千二百二十五步。問方為幾何。

開方術曰:“置積為實,借一算,步之,超一等。議所得,以一乘所借一算為法,而以除。除已,倍法為定法。其復除,折法而下。復置借算步之如初。以複議一乘之。所得副,以加定法,以除。以所得副從定法。復除折下如前。”

——《九章算術》

對於任意正實數

P

,可以將其分解為兩個正實數之和

(a+b)^{2}

的形式,且有

(a+b)^{2}=a^{2}+2ab+b^{2}=a^{2}+(2a+b)b

,觀察該等式結構可得出結論:

欲求一個正實數 #FormatImgID_4# 的平方根,可以先求出另一個不大於 #FormatImgID_5# 的正實數 #FormatImgID_6# 的平方根,然後配湊 #FormatImgID_7# 求解。

為便於表述,在此約定

a^{2}

稱作

P

的部分,

a

稱作

P

該部分的部分平方根。

\exists c\in \textbf{N} \cap \left[ 0,9 \right],n\in \textbf{Z}\space\space

使

a=c\cdot10^{n-1}

,則

P=a^{2}+(2a+b)b=c^{2}\cdot10^{2n-2}+(2c\cdot10^{n-1}+b)b

對於任意0到9之間的實數,其平方均小於100,

q

位數

P

表示為

P=\delta\cdot10^{2k}+\sum_{i=1}^{k-i=0}{\epsilon_i\cdot10^{2(k-i)}},\space k\in \textbf{Z},0<\delta<100,0\le\epsilon_{i}<100

其中

k=\left \lceil \frac{q}{2} -1\right \rceil

,令

n=2

,考慮①的形式,取

c

c^{2}\le\delta

c

可能的最大值。

\delta-c^{2}=\eta

,先僅考慮

\epsilon_{1}

,嘗試求出

\delta\cdot10^{2k}+\varepsilon_{1}\cdot10^{2(k-1)}

的平方根(這同時也是

P

的部分平方根)取非負整數

\beta<10

使

(20c+\beta)\beta\le100\eta+\epsilon_{1}

且差最小,設差為

\eta_{2}

現在得到了一個數

c\cdot10^{k}+\beta\cdot10^{k-1}

,這個數除了最高兩個數位之外其它數位(包括小數位)都是零,在所有數位相同且除最高兩位外均為零的數中,它是不大於且最接近

P

的平方根的數,

\sqrt{P}=c\cdot10^{k}+\beta\cdot10^{k-1}+\sum_{i=2}^{}{\beta_{i}\cdot10^{k-i}},\space0\le\beta_{i}<10

c\cdot10^{k}+\beta\cdot10^{k-1}

P

的一個部分平方根,現在再考慮

\epsilon_{2}

,即不斷擴大

P

的部分,並不斷擴大

P

的部分平方根,並最終求出

P

的平方根。

二,筆算開平方

根據上一節的內容,現在已經可以歸納出計算正實數平方根的一般方法,現以

P=40960

為例。

以小數點處為基準,將P每兩位進行一次劃分,即

P=4|09|60.|00|0

取最左邊的劃分,令其為

\delta

,即

\delta=4,k=2

,使

c^{2}=4,\space c=2,\space\delta-c^{2}=0

取下一個劃分,令其為

\epsilon_{1}

,即

\epsilon=9

,考慮

(20c+\beta)\beta \le \eta+\epsilon_{1}

,故

\beta=0

c_{2}=20,\eta_{2}=9

。重複上述步驟,考慮

(20c_{2}+\beta_{2})\beta_{2} \le \eta_{2}+\epsilon_{2}

,得

\beta_{2}=2

c_{3}=202,\eta_{3}=156

\beta_{3}=3……

直到到此步,計算出的結果為202。3,

\sqrt{40960}

的確切值在202到203之間。

這樣的計算雖然可以手動完成,但用計算機來幫忙顯然是更明智的,從演算法的角度考慮,這是一個實現及其容易的迭代演算法。

#include

#include

#include

double

sqrt

const

char

*

number

{

using

namespace

std

int

ans

=

0

int

decimal_seg

12

];

memset

decimal_seg

0

sizeof

decimal_seg

));

vector

<

int

>

integer

stack

<

int

>

integer_seg

bool

point

=

false

int

n

=

0

while

*

number

{

if

*

number

!=

‘。’

{

if

point

integer

push_back

*

number

-

‘0’

);

else

decimal_seg

n

++

=

*

number

-

‘0’

}

else

point

=

true

number

++

}

for

int

size

=

integer

size

(),

a

size

size

=

integer

size

())

{

a

=

integer

size

-

1

];

if

size

-

2

>=

0

a

+=

integer

size

-

2

*

10

integer

pop_back

();

if

integer

empty

())

integer

pop_back

();

integer_seg

push

a

);

}

int

a

=

integer_seg

top

(),

p

integer_seg

pop

();

if

a

for

int

i

=

9

i

>=

1

i

——

{

if

i

*

i

<=

a

{

ans

+=

i

p

=

a

-

i

*

i

);

break

}

}

else

{

p

=

0

}

while

integer_seg

empty

())

{

p

*=

100

p

+=

integer_seg

top

();

integer_seg

pop

();

for

int

i

=

9

i

>=

0

i

——

{

if

((

ans

*

20

+

i

*

i

<=

p

{

p

-=

ans

*

20

+

i

*

i

ans

*=

10

ans

+=

i

break

}

}

}

int

point_assis

=

1

for

int

i

=

0

i

<

2

*

6

i

+=

2

{

p

*=

100

p

+=

decimal_seg

i

*

10

+

decimal_seg

i

+

1

];

for

int

j

=

9

j

>=

0

j

——

{

if

((

ans

*

20

+

j

*

j

<=

p

{

p

-=

ans

*

20

+

j

*

j

ans

*=

10

ans

+=

j

point_assis

*=

10

break

}

}

}

double

true_ans

=

ans

true_ans

/=

double

point_assis

return

true_ans

}

輸出精度為小數點後六位。

標簽: integer  seg  ANS  平方根  size