20
Tổng quan Kỹ thuật lập trình Trang 1 MỘT SỐ VẤN ĐỀ CỦA KỸ THUẬT LẬP TRÌNH ............................................................. 2 1.1 Phân tích và đặc tả vấn đề bài toán ................................................................................... 2 1.2 Chọn lựa cấu trúc dữ liệu và phát triển thuật toán.......................................................... 2 1.3 Mã hóa chƣơng trình........................................................................................................... 2 1.3.1 Giới thiệu .......................................................................................................................... 2 1.3.2 Cách đặt tên biến và tên hằng......................................................................................... 2 1.3.3 Trình bày tổng quan của chƣơng trình ......................................................................... 3 1.4 Nguyên lý lập trình .............................................................................................................. 4 1.4.1 Nguyên lý tối thiểu ........................................................................................................... 4 1.4.2 Nguyên lý địa phƣơng...................................................................................................... 4 1.4.3 Nguyên lý nhất quán ........................................................................................................ 5 1.4.4 Nguyên lý an toàn ............................................................................................................ 6 1.4.5 Phƣơng pháp TOP-DOWN ............................................................................................. 8 1.4.6 Phƣơng pháp BOTTOM-UP......................................................................................... 11 1.5 Kiểm thử chƣơng trình ..................................................................................................... 14 1.6 Bảo trì chƣơng trình .......................................................................................................... 14 KỸ THUẬT TỐI ƢU HÓA CHƢƠNG TRÌNH .................................................................. 15 1.7 Ví dụ bài toán ..................................................................................................................... 15 1.7.1 Cách giải thứ nhất .......................................................................................................... 15 1.7.2 Cách giải thứ hai ............................................................................................................ 15 1.7.3 Cách giải thứ ba ............................................................................................................. 16 1.7.4 Cách giải thứ tƣ .............................................................................................................. 17 BÀI TẬP .......................................................................................................................... 19 1.8 Kỹ thuật lính canh ............................................................................................................. 19 1.9 Kỹ thuật đặt cờ hiệu .......................................................................................................... 19 1.10 Kỹ thuật đếm...................................................................................................................... 19 1.11 Kỹ thuật thêm/xóa phần tử ............................................................................................... 19 1.12 Kỹ thuật sắp xếp ................................................................................................................ 19 1.13 Tối ƣu hóa........................................................................................................................... 20 TÀI LIỆU THAM KHẢO................................................................................................... 20

Tong quan ki thuat lap trinh

Embed Size (px)

Citation preview

Page 1: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 1

MỘT SỐ VẤN ĐỀ CỦA KỸ THUẬT LẬP TRÌNH ............................................................. 2

1.1 Phân tích và đặc tả vấn đề bài toán ................................................................................... 2

1.2 Chọn lựa cấu trúc dữ liệu và phát triển thuật toán.......................................................... 2

1.3 Mã hóa chƣơng trình ........................................................................................................... 2

1.3.1 Giới thiệu .......................................................................................................................... 2

1.3.2 Cách đặt tên biến và tên hằng......................................................................................... 2

1.3.3 Trình bày tổng quan của chƣơng trình ......................................................................... 3

1.4 Nguyên lý lập trình .............................................................................................................. 4

1.4.1 Nguyên lý tối thiểu ........................................................................................................... 4

1.4.2 Nguyên lý địa phƣơng ...................................................................................................... 4

1.4.3 Nguyên lý nhất quán ........................................................................................................ 5

1.4.4 Nguyên lý an toàn ............................................................................................................ 6

1.4.5 Phƣơng pháp TOP-DOWN ............................................................................................. 8

1.4.6 Phƣơng pháp BOTTOM-UP ......................................................................................... 11

1.5 Kiểm thử chƣơng trình ..................................................................................................... 14

1.6 Bảo trì chƣơng trình .......................................................................................................... 14

KỸ THUẬT TỐI ƢU HÓA CHƢƠNG TRÌNH .................................................................. 15

1.7 Ví dụ bài toán ..................................................................................................................... 15

1.7.1 Cách giải thứ nhất .......................................................................................................... 15

1.7.2 Cách giải thứ hai ............................................................................................................ 15

1.7.3 Cách giải thứ ba ............................................................................................................. 16

1.7.4 Cách giải thứ tƣ .............................................................................................................. 17

BÀI TẬP .......................................................................................................................... 19

1.8 Kỹ thuật lính canh ............................................................................................................. 19

1.9 Kỹ thuật đặt cờ hiệu .......................................................................................................... 19

1.10 Kỹ thuật đếm ...................................................................................................................... 19

1.11 Kỹ thuật thêm/xóa phần tử ............................................................................................... 19

1.12 Kỹ thuật sắp xếp ................................................................................................................ 19

1.13 Tối ƣu hóa ........................................................................................................................... 20

TÀI LIỆU THAM KHẢO ................................................................................................... 20

Page 2: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 2

MỘT SỐ VẤN ĐỀ CỦA KỸ THUẬT LẬP TRÌNH

1.1 Phân tích và đặc tả vấn đề bài toán

Xác định rõ thông tin đầu vào (nếu có), thông tin đầu ra. Mô hình hóa bài toán.

1.2 Chọn lựa cấu trúc dữ liệu và phát triển thuật toán

Sau khi việc đặc tả vấn đề hoàn tất, các cấu trúc dữ liệu thích hợp cần được chọn lựa để tổ

chức dữ liệu vào và các thuật toán phải được thiết kế để xử lý dữ liệu đó và để tạo đầu ra theo yêu

cầu của vấn đề bài toán. Giai đoạn này đòi hỏi sự khéo léo và óc sáng tạo và là giai đoạn khó khăn

nhất. Các kỹ thuật lập trình thường gặp, các chiến lược thiết kế thuật tóan quan trọng như: chia để

trị, quay lui, nhánh cận, tham lam, quy hoạch động, … sẽ được tìm hiểu trong các chuơng tiếp theo.

1.3 Mã hóa chƣơng trình

1.3.1 Giới thiệu

Phong cách lập trình của một lập trình viên cũng giống như phong cách sống của một con

người. Có lẽ song song với việc học các kiến thức phục vụ cho công việc lập trình sinh viên phải

học và rèn luyện cho bản thân mình một phong cách lập trình tốt. Việc học và rèn luyện một

phong cách lập trình tốt nên tiến hành ngay từ khi mỗi sinh viên mới bắt tay vào học lập trình.

Một phong cách lập trình tốt sẽ giúp sinh viên tạo ra một chương trình dễ đọc, dễ hiểu, dễ tìm và

sửa lỗi, dễ nhận thấy cấu trúc bậc cao của chương trình. Những điều này sẽ thuận lợi cho cả

người viết chương trình, người đánh giá chương trình, và người bảo trì chương trình. Sau đây là

một số những lời khuyên về một phong cách lập trình tốt được đúc kết từ kinh nghiệm của các

chuyên gia lập trình trên thế giới.

