從九章算術開始的開方法
一,推導
今有積五萬五千二百二十五步。問方為幾何。
開方術曰:“置積為實,借一算,步之,超一等。議所得,以一乘所借一算為法,而以除。除已,倍法為定法。其復除,折法而下。復置借算步之如初。以複議一乘之。所得副,以加定法,以除。以所得副從定法。復除折下如前。”
——《九章算術》
對於任意正實數
,可以將其分解為兩個正實數之和
的形式,且有
,觀察該等式結構可得出結論:
欲求一個正實數 #FormatImgID_4# 的平方根,可以先求出另一個不大於 #FormatImgID_5# 的正實數 #FormatImgID_6# 的平方根,然後配湊 #FormatImgID_7# 求解。
為便於表述,在此約定
稱作
的部分,
稱作
該部分的部分平方根。
使
,則
①
對於任意0到9之間的實數,其平方均小於100,
位數
表示為
②
其中
,令
,考慮①的形式,取
為
下
可能的最大值。
設
,先僅考慮
,嘗試求出
的平方根(這同時也是
的部分平方根)取非負整數
使
且差最小,設差為
。
現在得到了一個數
,這個數除了最高兩個數位之外其它數位(包括小數位)都是零,在所有數位相同且除最高兩位外均為零的數中,它是不大於且最接近
的平方根的數,
。
是
的一個部分平方根,現在再考慮
,即不斷擴大
的部分,並不斷擴大
的部分平方根,並最終求出
的平方根。
二,筆算開平方
根據上一節的內容,現在已經可以歸納出計算正實數平方根的一般方法,現以
為例。
以小數點處為基準,將P每兩位進行一次劃分,即
。
取最左邊的劃分,令其為
,即
,使
取下一個劃分,令其為
,即
,考慮
,故
,
。重複上述步驟,考慮
,得
,
;
直到到此步,計算出的結果為202。3,
的確切值在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
;
}
輸出精度為小數點後六位。