2007年1月29日 星期一

CODE {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 8pt;
overflow:auto;
background: #f0f0f0 url(http://klcintw.images.googlepages.com/Code_BG.gif) lef\
t top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:200px;
line-height: 1.2em;
}

[code]
for i=1 to 10 step 1
for j=1 to 10 step 1
k= i*j
print *, i, j, k

next j
next i
[/code]

前一個程式的計算結果

觀察以下的計算結果,有幾點 值得注意

一 sum > 某一個值得時候,會出現錯誤的 負值答案

二 所需要的計算時間,從 0.0 突然跳到 0.016
可見,比 0.016 小的時間距離,無法被計算出來

三 執行 13.4 億次的加法運算,需要時間約 5.8 秒左右,
從而知道,每秒鐘大概可以執行 兩億個 加法運算,
確實,電腦很快。


[pre]


10 55 0.000000
20 210 0.000000
40 820 0.000000
80 3240 0.000000
160 12880 0.000000
320 51360 0.000000
640 205120 0.000000
1280 819840 0.000000
2560 3278080 0.000000
5120 13109760 0.000000
10240 52433920 0.000000
20480 209725440 0.000000
40960 838881280 0.000000
81920 -939483136 0.000000
163840 536952832 0.000000
327680 -2147319808 0.000000
655360 327680 0.000000
1310720 655360 0.000000
*** 2621440 1310720 0.016000
*** 5242880 2621440 0.031000
*** 10485760 5242880 0.047000
*** 20971520 10485760 0.078000
*** 41943040 20971520 0.172000
*** 83886080 41943040 0.360000
*** 167772160 83886080 0.703000
*** 335544320 167772160 1.375000
*** 671088640 335544320 2.859000
*** 1342177280 671088640 5.828000
Press any key to continue

[/pre]

time1(), time2() 的副程式

這個程式,顯示了 幾點報告

一 從 1 + 2 + ... + 13億
大概花費的時間是 5.8秒

二 時間的計算,有最小的精確度問題,
小於 0.016秒的時間 無法計算

三 整數有其一定的範圍,加法的答案超過 21億
會出現負值

這些問題,將來有需要的時候,會繼續深入研究


[pre]

! file: VF26.F90
! include 'sj-01.inc'
! -----------------------------------------------

subroutine time1(t1)
implicit none
integer t1

! values (5) The hour of the day (range 0 to 23) - local time
! values (6) The minutes of the hour (range 0 to 59) - local time
! values (7) The seconds of the minute (range 0 to 59) - local time
! values (8) The milliseconds of the second (range 0 to 999) - local time
INTEGER DATE_TIME(8)
CHARACTER (LEN= 12) REAL_CLOCK(3)

CALL DATE_AND_TIME(REAL_CLOCK (1), REAL_CLOCK (2), &
REAL_CLOCK (3), DATE_TIME)
! -----------------------------------------------

t1= date_time(5) !hour
t1= t1*60 + date_time(6) !minutes
t1= t1*60 + date_time(7) !seconds
t1= t1*1000 + date_time(8) !milliseconds
end ! of time1()
! -----------------------------------------------

subroutine time2(t1, dt)
integer t1, t2
real dt

call time1(t2)
dt= (t2 - t1)/(1000.0)

! dt must >= 0.0
if (dt < 0.0) then
dt= dt + 1.0*24.0*60.0*60.0
end if
end ! of time2()
! -----------------------------------------------

! begin of main()
implicit none

integer t1, no, sum, i
real dt

no= 10
do while(no > 0)
call time1(t1)

sum= 0
do i=1, no
sum= sum + i
end do

call time2(t1, dt)
if (dt > 0.0) then
write(*, '(1x, A4, 2I12, F10.6)')'*** ', no, sum, dt
else
write(*, '(1x, A4, 2I12, F10.6)')' ', no, sum, dt
end if

no= no*2
end do
end ! of main()
! end of file

[/pre]

2007年1月28日 星期日

移出去的副程式,檔案內容不變

目的,是為了讓 主程式 瘦身。