1.3.2 Cách đặt tên biến và tên hằng

Khi mới học lập trình, chúng ta thường cảm thấy việc đặt tên biến và tên hằng rất dễ dàng,

bởi vì những chương trình đầu tiên thường ngắn, đơn giản, dễ hiểu và được lập trình trong một

khoảng thời gian ngắn, liên tục. Nhưng sau khi lượng kiến thức đã nhiều, các chương trình bắt

đầu dài ra, phức tạp hơn, số lượng biến và hằng cần dùng ngày càng nhiều, đặc biệt là một

chương trình có thể được lập trình trong một khoảng thời gian dài, có thể là vài ngày, hoặc vài

tháng, thậm chí có những chương trình mà cả một công ty phần mềm có thể phải viết trên mười

năm, khi đó việc đặt tên biến và tên hằng sẽ là vấn đề không thể không quan tâm. Sinh viên phải

biết cách đặt tên biến và tên hằng sao cho chúng mang ý nghĩa rõ ràng và có tính gợi nhớ (lập

trình viên có thể nhìn vào tên biến và tên hằng và hiểu được chức năng và tác dụng của chúng).

Sau đây là một số lời khuyển về cách đặt tên biến và tên hằng:

1.3.2.1 Tên biến, tên hằng phải đƣợc đặt bởi một dãy kí tự có nghĩa

Ví dụ: char pp;

Cách đặt tên biến như vậy là không tốt, biến pp không mang ý nghĩa và không gợi nhớ cho

lập trình viên.

Nên đặt là char person_position;

Cách đặt tên biến như vậy là tốt, tên biến mang ý nghĩa rõ ràng đồng thời gợi nhớ cho lập

trình viên.

1.3.2.2 Tên biến không nên đặt bằng toàn kí tự in hoa

Ví dụ: char PERSON_POSITION;

Việc đặt tên biến như thế này là không tốt, bởi nếu ta coi một chương trình như một văn bản

thì trong một văn bản có quá nhiều chữ cái in hoa sẽ làm cho người đọc khó chịu và không nhận

ra những chỗ quan trọng khác trong văn bản.

1.3.2.3 Tên hằng nên đặt bằng một chuỗi kí tự in hoa

Trong một chương trình, số lượng các hằng xuất hiện không nhiều, vậy nếu chúng ta đặt tên

Page 3: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 3

hằng là chuỗi kí tự in hoa sẽ phân biệt được chúng với tên biến. tên kiểu dữ liệu cũng nên đặt

bằng chuỗi ký tự hoa.

1.3.2.4 Các từ khoá nên dùng toàn chữ cái thƣờng

Như chúng ta đã biết trong một số ngôn ngữ thì bộ chương trình dịch không phân biệt chữ

cái in hoa và chữ cái in thường (ví dụ trong ngôn ngữ lập trình Pascal). Nhưng trong một số ngôn

ngữ lập trình khác thì chữ các in hoa và chữ cái in thường được coi là khác nhau. Trong hầu hết

các ngôn ngữ lập trình này, các từ khoá được viết dưới dạng chữ cái in thường (ví dụ trong ngôn

ngữ lập trình C++). Vậy một thói quen về việc dùng các từ khoá bằng toàn chữ cái in thường thì

sẽ dễ dàng cho chúng ta hơn rất nhiều khi chúng ta học một ngôn ngữ lập trình mới.

1.3.2.5 Cách đặt tên biến dài

Khi một chương trình dài và phức tạp, đặc biệt là nhiều người viết thì lượng biến cần phải

sử dụng là rất lớn. Để tránh sự nhầm lẫn giữa các biến khác nhau thì các lập trình viên bắt buộc

phải đặt các tên biến dài (thường từ 2 từ trở lên). Đối với các tên biến dài, ta có thể dùng chữ cái

in hoa hoặc dấu gạch chân dưới để phân biệt giữa các từ khác nhau.

Ví dụ: person_position hoặc PersonPosition: Là cách đặt tên biến tốt.

personposition: Là cách đặt tên biến không tốt.

1.3.2.6 Cách đặt tên hàm

Có nhiều cách đặt tên hàm khác nhau. Cách đặt tên hàm cũng giống như cách đặt tên biến.

Nhưng khi đặt tên cho một hàm thì cần có thêm một vài chú ý về cấu trúc ngữ pháp của tên hàm.

Một trong những cách đặt phổ biến trong việc đặt tên hàm là sử dụng cấu trúc ngữ pháp: Động từ

+ Cụm danh từ.

1.3.3 Trình bày tổng quan của chƣơng trình

1.3.3.1 Tổng quan chƣơng trình

Chương trình phải được lùi đầu dòng một cách thích hợp. Các câu lệnh bên trong các vòng

lặp (ví dụ: for, while, do while), các lệnh điều kiện rẽ nhánh (ví dụ: if, switch ) và trong cặp { }

phải được lùi thêm một bậc. Bên cạnh đó các câu lệnh khai báo biến cũng nên lùi vào một bậc.

Việc lùi vào một bậc có thể được thực hiện bằng một số dấu cách (space) hoặc dấu tab. Việc lùi

vào một cách thích hợp các dòng lệnh sẽ cho lập trình viên thấy được cấu trúc khối của chương

trình, điều đó giúp cho việc tìm lỗi và sửa lỗi dễ dàng hơn.

1.3.3.2 Đoạn chƣơng trình và dòng lệnh

Trong một file chương trình thường gồm nhiều hàm, trong một hàm có thể lại gồm nhiều

đoạn nhỏ. Mỗi đoạn nhỏ nên được tách nhau ra bởi các dòng trống. Trong một đoạn nhỏ ý nghĩa

của các dòng lệnh nên tương đối giống nhau và phù hợp với nhau.

Trong chương trình mỗi dòng chỉ nên viết một dòng lệnh (các bộ chương trình dịch của các

ngôn ngữ thường bỏ qua tất cả các dấu trống, vì vậy việc bạn cố viết một chương trình với số

lượng dòng ít bằng cách đẩy nhiều dòng lệnh trên cùng một dòng là vô nghĩa). Mỗi dòng lệnh

trên một dòng sẽ giúp bạn dễ dàng trong việc gỡ rối chương trình.

Đối với mỗi chương trình con nên liệt kê điều kiện trước và điều kiện sau của chúng.

Nên có ít nhất một dòng trống (hoặc một dòng kẻ) trước mỗi chương trình con để tách biệt

nhưng chương trình con, điều này sẽ giúp cho ta phân biệt được các chương trình con khác nhau.

1.3.3.3 Chƣơng trình phải có cấu trúc tốt

Sử dụng lối tiếp cận: “từ trên xuống dưới” để viết một chương trình cho một vấn đề phức tạp

nên chia chúng thành các vấn đề đơn giản hơn và viết thành từng hàm để giải quyết nó. Những hàm

này nên ngắn và có tính độc lập tương đối: mỗi hàm nên giải quyết trọn vẹn một vấn đề cụ thể.

