食物网的资料结构.docx
《食物网的资料结构.docx》由会员分享,可在线阅读,更多相关《食物网的资料结构.docx(47页珍藏版)》请在冰豆网上搜索。
食物网的资料结构
食物網的資料結構
研究學生:
陳嘉怡
指導老師:
陳怡芬老師
摘要:
用程式的方式模擬一個食物網,藉由生物「食」與「被食」的關係建立一個單位和另一個單位間的關聯,而此關係建立在生物的生物特性上(即把生物的特性,如生物量、食性關係等與予量化)。
使用了兩個方法,一是用陣列的方式,另一是用鍵方式。
前者用一個陣列來表示所有的生物,藉著流程控制找出各生物中可對應的關係,但也因生物的總個數是固定的,每一個不同數目的食物網模型得用不同的程式。
雖然只是修改少許的程式碼,仍是很不方便。
後者是為了改善前者而來的。
使用的是鍵結的結構,也因鍵結和陣列的差異,也使用完全不同的流程控制。
並在資料讀入時才定訂生物的總數,因此解決了前者的問題。
研究動機:
國中時生物曾讀到「食物網」的觀念,在這一個網狀的系統中,某一生物和某一生物之間有一定的關係,彼此之間緊密的結合,一但其中有所變更,常常會「觸一絲而動全網」,資料和資料間形成一種特殊的結構。
研究目的:
「食物鏈」中的生物彼此藉著「吃」和「被吃」的關係連結,這種資料結構帶有「鏈結」和「圖形」的特性;而當多個「食物鏈」結合成一個「食物網」的時候,同一個生物在不同的鏈中可能有不同的地位,又增加結構上的複雜度。
故想利用程式語言表達出此種網狀關係。
設備、器材:
TurboC++3.0
研究問題:
推演食物網中各生物間的利害關係,及一生物在網中的地位(是否足以影響其他網中生物)。
找出適用的資料結構及資料彼此間的互動關係。
研究內容:
根據ECOPATH模型(附錄一),每一種生物有許多資料和自己或他種生物之間的某些資料有一定的關係,如:
老鼠的生物量多寡決定於他的天敵(如:
貓、蛇、老鷹……等)的食量、生物量和他的生物活境(如城市老鼠和鄉下老鼠的生物量一定不同)……等。
而當這些資料予以量化時,也就產生了處理這些數據的方程式,藉著這些方程式可以找出(或者說是「估計」)在實際情況中沒辦法得到的生物資訊。
ECOPATH就是其中一例,它是一個封閉生態系的模型,主體包含了幾道方程式,而我將方程式簡化,並依據簡化後的方程式來模擬一個簡版的ECOPATH模型。
(註:
之所以用簡化後的方程式來做,因為這分研究偏向「資料結構」和整體的應用。
所以比較傾向「先用簡化的方程式寫出一個模型的架構」,如果有能力再將模型擴充成「方程式可更改」的模式)
由於方程式是經簡化的,所以在建立模的時候,也暫時不考慮單位的問題;而所使用的數據,是自己依據方程式算出來的(也就是說數字本身符合方程式的運算),使用的數據資料為附錄二。
下面,將有兩個模型(模型一和模型二),她們用兩種不一樣的方式建立食物網模型。
在模型中,是以「生物量」(為「單位面積或體積中蘊藏的某種生物的總重量」,在這裡因為不考慮單位問題而定義意義不大。
但仍沿用這個名詞,方便解說)為未知,也就是整個模型以求出所有生物的「生物量」為基本要求。
研究結果、討論:
模型一:
這是我的笫一個模型,也是在結構上最單純的一個。
以一個一維的結構(struct)陣列來儲存各項生物的資料,再定義某些項目之間的關係,藉著這些關係,可建立一個單純的食物網模型。
一、根據方程式(這是簡化版,原版請參考附錄一):
Bi=
+Xi
Qik=Bk*(DC)ik*(FR)k
所定的結構如下:
check
表float型態
表int型態
B:
生物量。
X:
常數。
FR和DR:
和他種生物之間的關係數值。
(B、X、FR和DR的詳細定義參考附錄一)
eaten:
做為「被捕食」的計數器(下面將詳細說明)。
check:
做為此一單位(生物)的檢查碼,用以識別是否已「執行」過)下面將詳細說明)。
以這樣的一個結構做一個單位,在程式開始的時候宣告一個和資料中生物種類個數相同數目的陣列(因此他的個數無法改變------或者說是「要改變有點麻煩」,整個程式會亂亂的)。
下面是程式的執行流程及說明。
二、程式流程說明(參考「三、流程圖」):
1、Input:
先讀入檔案(*.txt),將各數值讀至相對應的陣列元素內。
檔案格式為:
B
FR
X
DR(一個陣列:
捕食者<->被捕食者)
單位A
單位B
單位C
單位D
單位E
單位F
單位A
-1,
0,
300,
0,
0,
0,
0,
0,
0,
單位B
-1,
2,
200,
1,
0,
0,
0,
0,
0,
單位C
-1,
1,
1500,
1,
0,
0,
0,
0,
0,
單位D
-1,
3,
1000,
0,
1,
0,
0,
0,
0,
單位E
-1,
3,
400,
0,
0,
1,
0,
0,
0,
單位F
1100,
4,
0,
0,
0.3,
0,
0.2,
0.5,
0,
Input.txt
(粗框者)
2、根據方程式,當其中一個單位M的生物量(B)已知時(PS.其他「非B」的資料項不可缺),可以得知捕食M的「捕食者L」的生物量(式i、ii);及被M捕食的「被捕食者N」的部分生物量(式iii、iv):
(式i、ii)
BM=
+XM(i)
(已知)(已知)(已知)
QML=BL*(DC)ML*(FR)L(ii)
(已知)(可求出)(已知)(已知)
(式iii、iv)
QNL=BM*(DC)NM*(FR)M(iii)
(已知)(已知)(已知)(已知)
BN=QNM+QNX+……+XM(iv)
(無法求得)(已知)(無法求得)(已知)
故只能求得「部分」生物量
3、整個主要過程是在一個while()的迴圈內進行,並用一個bio變數來計數(bio為「未知生物量的單位的個數」,每求得一個單位的生物量即減1,並隨時檢查bio的值;當bio等於0時,就強制跳出while()迴圈)。
4、程式在執行的時候,會去檢查每個單位的生物量(B)是否為-1(-1代表「未輸入」,以和「生物量為0」做區別);當生物量(B)「有值」(即不等於-1)時,則呼叫兩個函數,一個可將「捕食者」的生物量求出來(voideater(int));一個可算出「被捕常者」的部分生物量(voideat(int)),並儲存在一個N乘以N的二維陣列內(N為單位個數)。
5、因為是在while()內,每一次迴圈都會重覆查看單位的生物量,因此設一個檢查碼(intcheck),在笫一次對已知的單位生物量執行過eater()及eat()函數後,笫二次再碰到時會直接跳過,除了簡省時間外,也可以避免「被捕食者」因被重覆執行eater()函數而使生物量(B)的值也被重覆計算。
6、每一個「被捕食者」在所有的「被捕食量」都知道後,即可求出生物量(B),「被捕食量」原來儲存在一個N乘以N的陣列中,因此增設一個變數eaten,紀錄每一單位的「被捕食次數」。
每求出一個「被捕食量」後即減1,當eaten等於0時,就可以呼函數(floatadd(int))進行累加的動作來求出生物量。
eaten的值可在整理資料初值(voidfirst_set())時邊計算得到。
7、當跳出while()迴圈後,程式便會將各生物的生物量(由螢幕)列印出來。
三、流程圖(見次頁):
包含
(1):
主函式的流程-----------------voidmain()。
(2):
副函式eater的流程--------voideater(int)。
(3):
副函式eat的流程-----------voideat(int)。
註:
流程圖只包含主要函式。
(1):
主函式的流程-----------------voidmain()
(2):
副函式eater的流程--------eater(int)
(3):
副函式eat的流程-----------eat(int)
四、程式碼:
(模型一的程式碼)
#include
#include
#include
#include
#defineN6//生物的「數量」,因為是陣例結構,故先指定
structchain/*結構定義*/
{
floatbiomass,x,fr,dr[N];
inteaten,check;
};
chainanimal[N];//宣告結構
floatqik[N][N];//宣告一陣列(儲存各「被捕食量」用)
intbio=N-1;//宣告變數bio(流程控制用)
voidread()/*副函式(讀檔用)*/
{
inti,j;
floatcj;
FILE*fp;
if((fp=fopen("e:
\\7351420\\input\\chain_3.txt","r"))==NULL)
{
cout<<"OpenFileError!
!
";
exit
(1);
}
for(i=0;i{
fscanf(fp,"%f,",&cj);
animal[i].biomass=cj;
fscanf(fp,"%f,",&cj);
animal[i].fr=cj;
fscanf(fp,"%f,",&cj);
animal[i].x=cj;
for(j=0;j{
fscanf(fp,"%f,",&cj);
animal[i].dr[j]=cj;
}
}
fclose(fp);
}
voidfirst_set()/*副函式(初值設定用)*/
{
inti,j;
for(i=0;i{
animal[i].eaten=0;
animal[i].check=1;
for(j=0;jqik[i][j]=0;
}
for(i=0;ifor(j=0;j{
if(animal[j].dr[i]!
=0)
animal[i].eaten++;
if(animal[i].biomass!
=-1)
animal[i].eaten=-1;
}
}
voideater(intuse)/*副函式(算「捕食者」生物量用)*/
{
inti;
floatcy,cu;
for(i=0;iif(animal[i].dr[use]!
=0)
if(animal[i].biomass==-1)
{
cy=animal[use].biomass-animal[use].x;
cu=animal[i].fr*animal[i].dr[use];
animal[i].biomass=cy/cu;
bio--;
}
}
floatadd(intuse)/*副函式(累加)*/
{
intj;
floatsum=0;
for(j=0;jsum=sum+qik[use][j];
return(sum);
}
voideat(intuse)/*副函式(算「被捕食者」的「部分生物量」用)*/
{
intj;
for(j=0;jif(animal[use].dr[j]!
=0)
{
qik[j][use]=animal[use].dr[j]*
animal[use].biomass*
animal[use].fr;
animal[j].eaten--;
//檢查是否有「被捕食者」的生物量已可求得?
if((animal[j].eaten==0)&&(animal[j].biomass==-1))
{
animal[j].biomass=animal[j].x+add(j);
bio--;
}
}
}
voidmain()/*主函式*/
{
unsignedlongcount=0;
intuse,i;
read();//讀檔
first_set();//初值設定
use=count%N;
while
(1)//while迴圈(整個重點內容!
!
!
)
{
use=count%N;
if((animal[use].biomass!
=-1)&&(animal[use].check==1))
{
eater(use);
eat(use);
animal[use].check=0;
}
if(bio==0)//檢查(如果所有生物的生物量皆「已知」,可跳出迴圈)
break;
count++;
}
for(i=0;iprintf("\n%9.2f",animal[i].biomass);
cout<<"\n";
getch();
}
五、討論:
本來原則上在這一個模型中,在其他「非B」項的資料已知的條件下,只知其中任一位的生物量(B),都可以將其他單位的生物量(B)求出。
但卻有些情況例外。
在一個食物鏈中,不外乎包含幾種基本型:
(圖I)
(圖I)
(圖I)
C
B
B
A
A
C
B
A
將生物量「已知」和「未知」的單位之間的關係用表格表示:
笫i種情況
已知
未知
由已知可否求出未知
A
B
○
B
A
○
笫ii種情況
已知
未知
由已知可否求出未知
A
B、C
○(由A可知C、由C可知B)
B
A、C
○(由B可知C,由C可知A)
C
A、B
○(C可知A、B)
笫iii種情況
已知
未知
由已知可否求出未知
A
B、C
╳(∵不知道A「分別」對B、C的「被捕食量」,所以無法求得B及C)
B
A、C
╳(∵單憑B無法求出A,也就無法藉A求出C)
C
A、B
╳(∵單憑C無法求出A,也就無法藉A求出B)
上面的情況,是無法將「所有生物的生物量」找出來的情況。
因為是整個程序上的問題(由公式而來的),所以只能從「除錯」的方向改善------當資料有問題或不足而無法正確執行程式時,會出現提示說明告知使用者(這一點在模型二有改善)。
模型二:
由於模型一用的是「陣列」結構,當輸入單位個數改變時,必須要從程式碼的地方去更改,另外當要在一個已建立的模中新增或移除單位時,以陣列的結構來說都不太方便(新增的時候還可以再宣告一個個別的結構〔struct〕;但要刪除的時候就會有點麻煩……),而一直重覆執行(新增&刪除)後也會讓整個模型變的很亂……。
因此,模型二是模型一的延伸,所用的方程式和觀念與模型一同(故在此不贅述),而用鏈結(link)的方式取代陣列。
一、因為鏈結和陣列結構本身的差異,模型二的架構大致上改變了很多(註一)
在結構(struct)宣告方面,包含兩種結構(struct),分別構成兩串環狀鍵結。
如下:
pointer
Animalstruct
next
Chainstruct
Pointer(chain)
food
Animal結構存放的是一個單位的基本資料,包括名稱;B、X、FR(定義同前);temp:
為計算「被捕食量時的暫存空間,後面會再提到」。
eaten_time:
為「被捕食量」次數的計數器,作用同前面的「eaten」。
next:
是一個指標,指到下一個單位。
Chain結構存放的是單位和單位之間的「關係」。
Relation:
存放單位和單位之間的關係(數值)。
Food_che和eater_che:
是檢查碼(下面將詳細說明)。
Food和eater:
都是指標,分別指到relation的「關係人」(food指到「被捕食者」,也就是做為「食物」的那一邊;eater指到「捕食者」);next指到下一個「關係」。
(註:
陣列中的每一個單位是由「索引」控制的,也就是說,給索引一個值後即可「任意」提取你要的單位中的資料(圖i)。
但鍵結是「循序」的,也就是「找到笫一個單位,才能找到下一個」(圖ii),不能「隨意」的指定,因為他沒有一個像陣列一樣的「索引」。
)
二、產生鏈結時,需要一個長度和單位個數相同的animalstruct鏈結;但因為chainstruct存放的是單位和單位之間的「關係」,N個單位就有N*(N-1)/2種關係,也因此,兩者的長度事實上不是相同的(關係的個數在程式中由count(int)這個副函式算出)。
將上面的文字用圖示說明:
(左方為chain鏈結;右方為animal鏈結)
chain鏈結存放了單位單位之間的係,而food和eater指標(長在兩旁的箭頭)指向所存關係的「捕食者」和「被捕食者」,即會指到其中一個animal結構(右方圖)中。
執行時,以chainstruct這個鏈結為主軸,程式會先循序檢查所存放的關係值(relation),再依指標(food和eater)找到「關係人」,針對數值做處理。
三、程式的流程和說明(參考「四:
流程圖」):
1、Input:
先讀入檔案(*.txt),將各數值相對應的變數內。
檔案分為兩個,一個存放關於各生物的生物特性(*_1.txt);另一個存放生物和生物之間的關係(*_2.txt),即一個用來建立animal鍵結(*_1.txt);一個用來建立chain鍵結(*_2.txt)。
檔案格式如下。
以數據一為列(參考附錄二),其輸入檔為:
*_1.txt*_2.txt
11000
0100.3
010
00.2
0.5
6
a,-1,300,0
b,-1,200,2
c,-1,1500,1
d,-1,1000,3
e,-1,400,3
f,1100,0,4
2、資料都讀入後,就開始建立animal和chain這兩個鍵結的關係。
因為*_2.txt這個檔案的內容在建立時,即照著一定的關係:
笫一個數字是A-B的關係;笫二個是A-C;笫三個是A-D……,如下:
A-B,A-C,A-D,A-E,……A-N,
B-C,B-D,B-E,……B-N,
C-D,C-E,……C-N,
…………
(依此類推)
所以要建立兩者之間的關係(就是把chain中的food和eater指標分別對應到animal鍵結中的單位),就有一定的順序可循(由副函式establish()建立,此不多述,請參照程碼)。
3、整個主程是在一while()迴圈內進行,並用一個biom變數來計數(bio為「未知生物量的單位的個數」,每求得一個單位的生物量即減1,並隨時檢查biom的值;當bio等於0時,就強制跳出while()迴圈)。
4、執行時,以chainstruct這個鏈結為主軸,程式會先循序檢查所存放的關係值(relation),當relation有值時,就會先後分別檢查food和eater所指到的生物(在animal中)的生物量是否有值?
如果有,就可以針對它做處理(沒有的話則略過)。
5、模型二大致上所含單位和模型一相同(只是因為結構的關係不同而用了不同的方式或名稱取代了部分單位的功能)。
temp用來「暫存」生物量,當所有的「被捕食量」沒有全部求出來之前,先放在這;food_che和eater_che這兩個檢查碼是避免food和eater這兩指標被「重覆使用」而浪費時間而設的(只會做一次)。
這三個變數的詳細作用,請參照程式碼
6、在模型二中,多了一項檢查功能,除了當biom值等於零(代表所有生物的生物量都已知)外,如果while()迴圈跑了一遍,對整個程式的資料卻沒有任何更動的話,也會跳出迴圈,並提示:
「資料不足」訊息。
四、流程圖(見次頁):
包含:
主函式的流程--------------- voidmain_ch2()。
(因為模型二和模型一的差異主要在主函式的部分,其它小細節------程式片斷------都類似,故只列出此部分的程式碼)
五、程式碼:
(模型二的程式碼)
模型二的程式碼分成五個檔案(包含一個*.h檔和四個*.cpp檔):
1、toolbar.cpp:
介面(一個工具列式的介面)。
2、L1_1.cpp、L1_2.cpp和L1_3.cpp:
主要的函式內容,就是模型一的主要功能。
1、toolbar.h
#include
#include
#defineConColorWHITE+(BLUE<<4)
#defineMesColorYELLOW+(BLUE<<4)
#defineLevellColorBLACK+(LIGHTGRAY<<4)
#defineChooseColorYELLOW+(RED<<4)
#defineNormColorWHITE+(BLACK<<4)
#defineMenuColorYELLOW+(GREEN<<4)
#defineUp72
#defineDown80
#defineRight77
#defineLeft75
#defineEnter13
#defineFile0
#defineOption1
#defineHelp2
#defineEsc27
#defineM_top5
#defineM_bottom18
#defineM_left10
#defineM_right50
#defineC_top2
#defineC_bottom25
#defineC_left1
#defineC_right40
#defineD_top2
#defineD_bottom25
#defineD_left41
#defineD_right79
2、toolbar.cpp
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------------------------------------
char*item[]={"File","Option","Help"};
char*menu[3][3]={{"Load","Save","Exit"},
{"Option"},
{"Help","Oringin"}};
charfilename[30];
c