同時,可以將 不同的副程式 歸類,
個別發展,交給不同的人 同時發展,
分工合作。



[pre]

! file: sj-01.inc

subroutine skip1(no)
implicit none
integer no, i

! limits no in 1 .. 20
if ((no >= 1) .AND. (no <= 20)) then
! no is OK!
do i=1, no
print *, ' '
end do
else
! no is invalid
print *, ' '
end if

end ! of sub. skip1()
! -----------------------------------------------

subroutine pause1
implicit none
integer no

print *, 'Input 0 for stop the program, others for continue: '
read(*, '(I2)')no

if (no .EQ. 0) then
stop
end if

end ! of sub. pause1()
! -----------------------------------------------

recursive integer function fs(no)
implicit none
integer no

if (no <= 1) then
fs= 1
return
else
fs= fs(no-1) + fs(no-2)
return
end if

end ! of fs()
! -----------------------------------------------
! end of file


[/pre]

主程式,副程式分離以後的 費氏數列

CODE {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 8pt;
overflow:auto;
background: #f0f0f0 url(http://klcintw.images.googlepages.com/Code_BG.gif) lef\
t top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:200px;
line-height: 1.2em;
}


請注意 include 這個指令,
三個副程式,已經移到另外的一個檔案,
file: sj-01.inc

如此,我們可以化整為零,各個擊破


[code]

! file: VF26.F90
include 'sj-01.inc'
! -----------------------------------------------

! begin of main()
implicit none

integer no, f1, f2
real r1, r2
integer fs !for function 費氏數列

r2= (1.0 + sqrt(5.0))/2.0
call skip1(3)
print *, 'r2= ', r2
print *, '費氏數列的前後兩項的比值 會趨近於 黃金分割'
! -----------------------------------------------

! 以費氏數列 示範遞迴程式的寫作
call skip1(3)
do no=1, 18, 1
f1= fs(no+1)
f2= fs(no)
r1= float(f1)/float(f2)

print *, 'no= ', no, 'f1= ', f1, ', f2= ', f2, ', r1= ', r1
if ((mod(no, 10)) .EQ. 0) then
call skip1(1)
call pause1
end if
end do

end ! of main()
! -----------------------------------------------
! end of file


[/code]

加入前面兩個 副程式以後的 費氏數列程式

重複使用的副程式,鞥該能夠獨立在 主程式的外面,
我們 接下來所要討論的主題,
就是 如何解決 這個問題。


[code]

! file: VF26.F90

subroutine skip1(no)
implicit none
integer no, i

! limits no in 1 .. 20
if ((no >= 1) .AND. (no <= 20)) then
! no is OK!
do i=1, no
print *, ' '
end do
else
! no is invalid
print *, ' '
end if

end ! of sub. skip1()
! -----------------------------------------------

subroutine pause1
implicit none
integer no

print *, 'Input 0 for stop the program, others for continue: '
read(*, '(I2)')no

if (no .EQ. 0) then
stop
end if

end ! of sub. pause1()
! -----------------------------------------------

! begin of main()
implicit none

integer no, f1, f2
real r1, r2
integer fs !for function 費氏數列

r2= (1.0 + sqrt(5.0))/2.0
call skip1(3)
print *, 'r2= ', r2
print *, '費氏數列的前後兩項的比值 會趨近於 黃金分割'
! -----------------------------------------------

! 以費氏數列 示範遞迴程式的寫作
call skip1(3)
do no=1, 50, 1
f1= fs(no+1)
f2= fs(no)
r1= float(f1)/float(f2)

print *, 'no= ', no, 'f1= ', f1, ', f2= ', f2, ', r1= ', r1
if ((mod(no, 10)) .EQ. 0) then
call skip1(1)
call pause1
end if
end do

end
! -----------------------------------------------

recursive integer function fs(no)
implicit none
integer no

if (no <= 1) then
fs= 1
return
else
fs= fs(no-1) + fs(no-2)
return
end if

end
! end of file



[/code]

很有用的兩個副程式,skip1(), pause1()