1.3.3.4 Sử dụng tham trị, tham biến đúng lúc

Sử dụng các biến cục trong các chương trình con. Các biến chỉ dùng trong một chương trình

con nên được khai báo trong chương trình con đó.

Page 4: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 4

Sử dụng các tham số để chuyển thông tin đến và ra khỏi chương trình con. Nên tránh dùng

biến toàn cục để chuyển giao thông tin giữa các chương trình con bởi vì điều đó sẽ phá vỡ tính

độc lập của chúng. Việc xác định giá trị của một biến toàn cục nào đó tại một điểm trong chương

trình có thể rất khó khăn bởi vì nó có thể đã bị thay thế bởi các chương trình con khác.

Để bảo vệ các tham số không bị thay đổi bởi các chương trình con khác, nên khai báo nó

kiểu tham trị hơn là tham biến. Nếu không, chương trình con có thể thay đổi giá trị của các biến

một cách không định trước trong các chương trình con khác.

1.3.3.5 Chú thích

Mỗi chương trình phải có một phần chú thích chung giới thiệu mục tiêu của chương trình.

Điều đó sẽ giúp bạn dễ dàng nâng cấp và bảo trì chương trình trong thời gian sau này. Phần chú

thích của chương trình như một phát biểu ngắn ngọn về bài toán cần giải quyết.

Mỗi chương trình con cần phải có chú thích về mục đích của chương trình con đó, những

chú thích làm cho người đọc hiểu được mục đích của chương trình con chứ không phải giúp

người đọc hiểu được chương trình con đó được thực hiện như thế nào.

Đối với các cấu trúc dữ liệu quan trọng phải có chú thích và về mục đích của cấu trúc đó và

mỗi liên hệ của nó với các phần dữ liệu khác. Vấn đề chú thích cho những cấu trúc dữ liệu đặc

biệt cần thiết cho lập trình hướng chức năng.

Những đoạn chương trình phức tạp và khó hiểu, cần chú thích cẩn thận. Lời chú thích sẽ

liên quan đến nội dung của đoạn chương trình đó.

Mỗi chương trình con cần có chú thích về ý nghĩa cho từng tham số của chương trình.

1.4 Nguyên lý lập trình

1.4.1 Nguyên lý tối thiểu

Hãy bắt đầu từ một tập nguyên tắc và tối thiểu các phương tiện là các cấu trúc lệnh, kiểu dữ

liệu cùng các phép toán trên nó và thực hiện viết chương trình. Sau khi nắm chắc những công cụ

vòng đầu mới đặt vấn đề mở rộng sang hệ thống thư viện tiện ích của ngôn ngữ.

Khi làm quen với một ngôn ngữ lập trình nào đó, không nhất thiết phải lệ thuộc quá nhiều

vào hệ thống thư viện hàm của ngôn ngữ, mà điều quan trọng hơn là trước một bài toán cụ thể,

chúng ta sử dụng ngôn ngữ để giải quyết nó thế nào, và phương án tốt nhất là lập trình bằng chính

hệ thống thư viện hàm của riêng mình. Do vậy, đối với các ngôn ngữ lập trình, chúng ta chỉ cần

nắm vững một số các công cụ tối thiểu. Các phép toán số học: +; -; *; %; /, các phép toán số học

mở rộng: ++, --, +=, -=, *=, /=, %=, các phép toán so sánh: >, <, >=, <=, ==, các phép toán logic:

&&, ||, !, các toán tử thao tác bít &, |, ^, <<, >>, ~, các lệnh vào ra cơ bản, thao tác trên các kiểu

dữ liệu có cấu trúc, thao tác trên con trỏ, thao tác trên file, …

1.4.2 Nguyên lý địa phƣơng

Các biến địa phương trong hàm, thủ tục hoặc chu trình cho dù có trùng tên với biến toàn cục

thì khi xử lý biến đó trong hàm hoặc thủ tục vẫn không làm thay đổi giá trị của biến toàn cục.

Tên của các biến trong đối của hàm hoặc thủ tục đều là hình thức.

Mọi biến hình thức truyền theo trị cho hàm hoặc thủ tục đều là các biến địa phương.

Các biến khai báo bên trong các chương trình con, hàm hoặc thủ tục đều là biến địa phương.

Khi phải sử dụng biến phụ nên dùng biến địa phương và hạn chế tối đa việc sử dụng biến

toàn cục để tránh xảy ra các hiệu ứng phụ.

Ví dụ hoán đổi giá trị của hai số a và b sau đây sẽ minh họa rõ hơn về nguyên lý địa phương.

Ví dụ: Hoán đổi giá trị của hai biến a và b

#include <stdio.h>

#include <conio.h>

int a, b; // khai báo a, b là hai biến toàn cục.

void Swap(void)

{

Page 5: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 5

int a,b, temp; // khai báo a, b là hai biến địa phương

a= 3; b=5; // gán giá trị cho a và b

temp=a;

a=b;

b=temp; // đổi giá trị của a và b

printf(“\n Kết quả thực hiện trong thủ tục a=%5d b=%5d”, a, b);

}

void main(void)

{

a=1; b=8; // khởi đầu giá trị cho biến toàn cục a, b.

Swap();

printf(“\n Kết quả sau khi thực hiện thủ tục a =%5d b=%5d”, a, b);

getch();

}

Kết quả thực hiện chương trình:

Kết quả thực hiện trong thủ tục a = 5, b=3

Kết quả sau khi thực hiện thủ tục a = 1, b =8

Trong ví dụ trên a, b là hai biến toàn cục, hai biến a, b trong thủ tục Swap là hai biến cục bộ.

Các thao tác trong thủ tục Swap gán cho a giá trị 3 và b giá trị 5 sau đó thực hiện đổi giá trị của

a=5 và b=3 là công việc xử lý nội bộ của thủ tục mà không làm thay đổi giá trị của biến toàn cục

của a, b sau thi thực hiện xong thủ tục Swap. Do vậy, kết quả sau khi thực hiện Swap a = 1, b =8;

Điều đó chứng tỏ trong thủ tục Swap chưa bao giờ sử dụng tới hai biến toàn cục a và b. Tuy

nhiên, nếu ta thay 2 dòng int a, b, temp; và dòng a = 3; b = 5; bằng dòng int temp; thì thủ tục

Swap lại làm thay đổi giá trị của biến toàn cục a và b vì nó thao tác trực tiếp trên biến toàn cục.

1.4.3 Nguyên lý nhất quán

Dữ liệu thế nào thì phải thao tác thế ấy. Cần sớm phát hiện những mâu thuẫn giữa cấu trúc

dữ liệu và thao tác để kịp thời khắc phục.

Như chúng ta đã biết, kiểu là một tên chỉ tập các đối tượng thuộc miền xác định cùng với

những thao tác trên nó. Một biến khi định nghĩa bao giờ cũng thuộc một kiểu xác định nào đó

hoặc là kiểu cơ bản hoặc kiểu do người dùng định nghĩa. Thao tác với biến phụ thuộc vào những

