Tải bản đầy đủ (.doc) (28 trang)

Free Pascal: Yes, Turbo Pascal: No!

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (264.32 KB, 28 trang )

Free Pascal: Yes, Turbo Pascal: No!
Nguyễn Thanh Tùng
Các bạn đã bao giờ nghe đến cái tên Free Pascal (FP) chưa? Nếu chưa thì tôi xin giới
thiệu một cách ngắn gọn: FP là một môi trường lập trình rất tuyệt vời và mạnh mẽ, hoàn
toàn tương thích Turbo Pascal (TP) và điều đáng chú ý nhất là FP là được chọn làm môi
trường chuẩn thay thế TP trong các kì thi IOI. Vì sao vậy? Chúng ta hãy cùng tìm hiểu
những điều thú vị của FP mà TP không có để thấy câu trả lời nhé!!!
1. Bộ nhớ rộng rãi
Trong chúng ta, chắc có rất nhiều người biết về bài toán tìm dãy con chung dài nhất. Bài
toán phát biểu ngắn gọn như sau:
Cho 2 dãy A và B. Dãy A có các phần tử là a1, a2,…an, dãy B có các phần tử là là b1, b2,
…bn. Hãy tìm dãy C là dãy con của cả A và B và có nhiều phần tử nhất.
Chẳng hạn, nếu dãy A là 5 3 2 1 4 và B là 3 2 5 4 1 thì C là dãy 3 2 4.
Đây là một bài toán quy hoạch động kinh điển và có công thức quy hoạch động được thiết
lập như sau:
Gọi L(i,j) là độ dài dãy con chung lớn nhất của dãy Ăi) gồm các phần tử a1, a2,…ai và dãy
B(j) có các phần tử là là b1, b2,…bj.
Thế thì:
L(0,j)=L(i,0)=0.
Nếu ai</SUB>=Bj thì L(i,j)=L(i-1,j-1)+1.
Nếu ai ≠bj thì L(i,j)= max(L(i-1,j), L(i,j-1)).
Trường hợp 2 và 3 áp dụng với tất cả các chỉ số i từ 1 đến n và j từ 1 đến m. Nếu bạn chưa
tin vào tính đúng đắn của công thức thì tôi xin giải thích như sau:
Trường hợp 1 hiển nhiên.
Với công thức ở trường hợp 2 và 3 ta thấy: nếu ai =bj thì ta phải chọn ngay cặp phần tử
chung đó, các phần tử còn lại của 2 dãy là a1, a2,…a i−1 và b1, b2,…b j −1 có dãy con
chung lớn nhất gồm độ dài L(i-1,j-1), do đó L(i,j)=L(i-1,j-1) + 1. (Tư tưởng quy hoạch
động thể hiện ở chỗ L(i,j) đạt max thì L(i-1,j-1) cũng phải đạt max).
Còn nếu ai ≠bj thì ta có 2 lựa chọn: hoặc không xét phần tử ai và so dãy là a1, a2,…ai-1
với dãy b1, b2,…bj để được dãy con chung dài nhất L(i-1,j) phần tử; hoặc không xét phần
tử bj và so dãy là a1, a2,…ai với dãy b1, b2,…bj-1 để được dãy con chung dài nhất L(i,j-1)


phần tử. (Chú ý định nghĩa của L(i-1,j) và L(i,j-1)). Vì có 2 lựa chọn nên ta chọn hướng tốt
hơn, do đó L(i,j)=max(L(i-1,j) , L(i,j-1)).
Các bạn có thể băn khoăn là ở trường hợp 2 cũng có thể lựa chọn cả 2 tình huống trên chứ?
Thực chất không cần như vậy, vì dễ thấy L(i,j)≤ min(i,j) do đó L(i-1,j-1) + 1 chắc chắn
không nhỏ hơn cả L(i-1,j) và L(i,j-1).
Sau khi tính được xong toàn bộ L(i,j) thì ta sẽ được: dãy C có L(n,m) phần tử, để xác định
đó là các phần tử nào thì ta lần vết trên L theo 3 trường hợp trên để tìm các cặp
ai</SUB>=Bj được chọn. Các bạn xem trong chương trình cài đặt cụ thể dưới đây trên TP:
{$A+,B-,D+,E+,F-,G+,I+,L+,N+,O-,P-,Q+,R+,S+,T-,V+,X+}
program daycon;
const
inp = ’daycon.in0’;
out = ’daycon.out’;
max = 100;
type
mang1 = array[0..max] of integer;
mang2 = array[0..max] of mang1;
var
n,m,z : integer;
a,b,d : mang1;
L : mang2;
(*****************************************)
procedure nhap;
var
i : integer;
f : text;
begin
assign(f, inp);
reset(f);
readln(f, n, m);