call skip1(3)
跳三行空白

call pause1
暫停 程式的執行,讓你有機會 仔細的觀察
程式的計算結果的輸出,中間值的觀察,

很有用處喔
同時,可以學習 副程式的寫作。



[pre]

! begin of main()
implicit none
integer i

! test skip(), pause

call skip1(3)
do i=1,20,1
call skip1(i)
print *, 'i= ', i, ', i*i= ', i*i, ', sqrt(i)= ', sqrt(float(i))
call pause1
end do

end ! of main()
! -----------------------------------------------

subroutine skip1(no)
implicit none
integer no, i

! limits no in 1 .. 20
if ((no >= 1) .AND. (no <= 20)) then
! no is OK!
do i=1, no
print *, ' '
end do
else
! no is invalid
print *, ' '
end if

end ! of sub. skip1()
! -----------------------------------------------

subroutine pause1
implicit none
integer no

print *, 'Input 0 for stop the program, others for continue: '
read(*, '(I2)')no

if (no .EQ. 0) then
stop
end if

end ! of sub. pause1()
! -----------------------------------------------


[/pre]

基本的部分,已經介紹完畢

接下來,是綜合應用的部分。

會介紹一些有用的副程式,

skip(3)
pause()

time1(t1)
time2(t1, dt)

rnd1(s1)
rnd2(s1, x)
irnd(s1, i1,i2, ii)
init_rnd(s1)

藉由以上 副程式的探討,你會學到很多東西。
但是,我們必須承認,Fortran 不是一個很好的語言,
所以,在實作 以上的副程式的時候,
會感覺很彆扭,沒有 C語言好用。

遞迴函數呼叫的範例

利用遞迴的方式來解決問題,是非常直接 方便 快速的
一種 解題方法。

遞迴的缺點是 :會增加電腦的負荷,
使程式變慢。

但是,目前的PC 已經足過快速,所以 大部分的時候,
鼓勵使用遞迴的方式來寫程式


[code]

! begin of main()
implicit none

integer no, f1, f2
real r1, r2
integer fs !for function 費氏數列

r2= (1.0 + sqrt(5.0))/2.0
print *, 'r2= ', r2
print *, '費氏數列的前後兩項的比值 會趨近於 黃金分割'
! -----------------------------------------------

! 以費氏數列 示範遞迴程式的寫作
do no=1, 35, 1
f1= fs(no+1)
f2= fs(no)
r1= float(f1)/float(f2)

print *, 'no= ', no, 'f1= ', f1, ', f2= ', f2, ', r1= ', r1
end do

end
! -----------------------------------------------

recursive integer function fs(no)
implicit none
integer no

if (no <= 1) then
fs= 1
return
else
fs= fs(no-1) + fs(no-2)
return
end if

end


[/code]

函數的範例

以下是 函數的 範例,
Fortran 並沒有提供 所有的數學函數功能,
以下示範 degree 轉 radian 的函數寫作


[code]

! begin of main()
implicit none
real x, y
real dtor ! is a function

! sin(60)= sqrt(3)/2
! sin(45)= 1/sqrt(2)

x= 60.0
y= sin(dtor(x))
print *, 'x= ', x, ', y= ', y

y= sqrt(3.0)/2.0
print *, 'x= ', x, ', y + 1= ', y + 1
pause
! -----------------------------------------------

x= 45.0
y= sin(dtor(x))
print *, 'x= ', x, ', y= ', y

y= 1.0/sqrt(2.0)
print *, 'x= ', x, ', y + 1= ', y + 1

end
! -----------------------------------------------

! y= sin(dtor(x))
real function dtor(x)
implicit none
real x

dtor= x/180.0*(4.0*atan(1.0))

end


[/code]

副程式的範例

[code]

! begin of main()
implicit none
integer x, y

x= 1234
y= 4567
print *, 'x= ', x, ', y= ', y
pause

call swap_int(x, y)
print *, 'x= ', x, ', y= ', y

end
! -----------------------------------------------

! call swap_int(x, y)
subroutine swap_int(x, y)
implicit none
integer x, y, temp