thao tác được phép của kiểu. Hai kiểu khác nhau được phân biệt bởi tên, miền xác định và các

phép toán trên kiểu dữ liệu. Tuy nhiên, trên thực tế có nhiều lỗi nhập nhằng giữa phép toán và cấu

trúc dữ liệu mà chúng ta cần hiểu rõ.

Đối với kiểu ký tự, về nguyên tắc chúng ta không được phép thực hiện các phép toán số học

trên nó, nhưng ngôn ngữ C luôn đồng nhất giữa ký tự với số nguyên có độ lớn 1 byte. Do vậy,

những phép toán số học trên các ký tự thực chất là những phép toán số học trên các số nguyên.

Chẳng hạn, những thao tác như trong khai báo dưới đây là được phép:

char x1 = ‟A‟, x2 = ‟z‟;

x1 = (x1 + 100) % 255;

x2 = (x2 - x1) %255;

Mặc dù x1, x2 được khai báo là hai biến kiểu char, nhưng trong thao tác

x1 = (x1 + 100) % 255;

x2 = (x2 + x1) % 255;

Chương trình dịch sẽ tự động chuyển đổi x1 thành mã của ký tự „A‟ là 65, x2 thành mã ký

tự „z‟ là 122 để thực hiện phép toán. Kết quả nhận được x1 là một ký tự có mã là (65+100)%255

= 165; x2 là ký tự có mã là 32 ứng với mã của ký tự space.

Chúng ta có thể thực hiện được các phép toán số học trên kiểu int, long, float, double.

Nhưng đối với int và long, chúng ta cần đặc biệt chú ý phép chia hai số nguyên cho ta một số

nguyên, tích hai số nguyên cho ta một số nguyên, tổng hai số nguyên cho ta một số nguyên mặc

Page 6: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 6

dù thương hai số nguyên là một số thực, tích hai số nguyên hoặc tổng hai số nguyên có thể là một

số long int. Do vậy, muốn nhận được kết quả đúng, chúng ta cần phải chuyển đổi các biến thuộc

cùng một kiểu trước khi thực hiện phép toán. Ngược lại, ta không thể lấy modulo của hai số thực

hoặc thực hiện các thao tác dịch chuyển bít trên nó, vì những thao tác đó không nằm trong định

nghĩa của kiểu.

Điều tương tự cũng xảy ra với các string. Trong Pascal, phép toán so sánh hai string hoặc

gán trực tiếp hai Record cùng kiểu với nhau là được phép, ví dụ : Str1 > Str2, Str1 := Str2; Nhưng

trong C thì các phép toán trên lại không được định nghĩa, nếu muốn thực hiện nó, chúng ta chỉ có

cách định nghĩa lại hoặc thực hiện nó thông qua các lời gọi hàm.

1.4.4 Nguyên lý an toàn

Lỗi nặng nhất nằm ở mức cao nhất (mức ý đồ thiết kế) và ở mức thấp nhất thủ tục phải chịu

tải lớn nhất.

Mọi lỗi, dù là nhỏ nhất cũng phải được phát hiện ở một bước nào đó của chương trình. Quá

trình kiểm tra và phát hiện lỗi phải được thực hiện trước khi lỗi đó hoành hành.

Các loại lỗi thường xảy ra trong khi viết chương trình có thể được tổng kết lại như sau:

Lỗi được thông báo bởi từ khoá error (lỗi cú pháp): loại lỗi này thường xảy ra trong khi soạn

thảo chương trình, chúng ta có thể viết sai các từ khoá ví dụ thay vì viết là int chúng ta soạn thảo

sai thành Int (lỗi chữ in thường thành in hoa), hoặc viết sai cú pháp các biểu thức như thiếu các

dấu ngoặc đơn, ngoặc kép hoặc dấu chấm phẩy khi kết thúc một lệnh, hoặc chưa khai báo nguyên

mẫu cho hàm .

Lỗi được thông báo bởi từ khoá Warning (lỗi cảnh báo): lỗi này thường xảy ra khi ta khai

báo biến trong chương trình nhưng lại không sử dụng tới chúng, hoặc lỗi trong các biểu thức

kiểm tra khi biến được kiểm tra không xác định được giá trị của nó, hoặc lỗi do thứ tự ưu tiên các

phép toán trong biểu thức. Hai loại lỗi error và warning được thông báo ngay khi dịch chương

trình thành file *.OBJ. Quá trình liên kết (linker) các file *.OBJ để tạo nên file chương trình mã

máy *.EXE chỉ được tiếp tục khi chúng ta hiệu đính và khử bỏ mọi lỗi error.

Lỗi xảy ra trong quá trình liên kết: lỗi này thường xuất hiện khi ta sử dụng tới các lời gọi

hàm, nhưng những hàm đó mới chỉ tồn tại dưới dạng nguyên mẫu (function prototype) mà chưa

được mô tả chi tiết các hàm, hoặc những lời hàm gọi chưa đúng với tên của nó. Lỗi này được

khắc phục khi ta bổ sung đoạn chương trình con mô tả chi tiết cho hàm hoặc sửa đổi lại những lời

gọi hàm tương ứng.

Ta quan niệm, lỗi cú pháp (error), lỗi cảnh báo (warning) và lỗi liên kết (linker) là lỗi tầm

thường vì những lỗi này đã được Compiler của các ngôn ngữ lập trình phát hiện được. Để khắc

phục các lỗi loại này, chúng ta chỉ cần phải đọc và hiểu được những thông báo lỗi thường được

viết bằng tiếng Anh. Cũng cần phải lưu ý rằng, do mức độ phức tạp của chương trình dịch nên

không phải lỗi nào cũng được chỉ ra một cách tường minh và chính xác hoàn toàn tại nơi xuất

hiện lỗi.

Loại lỗi cuối cùng mà các compiler không thể phát hiện nổi đó là lỗi do chính lập trình viên

gây nên trong khi thiết kế chương trình và xử lý dữ liệu. Những lỗi này không được compiler

thông báo mà nó phải trả giá bằng quá trình tự test hoặc chứng minh được tính đúng đắn của

chương trình. Lỗi có thể nằm ở chính ý đồ thiết kế, hoặc lỗi do không lường trước được tính chất

của mỗi loại thông tin vào. Ví dụ sau minh họa cho lỗi thường xảy ra thuộc loại này.

Ví dụ: Tính tổng hai đa thức A bậc n, đa thức B bậc m.

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#define MAX 100

typedef float DaThuc[MAX];

Page 7: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 7

void Init(DaThuc A, int *n, DaThuc B, int *m)

{

int i, j;

float temp;

printf("\n Nhap n=");

scanf("%d", n);

printf("\n Nhap m=");

scanf("%d",m);

printf("\n Nhap he so da thuc A:");

for(i=0; i<*n;i++)

{

printf("\n A[%d]=", i);

scanf("%f", &A[i]);

}

printf("\n Nhap he so da thuc B:");

for(i=0; i<*m;i++)

{

printf("\n B[%d]=",i);

scanf("%f", &B[i]);

}

In(A,*n,'A');

In(B,*m,'B');

}