for i := 1 to n do read(f,a[i]);
readln(f);
for i := 1 to m do read(f,b[i]);
close(f);
end;
(*****************************************)
procedure trace;
var
i,j : integer;
begin
i := n; j := m; z := L[n,m];
repeat
if L[i,j] = L[i-1,j-1] + 1 then begin
d[i] := 1;
i := i - 1; j := j - 1;
end
else
if L[i,j] = L[i-1,j] then
i := i - 1
else j := j - 1;
until (i=0) or (j=0)
end;
(*****************************************)
procedure qhd;
var
i,j : integer;
function max(a,b : integer): integer;
begin
if a > b then max := a else max := b;
end;

begin
for i := 1 to n do begin
for j := 1 to m do
if a[i] = b[j] then
L[i,j] := L[i-1,j-1] + 1
else
L[i,j] := max(L[i-1,j],L[i,j-1]);
end;
end;
(*****************************************)
procedure xuly;
begin
qhd;
trace;
end;
(*****************************************)
procedure inkq;
var
f : text;
i : integer;
begin
assign(f, out);
rewrite(f);
writeln(f, z);
for i := 1 to n do
if d[i] = 1 then write(f,’ ’,a[i]);
close(f);
end;
(*****************************************)
begin

nhap;
xuly;
inkq;
end.
Thuật toán có thời gian thực thi cỡ O(n2). Với n,m =100 thì chương trình chạy trong chớp
mắt. Vậy thì tại sao không thử giải với n=1000 (ai mà không muốn chương trình của mình
mạnh hơn). Vậy ta sửa hằng số max trong chương trình thành 1000. Và, TP báo lỗi
"structure too large"!!! Tại sao lại thế???
Câu trả lời là: TP là môi trường lập trình 16 bit trên HĐH DOS do đó nó có nhiều hạn chế.
Han chế thứ nhât là kích thước của biến và kiểu &l; 64KB, trong đó có biến mảng và
kiểu mảng. Đó là do dùng số 16 bit thì chỉ có thể chỉ số hoá được 216 = 64K giá trị thôi.
Khi ta khai báo max = 1000 thì mảng L của ta có kích thước 1000x1000x2 (2 là kích thước
kiểu integer)=2.106>>64K nên TP báo lỗi "structure too large" (kiểu cấu trúc quá lớn) là
đúng rồi.
Vậy bây giờ thay vì khai báo mảng L là mảng 2 chiều, ta sẽ khai báo L thành rất nhiều
mảng nhỏ hơn. Bạn cứ thử thế mà xem. Nếu TP không báo lỗi "structure too large" thì
cũng báo lỗi là "too many varibles". Đó là do hạn chế thứ 2 của TP: tổng kích thước các
biến toàn cục (global) cũng ≤ 64KB . Bạn có chia L thành bao nhiêu mảng con thì TP vẫn
bắt tổng kích thước của chúng ≤ 64KB.
Vẫn còn giải pháp nữa: thay vì dùng mảng tĩnh thì dùng mảng động. Khai báo L là mảng
1000 con trỏ, mỗi con trỏ trỏ đến một mảng 1000 phần tử. (L:Array[1...max] of ^mang1).
May quá, TP không báo lỗi khi dịch. Nhưng khi chạy thì ôi thôi, lỗi "Heap overflow" (tràn
heap). Nguyên nhân là hạn chế của DOS: toàn bộ bộ nhớ DOS có thể sử dụng ≤ 640 KB.
Mà các chương trình hệ thống và IDE của TP cũng chiếm mất hơn 300KB rồi. Tức là
chương trình của bạn dù có tận dụng hết bộ nhó còn lại cũng chỉ được 300KB nữa thôi.
(Khi bạn nhấn F9 để dịch, TP báo xxxKB free memory, đó chính là phần heap tối đa hệ
thống có thể cấp phát cho các biến động đó).
Vẫn còn nhiều giải pháp có thể giải quyết: dùng 2 mảng một chiều tính lẫn nhau và đánh
dấu lần vết bằng bit; ghi ra file; dùng mảng răng lược… Nhưng dù sao thì cũng chỉ là giải
pháp tình thế, hơn nữa lại rất phức tạp. Giải pháp tốt nhất là dùng một môi trường lập trình