temp= x
x= y
y= temp

end


[/code]

關於 副程式

結構化的程式設計的目的是為了
讓 程式碼 明白易懂。
除了 避免使用 goto 指令之外,
讓 程式單元簡短,也是一個方法。
主程式和每一個副程式,函數等
都必須限制在 十幾 二十行左右。

根據 程式所需要的功能,依序切割成
所需要的主程式,副程式和 函數。

我們接下來的說明,會針對 副程式,
函數和 遞迴,使用 簡單的範例 作詳細的介紹。

敬請 拭目以待。

2007年1月27日 星期六

三 迴圈的結構

以下的範例程式,示範 fortran 所提供的
三種回圈結構

迴圈很重要,請多加練習。


[pre]

! begin of main()
implicit none
integer no, sum, i

! loop for 1 + 2 + ... + no
no= 10000000

sum= 0
do i=1, no, 1
sum= sum + i
end do
print *, 'do loop, sum= ', sum
pause
! -----------------------------------------------

sum= 0
i= 1
do while (i <= no)
sum= sum + i
i= i + 1
end do
print *, 'do while(), sum= ', sum + 1
pause
! -----------------------------------------------

! repeat ... until
sum= 0
i= 1
do
sum= sum + i
i= i + 1
if (i > no) exit
end do
print *, 'repeat ... until(), sum= ', sum + 2

end


[/pre]

二 選擇的結構

請注意
if (x > y) then
! 當 (x > y) 的判斷結果是 Yes 的時候,
! 執行 這裡的指令,然後 跳到 相對應的
! end if 的後面

else if (x < y) then
! 當 (x < y) 的判斷結果是 Yes 的時候,
! 執行 這裡的指令,然後 跳到 相對應的
! end if 的後面


else
! 當上面所有的判斷結果都是 No 的時候
! 執行 這裡的指令,然後 跳到 相對應的
! end if 的後面

end if

選擇式的結構很重要,請務必自己敲程式,
自己實習一遍


[pre]

! begin of main()
implicit none
real a,b,c, d, x1,y1, x2,y2

! y= a*(x^2) + b*x + c
! D= (b^2) - 4*a*c
! -----------------------------------------------

a= 1.0
b= 3.0
! b= 2.0
! b= 1.0

c= 1.0
! 修改上面的 a,b,c 係數的值,重新 compile
! -----------------------------------------------

d= (b*b) - 4.0*a*c
if (d > 0.0) then
print *, 'D > 0.0, D= ', d
pause

x1= (-b + sqrt(d))/(2.0*a)
x2= (-b - sqrt(d))/(2.0*a)

! 驗算
y1= a*(x1*x1) + b*(x1) + c
y2= a*(x2*x2) + b*(x2) + c
print *, 'x1= ', x1, ', y1= ', y1
print *, 'x2= ', x2, ', y2= ', y2
else if (d < 0.0) then
print *, 'D < 0.0, D= ', d
pause
else
print *, 'D = 0.0, D= ', d
pause

x1= (-b + sqrt(d))/(2.0*a)

! 驗算
y1= a*(x1*x1) + b*(x1) + c
print *, 'x1= ', x1, ', y1= ', y1
end if

end


[/pre]

一 順序的結構

就是 程式設計指令的執行,是按照一定的順序
依序執行。

兩個變數的值作交換的動作的範例,
就是 順序結構

temp= x
x= y
y= temp

以上這三行程式,如果把順序弄亂了,
是否 仍然會得到 正確的結果。

請詳細 思考一下。


[pre]

! begin of main()

implicit none
integer x,y, temp

x= 123
y= 456
print *, 'x= ', x, ', y= ', y
pause


! swap the value of x, y
temp= x
x= y
y= temp
! ----------------------



print *, 'x= ', x, ', y= ', y
pause

end


[/pre]

程式設計的基本結構

只有三種

一 順序
sequential

二 選擇
selection

三 迴圈
loop

不管是 怎麼複雜的程式設計,都是由這三種基本結構
所 組合而成