void Tong(DaThuc A, int n, DaThuc B, int m, DaThuc C)

{

int i, k;

if (n>= m )

{

k =n;

for(i=0; i<m; i++)

C[i] = A[i]+B[i];

for (i=m; i<n; i++)

C[i]=A[i];

In(C,k,'C');

}

else

{

k = m;

for(i=0; i<n; i++)

C[i] = A[i]+B[i];

for (i=n; i<m; i++)

C[i]=B[i];

In(C, k, „C‟);

}

}

void In(DaThuc A, int n, char c)

{

int i;

printf("\n Da thuc %c:", c);

Page 8: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 8

for(i=0;i<n; i++)

printf("%6.2f", A[i]);

}

void main(void)

{

DaThuc A, B, C;

int n, m;

Init(A, &n, B, &m);

Tong(A, n, B, m, C);

}

Trong ví dụ trên, chúng ta sử dụng định nghĩa MAX =100 để giải quyết bài toán. MAX

được hiểu là bậc của đa thức lớn nhất mà chúng ta cần xử lý. Như vậy, bản thân việc định nghĩa

MAX đã hạn chế tới phạm vi bài toán, hạn chế đó cũng có thể xuất phát từ ý đồ thiết kế. Do vậy,

nếu người sử dụng nhập n>MAX thì chương trình sẽ gặp lỗi. Nếu chúng ta khắc phục bằng cách

định nghĩa BẬC đủ lớn thì trong trường hợp xử lý các đa thức có bậc n nhỏ sẽ gây nên hiện tượng

lãng phí bộ nhớ, và trong nhiều trường hợp không đủ bộ nhớ để định nghĩa đa thức. Giải pháp

khắc phục các lỗi loại này là chúng ta sử dụng con trỏ thay cho các hằng.

1.4.5 Phƣơng pháp TOP-DOWN

Quá trình phân tích bài toán được thực hiện từ trên xuống dưới. Từ vấn đề chung nhất đến

vấn đề cụ thể nhất. Từ mức trừu tượng mang tính chất tổng quan tới mức đơn giản nhất là đơn vị

chương trình.

Một trong những nguyên lý quan trọng của lập trình cấu trúc là phương pháp phân tích từ

trên xuống (Top - Down) với quan điểm “thấy cây không bằng thấy rừng”, phải đứng cao hơn để

quan sát tổng thể khu rừng chứ không thể đứng trong rừng quan sát chính nó.

Quá trình phân rã bài toán được thực hiện theo từng mức khác nhau. Mức thấp nhất được

gọi là mức tổng quan (level 0), mức tổng quan cho phép ta nhìn tổng thể hệ thống thông qua các

chức năng của nó, nói cách khác mức 0 sẽ trả lời thay cho câu hỏi “Hệ thống có thể thực hiện

được những gì ?”. Mức tiếp theo là mức các chức năng chính. Ở mức này, những chức năng cụ

thể được mô tả. Một hệ thống có thể được phân tích thành nhiều mức khác nhau, mức thấp được

phép sử dụng các dịch vụ của mức cao. Quá trình phân tích tiếp tục phân rã hệ thống theo từng

chức năng phụ cho tới khi nào nhận được mức các đơn thể (UNIT, Function, Procedure), khi đó

chúng ta tiến hành cài đặt hệ thống.

Chúng ta sẽ làm rõ hơn từng mức của quá trình Top-Down thông qua bài toán sau:

Bài toán: Cho hai số nguyên có biểu diễn nhị phân là a=(a1, a2, . . ., an), b = (b1, b2,.., bn); ai,

bi =0, 1, i=1, 2, . . .n. Hãy xây dựng tập các thao tác trên hai số nguyên đó.

Mức tổng quan (level 0):

Hình dung toàn bộ những thao tác trên hai số nguyên a=(a1, a2, . . ., an), b=(b1,b2,..,bn) với

đầy đủ những chức năng chính của nó. Giả sử những thao tác đó bao gồm:

F1- Chuyển đổi a, b thành các số nhị phân;

F2- Tính tổng hai số nguyên: a + b;

F3- Tính hiệu hai số nguyên: a - b;

F4 Tính tích hai số nguyên: a *b;

F5- Thương hai số nguyên : a/b;

F6- Phần dư hai số nguyên: a % b;

F7- Ước số chung lớn nhất của hai số nguyên.

Mức 1: Mức các chức năng chính: mỗi chức năng cần mô tả đầy đủ thông tin vào (Input),

thông tin ra (Output), khuôn dạng (Format) và các hành động (Actions).

Chức năng F1: Chuyển đổi a, b thành các số ở hệ nhị phân

Input: a : integer;

Page 9: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 9

Output: a=(a1, a2, . . ., an)b; (*khai triển cơ số bất kỳ*)

Format: Binary(a);

Actions

{

Q = n;

k=0;

While ( Q≠ 0 )

{

ak = q mod b;

q = q div b;

k = k +1;

}

< Khai triển cơ số b của a là (ak-1, ak-2, . ., a1, a0) >;

}

Chức năng F2: Tính tổng hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = a + b;

Format: Addition(a, b);

Actions

{

c = 0;

for (j = 0; j< n; j++)

{

d = (aj + bj + c) div 2;

sj = aj + bj + c - 2d;

c = d;

}

sn = c;

< Khai triển nhị phân của tổng là (sn,sn-1. . .s1,s0) >

}

Chức năng F3: Hiệu hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = a – b;

Format: Subtraction(a, b);

Actions

{

b = -b;

c = Addition(a, b);

return(c);

}

Chức năng F4: Tích hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = a * b;

Format: Multual(a, b);

Actions

{

for (j =0; j< n; j++)

{

if ( bj =1)

cj = a<<j;

Page 10: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 10

else

cj = 0;

}

(* c0, c1, . . ., cn-1 là các tích riêng*)

p=0;

for(j=0; j< n; j++)

{

p = Addition(p, cj);

}

return(p);

}

Chức năng F5: Thương hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = a div b;

Format: Division(a, b);

Actions

{

c = 0;

while ( a>= b )

{

c = c +1;

a = Subtraction(a, b);

}

return(c);

}

Chức năng F6: Modulo hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = a mod b;

Format: Modulation(a, b);

Actions

{

while ( a>= b )

a = Subtraction(a, b);

return(a);

}

Chức năng F7: Ước số chung lớn nhất hai số nguyên a, b.

Input: a=(a1, a2, . . ., an), b = (b1, b2, .., bn);

Output: c = USCLN(a,b);

Format: USCLN(a, b);

Actions

{

while ( a≠ b )

{

if (a > b)

a=Subtraction(a, b)

else

b = Subtraction(b, a);

}

return(a);

}

Page 11: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 11

Để ý rằng, sau khi phân rã bài toán ở mức 1, chúng ta chỉ cần xây dựng hai phép toán cộng