mạnh hơn. Và IOI đã chọn FP.
Tôi đem chương trình trên với khai báo max =1000 chạy trên FP và mọi chuyện đều ổn,
chẳng có lỗi nào xảy ra hết. Đối với FP, bộ nhớ không bị hạn chế bởi con số 64KB nữa
(free mà).
Điều đó có được là nhờ những đặc tính tuyệt vời của FP:
a. FP là môi trường lập trình 32 bit. Dùng một số 32 bit thì có thể chỉ số hoá được 232 =
4G giá trị, vậy nên biến trong FP có thể có kích thước 4GB. Các bạn chú ý:
4GB=4x1024MB. Trong khi đó máy tính chúng ta thường dùng thường có chừng 128MB
RAM. Mảng L kích thước ≤ 2MB thì nhằm nhò gì.
b. FP là môi trường lập trình chạy trên nền các HĐH 32 bit (Windows, Linux, BeOS,
OS/2… và cả DOS nữa. Nhưng đó là phiên bản DOS 32 bit mở rộng). Đây cũng là điều
quan trọng. Vì nếu cho FP chạy trên DOS 16 bit (nếu có chạy được), thì với bộ nhớ chật
hẹp 640KB, FP cũng phải bó tay không phát huy được tài năng. Ngược lại do TP là 16 bit,
nên có cho chạy trên Windows 32 bit, thì cũng chỉ phát huy được tài năng đến mức của 16
bit mà thôi. Chạy trên môi trường 32 bit, ngoài RAM (đã rất nhiều), HĐH còn có có chế bộ
nhớ ảo (virtual memory) sử dụng một phần HĐ làm bộ nhớ tạm nên FP có thể cung cấp
cho bạn dung lượng nhớ có thể nói là thoải mái (free mà).
c. FP là tương thích hoàn toàn với TP. Đây cũng là một điều thú vị. Chương trình ở phàn
trên tôi viết trong TP, đem sang FP vẫn chạy ngon lành, chẳng phải sửa đổi gì hết (thực ra
thì có sửa giá trị max từ 100 thành 1000). IDE của FP thì giống hệt TP (tất nhiên có nhiều
chức năng tiên tiến hơn, nhưng những gì bạn làm được với TP đều làm được trên FP).
Tôi nghĩ vậy là quá đủ để chúng ta thay thế TP bằng FP. Nếu bạn còn băn khoăn, hãy đợi
các bài viết tiếp theo để tìm hiểu tiếp về những điều kì diệu mà FP có còn TP thì không.
Còn nếu bạn háo hức muốn dùng thử, hãy làm theo chỉ dẫn cài đặt dưới đây.
2. Cài đặt và sử dụng FP
a. Tải bộ cài đặt
FP có rất nhiều phiên bản, cả các phiên bản đã sử dụng chính thức và phiên bản còn đang
phát triển. Theo tôi thì các bạn nên sử dụng các phiên bản chính thức vì chúng ổn định
hơn. (Tôi hiện đang dùng phiên bản 1.0.6).
Để cài đặt bạn vào website của ISM (), hoặc nếu thích có thể đến