關於變數的精義 之三

請參考 變數的精義之一所述
同一個變數,在某一個瞬間,只能 存放一個數值。

所以,以下的範例,將兩個變數的內涵值 作交換的動作,
需要 使用第三個變數,
並且 需要按照某一定的執行順序,執行以下 三個指令,

不要以為以下的範例很簡單,很多人不會呢。


[pre]

! begin of main()

implicit none
integer x,y, temp

x= 123
y= 456
print *, 'x= ', x, ', y= ', y
pause

! swap the value of x, y
temp= x
x= y
y= temp

print *, 'x= ', x, ', y= ', y
pause

end

[/pre]

2007年1月26日 星期五

關於變數的精義 之二

no= no + 1
的意思,其中的 "=" 號,
不是相等的意思。

他的真正的意思是
先 計算 "=" 號 右邊的數學計算式子,
等結果 計算出來以後,將 答案 存放到
"=" 號 左邊的 變數。

如果 你認為他是 相等的話,那麼
將 "=" 兩邊的 no 消去,會得到

0 = 1
這是 不合理的事情,所以 這裡的 "=" 號,
不是 相等的 意思。



[pre]

! begin of main program
implicit none
integer no

no= 2
print *, 'no= ', no
pause

no= no + 1
print *, 'no= ', no

end


[/pre]

2007年1月25日 星期四

關於變數的 精義 之一

同一個變數,在不同的時間,可以存放 不同的變數值

但是,在某一瞬間,只能存放 一個值

以下的指令行,
implicit none

是強迫 每一個變數,在使用之前,一定要 經過宣告的程序,
這樣子,比較不會寫錯程式。


[pre]
! begin of main program
implicit none
real x

x= 4.0*atan(1.0)
print *, 'x= ', x
pause

x= exp(1.0)
print *, 'x= ', x

end


[/pre]

這樣也是一個程式

[pre]

program VF26
! do nothing, 這樣也是一個程式
end program VF26

[/pre]

關於 整個段落的注解

註解是 非常重要的東西

! 這一行是 註解

但是,對於 一整個 段落的注解,就需要 使用下列的方式來處理

[code]
! file: VF26.f90

program VF26
implicit none

! 以下的 block if 非常重要,他可以把整個段落的文章
! 一下子 通通變成 註解。

!DEC$IF (.FALSE.)
...("commented" code here -- although it's not greened out)
...
!DEC$ENDIF

print *, 'Hello World! at 10:51'

end program VF26
! end of file

[/code]

單行的註解, 註解很重要

以下是 VF 6.6 所提供的 sample code

! 這一行是 註解,註解 很重要

在同一行裡面,只要碰到 "!" 符號,後面的就是 註解



[pre]
! VF26.f90
!
! FUNCTIONS:
! VF26 - Entry point of console application.
!
! Example of displaying 'Hello World' at execution time.
!

!****************************************************************************
!
! PROGRAM: VF26
!
! PURPOSE: Entry point for 'Hello World' sample console application.
!
!****************************************************************************

program VF26

implicit none

print *, 'Hello World! at 10:11'

end program VF26
[/pre]

使用的軟體

Windows XP sp2 繁體中文版 (CD X 1)

先 安裝 Visual C++ 6.0 和 MSDN (CD X 3)

接著 安裝 Compaq Visual Fortran 6.6.0 (CD X 1)

% 以上的安裝,皆須注意 選擇 "完整安裝",
才能擁有完整的功能和 線上求助。

使用的書本

這是就 台北市立圖書館 已經有的,
並不是 刻意首選


Fortran 95/90程式設計
黃, 逸萍, 1953-
出版者: 五南,

出版日期: 2004[民93]
面頁冊數: [18], 562面:
ISBN: 9571135666

館藏分佈狀況: 2 件館藏在架上
J12東湖.館藏J12東湖 編號 館藏類型
館藏位置312.932F68 4434 1 書刊 一般書庫區
2 書刊附件 服務台

for someone

利用這個 部落格,作為 Fortran 教學的 根據地。