và phép tính nhân các số nhị phân của a, b. Vì hiệu hai số a và b chính là tổng số của (a,-b).

Tương tự như vậy, tích hai số a và b được biểu diễn bằng tổng của một số lần phép nhân một bít

nhị phân của với a. Phép chia và lấy phần dư hai số a và b chính là phép trừ nhiều lần số a. Phép

tìm USCLN cũng tương tự như vậy.

Đối với các hệ thống lớn, quá trình còn được mô tả tiếp tục cho tới khi nhận được mức đơn

vị chương trình. Trong ví dụ đơn giản này, mức đơn vị chương trình xuất hiện ngay tại mức 1 nên

chúng ta không cần phân rã tiếp nữa mà dừng lại để cài đặt hệ thống.

1.4.6 Phƣơng pháp BOTTOM-UP

Đi từ cái riêng tới cái chung, từ các đối tượng thành phần ở mức cao tới các đối tượng thành

phần ở mức thấp, từ mức đơn vị chương trình tới mức tổng thể, từ những đơn vị đã biết lắp đặt

thành những đơn vị mới.

Nếu như phương pháp Top-Down là phương pháp phân rã vấn đề một cách có hệ thống từ

trên xuống, được ứng dụng chủ yếu cho quá trình phân tích và thiết hệ thống, thì phương pháp

Bottom- Up thường được sử dụng cho quá trình cài đặt hệ thống. Trong ví dụ trên, chúng ta sẽ

không thể xây dựng được chương trình một cách hoàn chỉnh nếu như ta chưa xây dựng được các

hàm Binary(a), Addition(a,b), Subtraction(a,b), Multial(a,b), Division(a,b), Modulation(a,b),

USCLN(a,b). Chương trình sau thể hiện quá trình cài đặt chương trình theo nguyên lý Botton-Up:

#include <stdio.h>

#include <math.h>

#include <conio.h>

#include <stdlib.h>

#include <alloc.h>

#include <dos.h>

void Init(int *a, int *b)

{

printf("\n Nhap a=");

scanf("%d", a);

printf("\n Nhap b=");

scanf("%d", b);

}

void Binary(int a)

{

int i, k=1;

for(i=15; i>=0; i--)

{

if ( a & (k<<i))

printf("%2d",1);

else

printf("%2d",0);

}

printf("\n");delay(500);

}

int bit(int a, int k)

{

int j=1;

if (a & (j<<k))

return(1);

Page 12: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 12

return(0);

}

int Addition(int a, int b)

{

int du, d, s, j, c=0;

du=0;

for ( j=0; j<=15; j++)

{

d =( bit(a,j) + bit(b, j) +du)/2;

s = bit(a,j)+bit(b,j)+ du - 2*d;

c = c | (s <<j);

du = d;

}

return(c);

}

int Multiply(int a, int b)

{

int c,j, p=0;

for(j=0; j<=15; j++)

{

c = bit(b, j);

if (c==1)

{

c = a<<j;

p= Addition(p, c);

}

else

c=0;

}

return(p);

}

int Subtraction(int a, int b)

{

int c;

b=-b;

c=Addition(a,b);

return(c);

}

int Modulo(int a, int b)

{

while(a>=b)

a = Subtraction(a,b);

return(a);

}

int Division(int a, int b)

Page 13: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 13

{

int d=0;

while(a>=b)

{

a= Subtraction(a,b);

d++;

}

return(d);

}

int USCLN(int a, int b)

{

while(a!=b)

{

if(a>b)

a = Subtraction(a,b);

else

b = Subtraction(b,a);

}

return(a);

}

void main(void)

{

int a, b, key, control=0;

do

{

clrscr();

printf("\n Tap thao tac voi so nguyen");

printf("\n 1 - Nhap hai so a,b");

printf("\n 2 - So nhi phan cua a, b");

printf("\n 3 - Tong hai so a,b");

printf("\n 4 - Hieu hai so a,b");

printf("\n 5 - Tich hai so a,b");

printf("\n 6 - Thuong hai so a,b");

printf("\n 7 - Phan du hai so a,b");

printf("\n 8 - USCLN hai so a,b");

printf("\n 0 - Tro ve: ");

key=getch();

switch(key)

{

case '1': Init(&a, &b);

control=1;

break;

case '2': if (control)

{

Binary(a);

Binary(b);

}

break;

Page 14: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 14

case‟3‟: if(control)

printf("\n Tong a+b = %d", Addition(a, b));

break;

case '4': if (control)

printf("\n Hieu a-b =%d", Subtraction(a, b));

break;

case '5': if(control)

printf("\n Tich a*b =%d", Multiply(a,b));

break;

case '6': if(control)

printf("\n Chia nguyen a div b=%d",Division(a,b));

break;

case '7': if(control)

printf("\n Phan du a mod b =%d", Modulo(a,b));

break;

case '8': if(control)

printf("\n Uoc so chung lon nhat:%d",USCLN(a,b));

break;

}

} while(key!='0');

delay(1000);

}

1.5 Kiểm thử chƣơng trình

Cần đưa ra các bộ test để kiểm thử các trường hợp có thể xảy ra của chương trình. Phần lớn

khi một chương trình viết đều có lỗi: các lỗi về cú pháp ngôn ngữ thì dễ sửa, tuy nhiên các lỗi

logíc thì khó sửa hơn nhiều. Lỗi này là do mã hóa không chính xác thuật toán hoặc cũng có thể là

do chính bản thân việc thiết kế thuật toán.

1.6 Bảo trì chƣơng trình

Khi một chương trình đã được mã hóa và kiểm thử thành công. Nó bắt đầu cuộc sống có ích

của nó. Nó có thể được sử dụng trong nhiều năm hoặc cũng có thể là trong một chu kỳ sống ngắn.

Phần lớn các chương trình luôn cần được cập nhật, thay đổi.

Việc bảo trì phần mềm là một phận quan trọng trong chu kỳ sống của một phần mềm. đôi

lúc thời gian để bảo trì phần mềm còn nhiều hơn cả thời gian viết chương trình khi chuyển giao

cho khách hàng. Do đó, mỗi lập trình viên phải làm hết sức để có được một chương trình dễ đọc,

có cấu trúc rõ ràng, dễ thay đổi, dễ bảo trì.

Page 15: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 15

KỸ THUẬT TỐI ƢU HÓA CHƢƠNG TRÌNH

Tối ưu hoá là một đòi hỏi thường xuyên không chỉ trong quá trình giải quyết các bài toán tin

học, mà ngay trong giải quyết các công việc thường ngày của chúng ta. Tối ưu hoá thuật toán là

một công việc yêu cầu tư duy rất cao, cùng với khả năng sử dụng thuần thục các cấu trúc dữ liệu.

Trong bài viết này, tôi xin được chia sẻ với các bạn về tối ưu hoá thuật toán trong giải quyết một