thẳng website của FP (), vào mục download và tải file zip chứa
bộ cài. Chú ý là có nhiều phiên bản của FP cho các hệ điều hành khác nhau nên bạn phải
chú ý:
- Tải bộ cài các phiên bản chính thức. Các phiên bản chính thức có chữ số cuối cùng là số
chẵn (như bản của tôi là 1.0.6, có thể bây giờ có nhiều bản mới hơn rồi).
- Tải bộ cài cho HĐH Windows và DOS. Bạn có thể nhận biết điều này qua tên file zip.
Như file của tôi là dosw32106full.zip, có nghĩa là bộ cài đầy đủ cho HĐH Windows và
DOS, phiên bản 1.0.6.
b. Unzip và Install
Sau khi đã tải được bộ cài (tất cả ở trong một file zip), bạn unzip file đó ra một folder rồi
chạy file Install.exe. Giao diện cài đặt hiện ra, yêu cầu bạn lựa chọn thư mục cài đặt cho
FP (mặc định là C:PP). Và tiếp theo là một bảng lựa chọn các cấu hình cài đặt. Theo tôi thì
bạn nên chọn các cấu hình mặc định, và đơn giản là nhấn Enter để bộ cài tự làm việc của
mình.
c. Chạy và cấu hình IDE
Sau khi cài xong, toàn bộ IDE, chương trình dịch, tài liệu, ví dụ,… của FP được copy vào
thư mục bạn đã chọn khi cài đặt (mặc định là C:PP). Có 2 bản cho cả Windows và DOS
(32bit). Nếu chỉ dùng FP thay thế TP trong học tập thì theo tôi bạn nên sử dụng bản cho
DOS. Vì qua sử dụng tôi thấy IDE của FP trên DOS hoạt động ổn định hơn IDE của FP
trên Windows.
Để sử dụng IDE của FP trên DOS, bạn vào thư mục con bingo32v2 và chạy file fp.exe. (Có
thể sẽ cần nhấn Alt-Enter để chương trình chạy toàn màn hình full screen. Bạn sẽ thấy
ngạc nhiên, hoặc cũng chẳng ngạc nhiên lắm, vì giao diện IDE của FP giống hệt TP. (Tôi
thì thấy ân tượng hơn, nhất là hình ảnh nền).
Đến đây thì mọi chuyện như trên TP thôi. Nhưng để thuận tiện hơn trong sử dụng, bạn nên
thiết lập một số thông số cấu hình như sau:
- Vào menu CompileTarget: chọn DOS (để nhấn Ctrl-F9 là chương trình của bạn có thể
chạy ngay lập tức như trong TP. Vì compiler ta dùng cũng đang chạy trên DOS mà. Nhưng
đây là DOS 32 bit đấy, không hạn chế như DOS 16 bit đâu).
- Vào menu OptionsMode: chọn Normal (để FP sẽ hỗ trợ chúng ta debug dễ chịu nhất).

- Vào menu OptionsCompiler:
ở tab Syntax các bạn chỉ chọn các mục:
+ TP/BP compatibily: để tương thích với TP (sau này khi quen với FP rồi thì không cần
nữa, còn bây giờ thì chọn chức năng này cho dễ sử dụng).
+ Stop after first error: bình thường FP dịch sẽ thông báo một loạt lỗi (giống C, Delphi…),
nhưng ta quen với việc thông báo lỗi đầu tiên gặp của TP nên chọn mục này cho thân thiện.
Sau này có thể không cần nữa.
+ C-like operators: đây là một điều thú vị của FP (các bạn hãy đón đọc trong kì sau nhé).
ở tab Code generaion các bạn chọn
+ Toàn bộ các mục phần Run-time checks: cho an toàn các bạn ạ, tránh trường hợp ta lập
trình sai như: truy cập ngoài mảng, tính toán tràn số mà máy vẫn không thông báo gì.
+ Target processor: chọn PPro/PII. Hầu hết máy của chúng ta là máy Pen III, Pen IV, thế
thì nên để FP tận dụng hết tiềm năng của chúng để chương trình của chúng ta chạy hiệu
quả hơn.
- Vào menu OptionsEnviroimentEditor: đặt các thông số soạn thảo mà bạn thích. Tôi thì
thường chỉ đặt các mục sau:
Các mục mà TP mặc định (và FP cũng thế):
+ Insert mode: chế độ gõ chèn.
+ Auto indent mode: đặt lề tự động.
+ Backspace unindent.
+ Tab size: 4. Indent size: 4.
Những thông số này giúp chương trình dễ đọc hơn (có cấu trúc, thẳng lề, các đầu dòng thò
thụt đều đặn theo khoảng cách 4 kí tự).
Syntax highlight: hiện sáng từ khoá (trong TP cũng có).
Persirtent block: khối sau khi đánh dấu sẽ hiển thị cho đến khi dùng lệnh ẩn khối.
Auto closing brackets: tự động thêm kí tự đóng ngoặc. Đây là điều đặc sắc không có trong
TP. Bình thuờng trong TP bạn gõ các kí tự mở ngoặc như (, [, { thì phải tự thêm kí tự đóng
ngoặc cho đúng. Nếu không chú ý thì rất dễ lẫn. Còn trong FP nếu đặt chức năng này thì
khi ta gõ kí tự mở ngoặc, FP cũng thêm luôn kí tự đóng ngoặc, do đó chúng ta không sợ
các lầm lẫn thừa thiếu đóng mở ngoặc nữa.

Chúc các bạn sảng khoái khám phá và tận hưởng những điều thú vị của FP. Hẹn gặp lại
trong các bài viết sau.
Free Pascal: Yes, Turbo Pascal: No!
Nguyễn Thanh Tùng
(Tiếp theo số trước)
Trong số trước chúng ta đã biết một đặc tính rất tuyệt vời của FP là hỗ trợ bộ nhớ rộng
rãi. Không chỉ có vậy, trong bài này chúng ta sẽ tìm hiểu tiếp về những điều thú vị khác
của FP.
1. Kiểu số nguyên lớn
Có rất nhiều bài toán cần chúng ta phải tính toán với những số nguyên lớn. Chẳng hạn như
tính giai thừa, tính số Fibonacci hay tìm các số nguyên tố lớn (chẳng hạn tìm các số nguyên
tố lớn để dùng trong thuật toán mã hoá RSA). Với kiểu Integer của TP ta tìm được số
nguyên tố lớn nhất có 5 chữ số. Với kiểu LongInt thì được 9 chữ số. Muốn tìm được các số
to hơn thì phải dùng kiểu số thực (như comp hay extended). Nhưng có điều bất tiện là các
kiểu số thực thì không dùng các phép toán div, mod được nên cài đặt rất khó khăn.
Ngoài bài toán về tìm số nguyên tố lớn, với những bài toán khác như tính giai thừa, tính số
Fibonacci,… kiểu integer của TP rất hạn chế.
Một hạn chế thứ hai với kiểu integer của TP là hay gặp các lỗi tính toán số học. (Không
biết bạn có đặt {$Q+} để phát hiện các lỗi như vậy chưa). Lỗi tính toán số học xảy ra khi
chúng ta tính biểu thức có các hạng tử trong miền integer nhưng kết quả thì nằm ngoài
miền (chẳng hạn 30000 + 30000). Những lỗi như vậy thường ít khi ta để ý, nhưng rất phiền
toái. Sửa chúng thì cũng không khó lắm, chỉ cần chuyển đổi kiểu (thành LongInt hay Real)
là OK.
Với FP thì những hạn chế đó không thành vấn đề. Với lợi thế 32 bit (gấp đôi TP), FP cung
cấp kiểu Int64, mới nghe chắc bạn cũng đoán được đó là kiểu số nguyên 64 bit. Với Int64
các bạn có thể tìm được các số nguyên tố 18 chữ số (cỡ tỉ tỉ) hay tính được giai thừa của
20. (Nếu vẫn muốn hơn thì ta phải xây dựng riêng một kiểu BigInt, ta sẽ làm điều đó trong
phần sau).
Trong trường hợp muốn tiết kiệm bộ nhớ, ta vẫn có thể sử dụng kiểu Byte (kích thước 1
byte), SmallInt (kích thức 2 byte).

Bảng sau trình bày về các kiểu nguyên của FP:
Chú ý: với kiểu Integer, mặc định FP dùng kích thước 2 byte. Vì vậy khi muốn dùng kiểu
nguyên lớn, ta nên khai báo rõ ràng.
2. Kiểu string lớn
Khi lập trình, chúng ta rất nhiều lần gặp vấn đề với các xâu tối đa 255 kí tự của TP (chẳng
hạn bài toán xâu đối xứng, bài toán đếm từ…). Ta có thể giải quyết vấn đề bằng mảng kí
tự (array of char) nhưng khi đó ta lại không thể dùng các phép toán trên xâu rất mạnh của
Pascal.
Không chỉ có cải tiến về kiểu nguyên, kiểu string trong FP cũng rất tuyệt vời. String trong
FP không còn hạn chế 255 kí tự của TP nữa mà có kích thước tối đa là 2.. tỉ kí tự. Hơn
nữa FP còn hỗ trợ kiểu xâu Unicode (WideString). Nếu bạn vẫn thích kiểu String cũ của
TP, bạn có thể dùng kiểu ShortString.
Bây giờ bạn có thể viết chương trình giải bài xâu đối xứng, xâu con chung với kiểu string
của trên FP và hạn chế n cỡ 1000 một cách rất dễ dàng. Chúng ta sẽ tìm hiểu kĩ hơn về xâu
trong FP ở một bài báo khác.
3. Viết hàm thuận lợi hơn
FP có rất nhiều cải tiến trong cách viết các hàm. Để so sánh, chúng ta sẽ xem xét một số ví
dụ. Trong TP, chúng ta viết hàm tính giai thừa như sau:
function gt(n:integer):integer;
var
i,tg:integer;
begin
tg:=1;
for i:=1 to n do tg:=tg*i;
gt:=tg;
end;
Tại sao ta lại phải thêm một biến tg để lưu trữ kết quả trung gian? Đó là do trong TP với
tên hàm ta chỉ được sử dụng duy nhất lệnh gán trị. Nếu đưa tên hàm vào biểu thức thì sẽ
thực hiện lời gọi hàm.
Điều này đã được FP làm cho free bằng cách cho phép sử dụng tên hàm như một biến

(giống như Object Pascal dùng biến Result). Khi đó tên hàm có thể xuất hiện ở trong cách
biểu thức tính toán ngay trong thân hàm mà không tạo ra lời gọi đệ quy. Hàm giai thừa
trong FP có thể viết rất tiết kiệm biến như sau:
function Gt(n: integer): int64;
begin
gt := 1;
for n := n downto 2 do gt := gt * n;
end;
Vậy khi ta muốn gọi đệ quy thì sao? Thì chỉ việc thêm cặp dấu () và truyền tham số cần
thiết. FP sẽ biết ta muốn gọi đệ quy khi ta có thêm cặp (). Hàm giai thừa viết kiểu đệ quy
như sau:
function Gt(n: integer): int64;
begin
if n=0 then exit(1)
else exit(n*gt(n-1));
end;
Trong cách viết này ta còn thấy một điều tiện lợi của FP: dùng lệnh exit để trả lại kết quả
cho hàm (giống như C và Object Pascal sử dụng lệnh return). Bạn sẽ thấy sự tiện lợi của
cách viết này khi viết các hàm dạng " phát hiện được phần tử đầu tiên rồi thoát".
Chẳng hạn hàm tìm vị trí của phần tử x trong mảng a có n phần tử. Viết trong TP ta phải
viết như sau:
function Find(x: integer): integer;
Var
i : integer;
begin
for i := 1 to n do
if a[i] = x then begin
Find := i;
exit;
end;

Find := 0;
end;
Hàm này viết trong FP thì ngắn ngọn hơn nhiều:
function Find(x: integer): integer;
Var
i : integer;
begin
for i := 1 to n do
if a[i]=x then exit(i);
exit(0);
end;
4. Kết quả trả lại của hàm có thể là kiểu cấu trúc
Rất nhiều ngôn ngữ lập trình thông dụng như C, VB… chỉ cho phép kết quả trả lại của hàm
là các kiểu cơ sở như: số nguyên, số thực, kí tự, con trỏ… Riêng TP thì có thêm kiểu xâu
kí tự. Nếu muốn trả lại kết quả là kiểu cấu trúc như mảng hay bản ghi thì bạn chỉ có cách
dùng tham biến thôi. Một số NNLT hướng đối tượng như C++, Java, Object Pascal có cho
phép kết quả trả lại là một đối tượng, nhưng như vậy vẫn không free như FP: FP cho phép
kết quả của hàm có thể là kiểu cấu trúc.
Để hiểu hơn chúng ta cùng làm bài toán sau (Đề thi OLP2004):
Gọi X là tập tất cả các xâu nhị phân không có 2 bit 1 liền nhau. Các xâu được đánh thứ tự
theo trình tự được sinh ra (từ nhỏ đến lớn, bit 0 đứng trước bit 1). Chẳng hạn với n=5 ta có
các xâu sau:
Bài toán đặt ra: hãy xác định xâu nhị phân n bit ứng với số thứ tự m cho trước. Hạn chể: n
<= 200.
Bài toán này có thuật giải như sau:
Gọi L[k] là số các xâu nhị phân như vậy có k bit. Nếu bit thứ k của nó là bit 0 thì k-1 bit
còn lại là tự do (tức là ta có L[k-1] dãy). Nếu thứ k của nó là bit 1 thì bit k -1 phải là 0, và
k-2 bit còn lại free. Vậy ta có: L[k] = L[k-1] + L[k-2]. Chú ý: L[1]=2 và L[2]=3.
Có công thức đó, ta tính số các xâu có n bit. Để xác định xâu nhị phân n bit có thứ tự m
cho trước ta có nhận xét: nếu m > L[n-1] thì nhất định bit thứ n phải là 1 (vì thứ tự của xâu

có bit 0 đứng trước xâu có bit 1, và có đúng L[n-1] xâu có bit thứ n là bit 0). Xâu n-1 bit
còn lại có sẽ thứ tự là m-L[n-1]. Ngược lại thì bit thứ n là bit 0 và xâu n-1 bit còn lại có thứ
tự là m.
Do đó ta có thể làm như sau:
for i:=n downto 1 do
if m <= L[i-1] then
x[i] := 0
else begin
x[i] := 1;
m := m - L[i-1];
end;
Tuy nhiên n có thể bằng 200 nên m và các giá trị L[i] có thể xấp xỉ 2
200
, tức là cỡ 10
70
(vì
2
10
xấp xỉ 10
3
). Ta không thể dùng các kiểu số có sẵn mà phải tự xây dựng một kiểu số lớn
hơn.
Có nhiều người thích dùng xâu để biểu diễn số lớn, nhưng tôi thấy dùng mảng thì thích
hợp hơn. Ta dùng mảng biểu diễn số nguyên lớn, mỗi phần tử của mảng là một chữ
số. Các chữ số lưu trữ trong mảng theo chiều từ trái sang phải: chữ số hàng đơn vị là phần
tử 1, chữ số hàng chục là phần tử 2…
Ta khai báo kiểu số lớn đó như sau:
const max = 100;
type BigInt = array[1...max] of byte;
Để cộng, trừ 2 số nguyên lớn biểu diễn bằng mảng, ta dùng thuật toán cộng kiểu thủ công

(cộng các chữ số từ bậc thấp đến bậc cao, có nhớ). Các thủ tục cộng, trừ viết trong TP như
sau:
procedure cong(var a,b,c : BigInt);
var
i,t : integer;
begin
t := 0;
for i:=1 to max do begin
t := t + a[i] + b[i];
c[i] := t mod 10;
t := t div 10;
end;
end;
procedure tru(var a,b,c : BigInt);
var
i,t : integer;
begin
t := 0;
for i:=1 to max do begin
t := a[i] - b[i] - t;
if t < 0 then begin
c[i] := 10 + t;
t := 1;
end else begin;
c[i] := t;
t := 0;
end
end;
end;
Ta khai báo L là mảng 200 phần tử kiểu BigInt. Gán L[1] là 2, L[2] là 3 rồi tính các phần

tử khác bằng câu lệnh như sau:
for i:=3 to n do cong(L[i-1],L[i-2],L[i]);
Viết như vậy thì chương trình hoạt động tốt, nhưng không trực quan lắm. Nếu có thể viết
L[i]:=cong(L[i-1],L[i-2]) thì sẽ trong sáng hơn nhiều. Trong TP thì không thể, nhưng trong
FP hoàn toàn có thể viết được như vậy. Sau đây là toàn bộ chương trình nguồn giải bài
toán này viết bằng FP:
program Xau2bit1;
const
inp = ’fibo.inp’;
out = ’fibo.out’;
max = 100;
type
BigInt = array[1..max] of byte;
var
n : integer;
m : BigInt;
s : string;
kq: array[1..200] of byte;
L : array[1..200] of BigInt;
f : text;
{==================================}
procedure nhap;
var
c : char;
i : integer;
begin
assign(f,inp);
reset(f);
readln(f,n);
readln(f,s);

close(f);
end;
{==================================}
function chuyen(s : string): BigInt;
var
i : integer;
begin
for i:=1 to length(s) do
chuyen[i]:=ord(s[i]) - ord('0');
for i:=i+1 to max do
chuyen[i]:=0;
end;
function cong(var a,b : BigInt): BigInt;
var
i,c : integer;
begin
c := 0;
for i:=1 to max do begin
c := c + a[i] + b[i];
cong[i] := c mod 10;
c := c div 10;
end;
end;
function tru(var a,b : BigInt): BigInt;
var
i,c : integer;
begin

×