bài toán tưởng chừng rất đơn giản, nhưng việc tìm ra thuật toán tối ưu cho nó lại không dễ chút

nào. Tối ưu hoá thường được tiến hành theo hai góc độ đó là tối ưu theo không gian có nghĩa là

tối ưu không gian lưu trữ (bộ nhớ), và tối ưu theo thời gian có nghĩa là giảm độ phức tạp thuật

toán, giảm các bước xử lý trong chương trình…Tuy nhiên không phải lúc nào ta cũng có thể đạt

được đồng thời cả hai điều đó, trong nhiều trường hợp tối ưu về thời gian sẽ làm tăng không gian

lưu trữ, và ngược lại.

1.7 Ví dụ bài toán

Cho dãy số a0, a1, …, an-1. Hãy tìm dãy con có tổng lớn nhất.

Chúng ta thấy rằng bài toán khá đơn giản, song nếu không được giải quyết tốt sẽ không đáp

ứng được yêu cầu về thời gian. Với các bài toán có hạn chế về thời gian như thế, đòi hỏi ta phải

đưa ra được thuật toán tối ưu.

1.7.1 Cách giải thứ nhất

Cách làm sơ khai nhất là xét tất cả các đoạn con có thể theo đoạn mã sau:

int Findmax(int a[], int &dau, int &cuoi)

{

max=a[0];

dau=cuoi=0;

for(i=0;i<N;i++)

{

for(j=i;j<N;j++)

{

s=0;

for(k=i;k<=j;k++)

s+=a[k];

if (max<s)

{

max=s;

dau=i;

cuoi=j;

}

}

}

return max;

}

Ta dễ thấy rằng cách làm trên có độ phức tạp O(n3) với 3 vòng for lồng nhau. Đoạn mã trên

có quá nhiều cái để ta có thể tối ưu, ví dụ như khi tính tổng từ i đến j đã không tận dụng được

tổng từ i đến (j-1) đã được tính trước đó. Do vậy đây là một đoạn mã khó có thể chấp nhận được

về mặt kỹ thuật lập trình lẫn kỹ năng viết chương trình.

1.7.2 Cách giải thứ hai

Với một cải tiến nhỏ ta sẽ xét hết tất cả các dãy con có thể của dãy số bắt đầu từ vị trí thứ i

(i = 0,1,2…n-1), với việc tận dụng lại các giá trị đã được tính trước đó. Chúng ta cùng theo dõi

đoạn mã sau:

Page 16: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 16

int Findmax(int a[], int &dau, int &cuoi)

{

max=a[0];

dau=cuoi=0;

for(i=0;i<N-1;i++)

{

s=0;

for(j=i;j<N;j++)

{

s+=a[j];

if (s>max)

{

max=s;

dau=i;

cuoi=j;

}

}

}

return max;

}

Thuật toán trên sử dụng hai vòng lặp lồng nhau, vòng đầu tiên lặp n-1 lần, vòng thứ hai lặp

tối đa n lần, nên dễ thấy độ phức tạp của thuật toán này là O(n2). Chúng tôi tin rằng nhiều người

sẽ đồng ý sử dụng cách làm trên, nhưng chắc chắn một điều là nó không đáp ứng được đòi hỏi về

giới hạn thời gian.

1.7.3 Cách giải thứ ba

Ta cùng tìm hiểu một cải tiến khác chắc chắn sẽ tốt hơn. Ta sử dụng thêm hai mảng k,c mỗi

mảng gồm n phần tử. Trong đó k[i] sẽ lưu giá trị lớn nhất của tổng dãy con mà giá trị a[i] là cuối

dãy, c[i] sẽ lưu chỉ số đầu của dãy đó. Như vậy k[i] =max(a[i],a[i]+k[i-1]). Hãy theo dõi đoạn mã

khá lý thú sau, nó được xây dựng trên tư tưởng quy hoạch động:

int Findmax(int a[], int &dau, int &cuoi)

{

k[0]=max=a[0];

c[0]=dau=cuoi=0;

for(i=1;i<N;i++)

{

s=k[i-1]+a[i];

k[i]=a[i];

c[i]=i;

if (s>k[i])

{

k[i]=s;

c[i]=c[i-1];

}

//tìm tổng lớn nhất

if (k[i]>max)

{

max=k[i];

cuoi=i;

}

Page 17: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 17

}

dau=c[cuoi];

return max;

}

Với thuật toán trên độ phức tạp là O(n) với chỉ một vòng lặp n lần duy nhất. Như vậy, có thể

nói ta đã tối ưu xong về mặt thời gian từ độ phức tạp O(n3) xuống còn O(n). Về không gian bộ

nhớ, ở cách làm trên ta đã sử dụng thêm hai mảng k và c mỗi mảng n phần tử, nếu không gian

đầu vào không phải hạn chế 10000 mà là 20000 thậm chí 30000 thì sao? Chắc chắn sẽ không đủ

không gian bộ nhớ để sử dụng hai mảng k và c như thế. Vậy giải quyết thế nào? Ta sẽ bỏ mảng k

đi khi sử dụng tính toán hoàn toàn trên mảng a, vậy có thể bỏ nốt mảng c đi không nếu bỏ đi thì ta

sẽ xác định giá trị đầu của mỗi dãy như thế nào đây?

1.7.4 Cách giải thứ tƣ

Tại mỗi vị trí đang xét ta sẽ so sánh để tìm ra dãy có tổng lớn nhất ngay như trên, và như

vậy ta sẽ sử dụng một biến để lưu giá trị đầu của dãy tìm được! Ý tưởng đó thể hiện qua đoạn mã

sau:

int Findmax(int a[], int &dau, int &cuoi)

{

dau=cuoi=luu=0;

max=a[0];

for(i=1;i<N;i++)

{

a[i]+=a[i-1];

if (a[i]<a[i]-a[i-1])

{

a[i]-=a[i-1];

dau=i;

}

//tìm tổng lớn nhất

if (a[i]>max)

{

max=a[i];

cuoi=i;

luu=dau;

}

}

dau=luu;

return max;

}

Cách làm này quả thật hiệu quả, tuy nhiên nó đã làm biến đổi dãy số ban đầu (mảng a). Làm

sao có thể giữ nguyên dãy số, không dùng thêm mảng phụ, vẫn đáp ứng được yêu cầu đề bài. Với

một cải tiến nhỏ ta sẽ thấy ở đoạn mã sau thật tuyệt vời:

int Findmax(int a[], int &dau, int &cuoi)

{

dau=luu=cuoi=0;

max=s=a[0];

for(i=1;i<N;i++)

{

s+=a[i];

if (s<a[i])

Page 18: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 18

{

s=a[i];

dau=i;

}

//tìm tổng lớn nhất

if (s>max)

{

max=s;

cuoi=i;

luu=dau;

}

}

dau=luu;

return max;

}

Với cải tiến cuối cùng này mọi yêu cầu của bài toán đã được giải quyết triệt để.

Page 19: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 19

BÀI TẬP

1.8 Kỹ thuật lính canh

1. Cho mảng một chiều a chứa n số nguyên.

Viết hàm kiểm tra xem x có thuộc mảng a hay không ? nếu có thì trả về giá trị i nhỏ nhất

sao chi a[i] bằng x, nếu không có thì trả về giá trị -1.

2. Tìm số nguyên tố nhỏ nhất lớn hơn mọi giá trị có trong mảng.

1.9 Kỹ thuật đặt cờ hiệu

3. Cho mảng một chiều a chứa n số nguyên.

a.Viết hàm kiểm tra mảng có tăng dần hay không ? Nếu có thì trả về giá trị 1 nếu không có

thì trả về giá trị 0.

b.Viết hàm kiểm tra trong mảng các số nguyên có tồn tại giá trị chẵn nhỏ hơn 2010 hay

không ? Nếu có thì trả về giá trị 1 nếu không có thì trả về giá trị 0.

c.Viết hàm kiểm tra các phần tử trong mảng có thành lập nên cấp số cộng không ? Nếu có

hãy chỉ ra công sai d.

d.Viết hàm tìm giá trị chẵn đầu tiên, nếu không có thì trả về giá trị -1.

1.10 Kỹ thuật đếm

4. Cho mảng một chiều a chứa n số nguyên.

a.Viết hàm đếm số lượng số chính phương, số lượng số hoàn hảo, số lượng số nguyên tố.

b.Viết hàm đếm số lượng các giá trị lớn nhất.

5. Viết hàm đếm số lượng các giá trị phân biệt.

1.11 Kỹ thuật thêm/xóa phần tử

6. Cho mảng một chiều a chứa n số nguyên.

a.Viết hàm chèn thêm một phần tử có giá trị x vào mảng tại vị trí k.

b.Viết hàm xóa phần tử tại vị trí k trong mảng.

c.Viết hàm xóa tất cả các phần tử có giá trị bằng x trong mảng.

d.Viết hàm xóa tất cả các số nguyên tố trong mảng.

7. Viết hàm chèn một phần tử x vào mảng a (giả sử là mảng a đã được sắp tăng dần) để tính tăng

dần của mảng a là không vi phạm sau khi chèn thêm x.

1.12 Kỹ thuật sắp xếp

8. Viết hàm tìm k giá trị khác nhau lớn nhất của mảng.

9. Viết hàm hãy đưa các số chẵn về đầu mảng, các số lẻ về cuối mảng, còn các số 0 ở giữa.

10. Viết hàm sắp xếp các các số trong mảng sao cho các số dương được sắp xếp tăng dần, các số

âm được sắp xếp giảm dần, còn các số 0 thì giữ nguyên vị trí.

11. Cho mảng 1 chiều chứa n số nguyên dương. Hãy tìm các cặp số nguyên tố cùng nhau.

12. Cho mảng một chiều a chứa các số nguyên dương. Hãy xuất các số nguyên tố trong mảng a

theo chiều tăng dần.

13. Cho mảng hai chiều (ma trận) m dòng, n cột các phần tử là các số nguyên. Hãy thực hiện các

công việc sau đây:

a. Tính tổng các phần tử nằm trên các biên của mảng.

b. Tính tổng các giá trị lẻ trên mỗi cột.

c. Liệt kê chỉ số dòng có chứa giá trị nguyên tố.

d. Kiểm tra xem mảng có tồn tại số hoàn thiện nào không ?

Page 20: Tong quan ki thuat lap trinh

Tổng quan Kỹ thuật lập trình Trang 20

e. Hoán vị hai cột k, l của mảng.

f. Dịch xuống xoay vòng các dòng.

g. Sắp xếp các dòng tăng dần theo tổng của từng dòng.

h*. Tìm giá trị xuất hiện nhiều lần nhất trong mảng.

14. Cho mảng hai chiều (ma trận) n dòng, n cột. Hãy thực hiện các công việc sau đây:

a. Tính tổng các phần tử nằm trên đường chéo phụ.

b. Kiểm tra ma trận có đối xứng qua đường chéo chính hay không ?

c. Kiểm tra xem đường chéo phụ có tăng dần từ trên xuống dưới hay không ?

d. Đếm số lượng giá trị chẵn trong ma trận tam giác trên.

e. Sắp xếp các phần tử trên đường chéo chính tăng dần.

f. Sắp xếp các phần tử trong ma trận tam giác trên tăng dần theo chiều từ trên xuống dưới và

từ trái qua phải.

g*. Ma trận vuông cấp n có 2n-1 đường chéo song song với đường chéo chính. - được đặt

tên là -(n-1) đến (n-1). Hãy tìm các đường chéo có tổng lớn nhất.

1.13 Tối ƣu hóa

15. Tính tổng !...

!3!2!11),(

32

n

xxxxxnS

n

16. Cho dãy n phần tử. hãy chuyển k phần tử đầu mảng về cuối mảng

17. Cho một bảng vuông gồm có n dòng, n cột (n<20). Trên mỗi ô của bảng chứa một phân số

(các phân số có cùng giá trị phải được xem là như nhau, chẳng hạn phân số 1/2 và 3/6 được xem

là như nhau). Hãy đếm tần số xuất hiện của các phân số trong bảng.

18. Cho hai chuỗi S1 và S2. Hãy tìm chuỗi con chung dài nhất.

19. Để quản lý nhân sự ở một tỉnh nọ có khoảng 1 triệu người, hãy sắp xếp tuổi của dân cư ở đây

theo thứ tự từ nhỏ đến lớn. Biết rằng tuổi ở đây chỉ nằm trong khoảng từ 1 đến 132. Tuổi của dân

cư được cho vào từ file AGE.INP viết liên tiếp nhau, cách nhau ít nhất một dấu cách hoặc một

dấu xuống dòng. Kết quả đưa ra file AGE.OUT cấu trúc như file vào nhưng đã được sắp xếp và

hãy cho biết tần suất xuất hiện của các độ tuổi.

TÀI LIỆU THAM KHẢO

1. Trần Tuấn Minh, Thiết kế và đánh giá thuật toán, Đà Lạt, 2002.

2. Nguyễn Đức Nghĩa, Nguyễn Tô Thành, Toán rời rạc, NXB Đại học Quốc gia Hà Nội, 2009

3. Học viện Công nghệ Bưu chính Viễn thông, Cấu trúc dữ liệu và giải thuật, Hà Nội, 2007

4. Đinh Mạnh Tường, Cấu trúc dữ liệu và giải thuật, NXB Đại Học Quốc Gia Hà Nội, 2008.

5. Huỳnh Minh Trí – Phan Tấn Quốc, Trường Đại học Sài Gòn, Giáo trình bài tập kỹ thuật lập

trình, 2009.

6. Học viện công nghệ bưu chính viễn thông, Giáo trình Kỹ thuật lập trình,, NXB Bưu điện,

2002.

7. Kỹ thuật lập trình C, Phạm Văn Ất, NXB Giáo dục, 2003.

8. www.google.com