UBUNTU DE CLAIR

Petit à petit, l'oiseau fait son nid

Tối ưu hóa mã nguồn C/ C++ – Optimize source code C/ C++

Khi bắt đầu viết một chương trình bằng một ngôn ngữ nào đó, thông thường bạn chỉ chúsource trọng vào việc hoàn tất các tính năng của chương trình yêu cầu, việc tối ưu nó được đặt ra sau khi bạn hoàn thành chương trình đó. Khi đó việc tối ưu chương trình sẽ gặp những khó khăn nhất định. Do đó trong khi viết code bạn nên tạo cho mình một thói quen sao cho mã nguồn được tối ưu trong những trường hợp cơ bản. Bài viết này sẽ trình bày về tối ưu hóa mã nguồn trong C/C++, tại sao phải tối ưu hóa mã nguồn? tối ưu hóa mã nguồn như thế nào? Nó đem lại lợi ích gì cho người lập trình?… Những câu hỏi đó sẽ được trả lời trong bài viết này.

1. Tại sao phải tối ưu mã lệnh?
– Sự ra đời của các trình biên dịch hiện đại đã giúp lập trình viên cải thiện đáng kể thời
gian và công sức phát triển phần mềm. Một vấn đề đáng quan tâm là xu hướng phát
triển phần mềm theo hướng trực quan nhanh và tiện dụng dần làm mặt bằng kĩ năng

viết mã lệnh của các lập trình viên giảm rõ rệt vì họ trông cậy hoàn toàn vào sự hỗ trợ
của trình biên dịch. Khi phát triển một hệ thống phần mềm có tần suất xử lý cao, ví dụ
các sản phẩm có chức năng điều phối hoạt động dây chuyền sản xuất trong nhà máy,

thì bên cạnh sự hỗ trợ của một trình biên dịch mạnh còn cần đến kĩ năng tối ưu mã
lệnh của lập trình viên. Kĩ năng tốt sẽ biến công việc lập trình khô khan, với các đoạn
code tưởng chừng lạnh lùng trở nên sinh động. Một đoạn mã lệnh tốt sẽ tận dụng tối

đa ưu điểm của ngôn ngữ và khả năng xử lý của hệ thống, từ đó giúp nâng cao đáng
kể hiệu suất hoạt động của hệ thống.why

– Để chương trình hoạt động tối ưu, điều đầu tiên là tận dụng những hỗ trợ sẵn có của
trình biên dịch thông qua các chỉ thị (directive) giúp tối ưu mã lệnh, tốc độ và kích
thước chương trình. Hầu hết các trình biên dịch phổ biến hiện nay đều hỗ trợ tốt việc

tối ưu mã khi biên dịch. Tuy nhiên, để đạt được hiệu quả tốt nhất, lập trình viên cần
tập cho mình thói quen tối ưu mã lệnh ngay từ khi bắt tay viết những chương trình đầu
tay. Bài viết này trình bày một số gợi ý rất cơ bản và kinh nghiệm thực tế tối ưu trong

lập trình bằng ngôn ngữ C/C++.

2. Tinh giản các biểu thức toán học.
– Các biểu thức toán học phức tạp khi được biên dịch có thể sinh ra nhiều mã dư thừa math

làm tăng kích thước và chậm tốc độ thực hiện của chương trình. Do đó khi viết các
biểu thức phức tạp lập trình viên cần nhớ một số đặc điểm cơ bản sau để giúp tinh
giản biểu thức:
– CPU xử lý các phép tính cộng và trừ nhanh hơn các phép tính chia và nhân.

Ví dụ:

+ Biểu thức Total = (A*B + A*C + A*D) cần 2 phép cộng và 3 phép nhân. Ta có thể
nhóm các phép cộng và viết thành Total = A*(B+C+D), tốc độ tính nhanh hơn vì
giảm đi một phép tính nhân.

+ Biểu thức Total = (B/A + C/A) cần 2 phép chia có thể viết thành Total = (B+C)/A,
giúp giảm đi một phép chia.

– CPU xử lý tính toán với các số nguyên (integer) chậm hơn với số thực (float,
double), và tốc độ xử lý float nhanh hơn double.

– Trong một số trường hợp nhân hoặc chia số nguyên, sử dụng toán tử dời bit (bit
shifting) sẽ nhanh hơn toán tử nhân chia.

Ví dụ:
Biểu thức (A *= 128) có thể tận dụng toán tử dời bit sang trái thành (A <<= 7).

Một số trình biên dịch có khả năng tối ưu mã khi biên dịch như Visual C++ 6 hoặc
.Net 2003, biểu thức (A *= 128) và (A <<= 7) đều được biên dịch thành:

mov eax, A

shl eax, 7 (toán tử shl được dùng thay vì mul/imul)

mov A, eax

Ta có thể tối ưu bằng cách sử dụng mã assembly trực tiếp trong mã C/C++ như sau
(xem thêm thủ thuật tận dụng thế mạnh của C/C++ bên dưới):

__asm shl A, 7;

3. Tối ưu việc sử dụng biến tạm
– Đối với một số biểu thức tính toán số học phức tạp, trình biên dịch thường tạo các
biến tạm trong bộ nhớ để chứa kết quả tính toán và cuối cùng mới gán giá trị này cho
biến kết quả. Việc sử dụng biến tạm làm giảm tốc độ tính toán do phải cấp phát vùngvarialbe

nhớ, tính toán và thực hiện việc gán kết quả cuối cùng. Để tránh việc sử dụng biến
tạm, ta có thể thực hiện việc tách các biểu thức phức tạp thành các biểu thức nhỏ hơn,
hoặc sử dụng các mẹo cho việc tính toán.

– Xem một ví dụ cộng các số nguyên sau:

A = B + C;
– Về cơ bản, khi thực hiện biểu thức này trình biên dịch tạo một biến tạm rồi thực hiện
cộng 2 giá trị B, C vào biến tạm này, cuối cùng sẽ gán kết quả cho A.

– Ta có thể viết lại biểu thức trên như sau để tránh sử dụng biến tạm làm chậm việc tính
toán:

 A = B; A += C; 

Trong lập trình hướng đối tượng, theo thói quen đôi khi lập trình viên sử dụng các
biến tạm không cần thiết như trong ví dụ sau:

 int MyFunc(const MyClass &A) { MyClass B; B = A; return B.value; } 

– Trong hàm trên, khi biến tạm B kiểu MyClass được khởi tạo thì constructor mặc định
sẽ được thực hiện. Sau đó B được gán giá trị của biến A thông qua việc sử dụng toán
tử =, khi đó copy constructor sẽ được gọi. Tuy nhiên với yêu cầu của bài toán thì việc

này không cần thiết, ta có thể viết lại như sau:

 int MyFunc(const MyClass &A) { return A.value; } 

– Dưới đây là một ví dụ khác cho bài toán hoán vị giá trị 2 số nguyên A và B. Thông
thường, yêu cầu này sẽ được viết như sau:

 int A = 7, B = 8; int nTemp; //biến tạm nTemp = A; A = B; B = nTemp; 

– Tuy nhiên, bạn có thể sử dụng mẹo sau để tránh sử dụng biến tạm và tăng tốc tính
toán:

+ Sử dụng toán tử XOR:

 A = A^B; B = A^B; A = A^B; 

+ Sử dụng phép cộng, trừ

 nX = nX + nY; nY = nX - nY; nX = nX - nY; 

– Bạn hãy chạy thử đoạn mã lệnh trên sẽ thấy điều bất ngờ thú vị khi bài toán hoán vị
được giải quyết hết sức đơn giản.

– Thủ thuật tránh sử dụng biến tạm cần áp dụng linh động tùy thuộc kiểu dữ liệu, đặc

biệt các loại dữ liệu phức tạp như kiểu structure, string… có cơ chế lưu trữ và xử lý
riêng. Đối với các trình biên dịch hiện đại, việc tối ưu theo cách này đôi khi không
cần thiết vì trình biên dịch đã hỗ trợ sẵn cơ chế tối ưu này khi biên dịch mã lệnh.

4. Tối ưu các biểu thức điều kiện và luận lý
– Biểu thức điều kiện là thành phần không thể thiếu ở hầu hết các chương trình máy
tính vì nó giúp lập trình viên biểu diễn và xử lý được các trạng thái của thế giới thực
dưới dạng các mã lệnh máy tính. Những điều kiện dư thừa có thể làm chậm việc tính

toán và gia tăng kích thước mã lệnh, thậm chí có những đoạn mã có xác suất xảy ra
rất thấp. Một trong những tiêu chí quan trọng của việc tối ưu các biểu thức điều kiện
là đưa các điều kiện có xác suất xảy ra cao nhất, tính toán nhanh nhất lên đầu biểu logic

thức.

– Đối với các biểu thức luận lý, ta có thể linh động chuyển các biểu thức điều kiện đơn
giản và xác suất xảy ra cao hơn lên trước, các điều kiện kiểm tra phức tạp ra sau.

– Ví dụ: Biểu thức logic ((A || B ) && C ) có thể viết thành (C && ( A || B )) vì điều

kiện C chỉ cần một phép kiểm tra TRUE, trong khi điều kiện (A || B) cần đến 2 phép
kiểm tra TRUE và một phép OR (||). Như vậy trong trường hợp C có giá trị FALSE,
biểu thức logic này sẽ có kết quả FALSE và không cần kiểm tra thêm giá trị (A || B).

– Đối với các biểu thức kiểm tra điều kiện phức tạp, ta có thể viết đảo ngược bằng cách
kiểm tra các giá trị cho kết quả không thoả trước, giúp tăng tốc độ kiểm tra.

Ví dụ: Kiểm tra một giá trị thuộc một miền giá trị cho trước.

 if (p <= max && p >= min && q <= max && q >= min) { //thực hiện khi thoả miền giá trị } else //không thoả { //thực hiện khi không thoả miền giá trị } 

Có thể viết thành:

 if (p > max || p < min || q > max || q < min) { } else { } 

5. Tránh các tính toán lặp lại trong biểu thức điều kiện

Ví dụ:

 if ((mydata->MyFunc() ) < min) { // ... } else if ((mydata->MyFunc() ) > max) { // ... } 

– Ta có thể chuyển hàm MyFunc ra ngoài biểu thức điều kiệu như sau:

 int temp_value = mydata->MyFunc(); if (temp_value < min) { // ... } else if (temp_value > max) { // ... } 

– Đối với biểu thức điều kiện dạng switch…case: nếu các giá trị cho case liên tục nhau,
trình biên dịch sẽ tạo ra bảng ánh xạ (còn gọi là jump table) giúp việc truy xuất đến
từng điều kiện nhanh hơn và giảm kích thước mã lệnh. Tuy nhiên khi các giá trị

không liên tục, trình biên dịch sẽ tạo một chuỗi các phép toán so sánh, từ đó gây chậm
việc xử lý:

Ví dụ sau cho kết quả truy xuất tối ưu khi sử dụng switch…case:

 int my_value; switch (my_value) { case A: //... break; case B: //... break; case C: //... break; case D: //... default: //... } 

– Trong trường hợp các giá trị dùng cho case không liên tục, ta có thể viết thành các
biểu thức if…elseif…else như sau:

 switch (my_value) { case A: //... break; case F: //... break; case T: } 

– Có thể viết thành:

 if (my_value == A) { // xử lý cho trường hợp A } else if (my_value == F) { // xử lý cho trường hợp F } else { // các trường hợp khác } 

6. Tối ưu vòng lặp

– Vòng lặp cũng là một thành phần cơ bản phản ánh khả năng tính toánloop không mệt mỏi

của máy tính. Tuy nhiên, việc sử dụng máy móc vòng lặp là một trong những nguyên
nhân làm giảm tốc độ thực hiện của chương trình. Một số thủ thuật sau sẽ giúp lập
trình viên tăng tốc vòng lặp của mình:

– Đối với các vòng lặp có số lần lặp nhỏ, ta có thể viết lại các biểu thức tính toán mà

không cần dùng vòng lặp. Nhờ vậy tiết kiệm được khoảng thời gian quản lý và tăng
biến đếm trong vòng lặp.

– Ví dụ cho vòng lặp sau:

 for( int i = 0; i < 4; i++ ) { array[i] =MyFunc(i); } 

có thể viết lại thành:

 array[0] = MyFunc(0); array[1] = MyFunc(1); array[2] = MyFunc(2); array[3] = MyFunc(3); 

– Đối với các vòng lặp phức tạp có số lần lặp lớn, cần hạn chế việc cấp phát các biến
nội bộ và các phép tính lặp đi lặp lại bên trong vòng lặp mà không liên quan đến biến
đếm lặp.

Ví dụ cho vòng lặp sau:

 int students_number = 10000; for( int i = 0; i < students_number; i++ ) { //hàm MyFunc mất nhiều thời gian thực hiện double sample_value = MyFunc(students_number); CalcStudentFunc(i, sample_value); } 

– Trong ví dụ trên, biến sample_value được tính ở mỗi vòng lặp một cách không cần
thiết vì hàm MyFunc có thể tốn rất nhiều thời gian, ta có thể dời đoạn mã tính toán
này ra ngoài vòng lặp như sau:

 int students_number = 10000; double sample_value = MyFunc(students_number); for( int i = 0; i < students_number; i++ ) { CalcStudentFunc(i, sample_value); } 

– Đối với vòng lặp từ 0 đến n phần tử như sau:

 for( int i = 0; i < max_number; i++ ) 

Nên thực hiện việc lặp từ giá trị max_number trở về 0 như sau:

 for( int i = max_number - 1; i >=0 ; -- i ) 

– Vì khi biên dịch thành mã máy, các phép so sánh với 0 (zero) sẽ được thực hiện nhanh
hơn với các số nguyên khác. Do đó phép so sánh ở mỗi vòng lặp là ( i >=0 ) sẽ nhanh
hơn phép so sánh ( i < max_number).

– Trong vòng lặp lớn, các toán tử prefix dạng (–i hoặc ++i) sẽ thực hiện nhanh hơn
toán tử postfix (i– hoặc i++). Nguyên nhân là do toán tử prefix tăng giá trị của biến trước sau đó trả kết quả về cho biểu thức, trong khi toán tử postfix phải lưu giá trị cũ

của biến vào một biến tạm, tăng giá trị của biến và trả về giá trị của biến tạm.

7. Tối ưu việc sử dụng bộ nhớ và con trỏ

– Con trỏ (pointer) có thể được gọi là một trong những “niềm tự hào” của C/C++, tuy
nhiên thực tế nó cũng là nguyên nhân làm đau đầu cho các lập trình viên, vì hầu hết
các trường hợp sụp đổ hệ thống, hết bộ nhớ, vi phạm vùng nhớ… đều xuất phát từ việc

sử dụng con trỏ không hợp lý. pointer

– Hạn chế pointer dereference: pointer dereference là thao tác gán địa chỉ vùng nhớ dữ
liệu cho một con trỏ. Các thao tác dereference tốn nhiều thời gian và có thể gây hậu
quả nghiêm trọng nếu vùng nhớ đích chưa được cấp phát.

Ví dụ với đoạn mã sau:

 for( int i = 0; i < max_number; i++ ) { SchoolData->ClassData->StudentData->Array[i] = my_value; } 

– Bằng cách di chuyển các pointer dereference nhiều cấp ra ngoài vòng lặp, đoạn mã
trên có thể viết lại như sau:

 unsigned long *Temp_Array = SchoolData->ClassData->StudentData->Array; for( int i = 0; i < max_number; i++ ) { Temp_Array[i] = my_value; } 

– Sử dụng tham chiếu (reference) cho đối tượng dữ liệu phức tạp trong các tham số
hàm. Việc sử dụng tham chiếu khi truyền nhận dữ liệu ở các hàm có thể giúp tăng tốc
đáng kể đối với các cấu trúc dữ liệu phức tạp. Trong lập trình hướng đối tượng, khi

một đối tượng truyền vào tham số dạng giá trị thì toàn bộ nội dung của đối tượng đó
sẽ được sao chép bằng copy constructor thành một bản khác khi truyền vào hàm. Nếu
truyền dạng tham chiếu thì loại trừ được việc sao chép này. Một điểm cần lưu ý khi sử

dụng tham chiếu là giá trị của đối tượng có thể được thay đổi bên trong hàm gọi, do
đó lập trình viên cần sử dụng thêm từ khóa const khi không muốn nội dung đối tượng
bị thay đổi.

Ví dụ: Khi truyền đối tượng dạng giá trị vào hàm để sử dụng, copy constructor sẽ

được gọi.

 void MyFunc(MyClass A) //copy constructor của A sẽ được gọi { int value = A.value; } 

Khi dùng dạng tham chiếu, đoạn mã trên có thể viết thành:

 void MyFunc(const MyClass &A) //không gọi copy constructor { int value = A.value; } 

– Tránh phân mảnh vùng nhớ: Tương tự như việc truy xuất dữ liệu trên đĩa, hiệu năng
truy xuất các dữ liệu trên vùng nhớ động sẽ giảm đi khi bộ nhớ bị phân mảnh. Một số
gợi ý sau sẽ giúp giảm việc phân mảnh bộ nhớ.

+ Tận dụng bộ nhớ tĩnh. Ví dụ: như tốc độ truy xuất vào một mảng tĩnh có tốc độ
nhanh hơn truy xuất vào một danh sách liên kết động.

+ Khi cần sử dụng bộ nhớ động, tránh cấp phát hoặc giải phóng những vùng nhớ kích

thước nhỏ. Ví dụ như ta có thể tận dụng xin cấp phát một mảng các đối tượng thay vì
từng đối tượng riêng lẻ.

+ Sử dụng STL container cho các đối tượng hoặc các cơ chế sử dụng bộ nhớ riêng có
khả năng tối ưu việc cấp phát bộ nhớ. STL cung cấp rất nhiều thuật toán và loại dữ

liệu cơ bản giúp tận dụng tối đa hiệu năng của C++. Các bạn có thể tìm đọc các sách
về STL sẽ biết thêm nhiều điều thú vị.

– Sau khi cấp phát một mảng các đối tượng, tránh nhầm lẫn khi sử dụng toán tử
delete[] và delete: với C++, toán tử delete[] sẽ chỉ định trình biên dịch xóa một chuỗi

các vùng nhớ, trong khi delete chỉ xóa vùng nhớ mà con trỏ chỉ đến, do đó có thể gây
hiện tượng “rác” và phân mảnh bộ nhớ.

Ví dụ:

 int *myarray = new int[50]; delete []myarray; 

Với delete[], trình biên dịch sẽ phát sinh mã như sau:

 mov ecx, dword ptr [myarray] mov dword ptr [ebp-6Ch], ecx mov edx, dword ptr [ebp-6Ch] push edx call operator delete[] (495F10h) //gọi toán tử delete[] add esp,4 

Trong khi với đoạn lệnh:

 int *myarray = new int[50]; delete myarray; 

Trình biên dịch sẽ phát sinh mã như sau:

 mov ecx, dword ptr [myarray] mov dword ptr [ebp-6Ch], ecx mov edx, dword ptr [ebp-6Ch] push edx call operator delete (495F10h) //gọi toán tử delete add esp,4 

8. Sử dụng hợp lý cơ chế bẫy lỗi try…catch

– Việc sử dụng không hợp lý các bẫy lỗi có thể là sai lầm tai hại vì trình biên dịch sẽ try

thêm các mã lệnh kiểm tra ở các đoạn mã được cài đặt try…catch, điều này làm tăng
kích thước và giảm tốc độ xử lý của chương trình, đồng thời gây khó khăn trong việc
sửa chữa các lỗi logic. Thống kê cho thấy các đoạn mã có sử dụng bẫy lỗi thì hiệu

xuất thực hiện giảm từ 5%-10% so với đoạn mã thông thường được viết cẩn thận. Để
hạn chế điều này, lập trình viên chỉ nên đặt bẫy lỗi ở những đoạn mã có nguy cơ lỗi
cao và khả năng dự báo trước thấp.

9. Tận dụng đặc tính xử lý của CPU

– Để đảm báo tốc độ truy xuất tối ưu, các bộ vi xử lý (CPU) 32-bit hiện nay yêu cầu dữcpu

liệu sắp xếp và tính toán trên bộ nhớ theo từng offset 4-byte. Yêu cầu này gọi là
memory alignment. Do vậy khi biên dịch một đối tượng dữ liệu có kích thước dưới 4-
byte, các trình biên dịch sẽ bổ sung thêm các byte trống để đảm bảo các dữ liệu được

sắp xếp theo đúng quy luật. Việc bổ sung này có thể làm tăng đáng kể kích thước dữ
liệu, đặc biệt đối với các cấu trúc dữ liệu như structure, class…

Xem ví dụ sau:

 class Test { bool a; int c; int d; bool b; };

– Theo nguyên tắc alignment 4-byte (hai biến “c” và “d” có kích thước 4 byte), các biến
“a” và “b” chỉ chiếm 1 byte và sau các biến này là biến int chiếm 4 byte, do đó trình
biên dịch sẽ bổ sung 3 byte cho mỗi biến này. Kết quả tính kích thước của lớp Test

bằng hàm sizeof(Test) sẽ là 16 byte.

– Ta có thể sắp xếp lại các biến thành viên của lớp Test như sau theo chiều giảm dần
kích thước:

 class Test { int c; int d; bool a; bool b; }; 

– Khi đó, hai biến “a” và “b” chiếm 2 byte, trình biên dịch chỉ cần bổ sung thêm 2 byte
sau biến “b” để đảm bảo tính sắp xếp 4-byte. Kết quả tính kích thước sau khi sắp xếp
lại class Test sẽ là 12 byte.

11. Tận dụng một số ưu điểm khác của C++

– Khi thiết kế các lớp (class) hướng đối tượng, ta có thể sử dụng các phương thức
“inline” để thực hiện các xử lý đơn giản và cần tốc độ nhanh. Theo thống kê, các
phương thức inline thực hiện nhanh hơn khoảng 5-10 lần so với phương thức được cài

đặt thông thường. c

– Sử dụng ngôn ngữ cấp thấp assembly: một trong những ưu điểm của ngôn ngữ
C/C++ là khả năng cho phép lập trình viên chèn các mã lệnh hợp ngữ vào mã nguồn
C/C++ thông qua từ khóa __asm { … }. Lợi thế này giúp tăng tốc đáng kể khi biên

dịch và khi chạy chương trình.

Ví dụ:

 int a, b, c, d, e; e = a*b + a*c; 

Trình biên dịch phát sinh mã hợp ngữ như sau:

 mov eax, dword ptr [a] imul eax, dword ptr [b] mov ecx, dword ptr [a] imul ecx, dword ptr [c] add eax, ecx mov dword ptr [e], eax 

Tuy nhiên, ta có thể viết rút gọn giảm được 1 phép imul (nhân), 1 phép mov (di
chuyển, sao chép):

 __asm { mov eax, b; add eax, c; imul eax, a; mov e, eax; }; 

– Ngôn ngữ C++ cho phép sử dụng từ khóa “register” khi khai báo biến để lưu trữ dữ
liệu của biến trong thanh ghi, giúp tăng tốc độ tính toán vì truy xuất dữ liệu trong
thanh ghi luôn nhanh hơn truy xuất trong bộ nhớ.

Ví dụ:

 for (register int i; i <max_number; ++i ) { // xử lý trong vòng lặp } 

– Ngôn ngữ C/C++ hỗ trợ các collection rất mạnh về tốc độ truy xuất như các bảng
map, hash_map,… nên tận dụng việc sử dụng các kiểu dữ liệu này thay cho các danh
sách liên kết bộ nhớ động (linked list).

12. Kết luận

– Một chương trình được đánh giá tốt khi tất cả các bộ phận tham gia vào hoạt động của
chương trình đạt hiệu suất cao nhất theo yêu cầu của người sử dụng. Một dòng lệnh optimize

đơn giản tưởng chừng sẽ hoạt động trong tích tắc có thể làm hệ thống trở nên chậm
chạp khi được gọi hàng ngàn, hàng triệu lần trong khoảng thời gian ngắn. Do vậy,
trong suốt qui trình hình thành sản phẩm phần mềm, giai đoạn cài đặt mã lệnh chiếm

vai trò hết sức quan trọng và cần kĩ năng tối ưu hóa cao nhất. Để đạt được điều đó,
không cách nào khác hơn là lập trình viên cần tự rèn luyện thật nhiều để thông thạo
ngôn ngữ mình chọn lựa, trình biên dịch mình sử dụng. Khi đó lập trình không còn là

việc tạo những đoạn mã khô khan, mà là một nghệ thuật.

(Theo Global CyberSoft Vietnam)

Cách sinh số ngẫu nhiên trong C++ – Random number library functions

Việc phát sinh số ngẫu nhiên là cần thiết khi chúng ta cần test chương trình. Việc tạo số ngẫu nhiên trong C++ cũng khá đơn giản. Thư viện chuẩn của C++ cung cấp môt hàm gọi là rand (#include <cstdlib>). Bài viết này sẽ giúp chúng ta hiểu hơn về chức năng tạo số ngẫu nhiên trong C++.

1. Tạo một số ngẫu nhiên:

Hàm rand() trả về một số nguyên nằm trong dãy [0, RAND_MAX], với RAND_MAX là một hằng số được định nghĩa trước. Hãy xem ví dụ bên dưới:

 // First example
#include <iostream>
#include <cstdlib>
using namespace std; int main() { int r0 = rand(); int r1 = rand(); int r2 = rand(); int r3 = rand(); int r4 = rand(); cout<< "r0 = "<< r0 << endl; cout<< "r1 = "<< r1 << endl; cout<< "r2 = "<< r2 << endl; cout<< "r3 = "<< r3 << endl; cout<< "r4 = "<< r4 << endl; system("pause"); return 0; }

– Output là:

image

– Nhưng sau khi chạy nhiều lần chương trình trên, bạn vẫn nhận được một output như lúc đầu. Vấn đề này sẽ được giải quyết trong mục 2.

2. Tạo một số ngẫu nhiên thay đổi theo thời gian

– Để tạo một số ngẫu nhiêu sau thay đổi khác nhau trong những lần chạy, bạn sử dụng hàm time (có trong #include<ctime>). Theo như thư viện MSDN (Microsoft Developers Network) nói rằng: time trả về số của giây được trôi qua được tính từ nữa đêm (00:00:00), ngày 1, tháng 1, năm 1970 theo UTC . (“The time function returns the number of seconds elapsed since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC), according to the system clock”).

– Các số ngẫu nhiên giả được tạo ra bắt đầu từ thời điểm bạn thiết lập sử dụng hàm srand. Dòng code dưới đây thiết lập điểm bắt đầu của thời gian hiện hành.

 srand(time(0));

– Giá trị được trả về từ time là qua srand. Lưu ý rằng số ngẫu nhiên được tạo ra trước lời gọirand.

Ví dụ với chương trình đầu tiên: chúng ta thêm srand(time(0)) trước lời gọi rand().

 // Second example
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std; int main() { srand(time(0)); int r0 = rand(); int r1 = rand(); int r2 = rand(); int r3 = rand(); int r4 = rand(); cout<< "r0 = "<< r0 << endl; cout<< "r1 = "<< r1 << endl; cout<< "r2 = "<< r2 << endl; cout<< "r3 = "<< r3 << endl; cout<< "r4 = "<< r4 << endl; system("pause"); return 0; }

– Output lần đầu:

image

– Output lần 2:

image– Output lần 3:

image

3. Tạo số ngẫu nhiên nằm trong dãy xác định (Specifying the range)

– Thông thường, một số ngẫu nhiên nằm trong dãy [0, RAND_MAX] không được mô tả. Đúng hơn là chúng ta không thích một dãy được xác định. Một số ngẫu nhiên trong dãy [0, n – 1]có thể được xác định bằng cách sử dụng toán tử % (modulus). Ví dụ như sinh ra một số ngẫu nhiên trong dãy [0, 21], thì bạn có thể viết như sau:

int num = rand();
int val = num % 22;

– Hoặc có thể viết một cách mạch lạc hơn là:

 int num = rand() % 22;

– Khi chúng ta cần một dãy không bắt đầu từ 0. Ví dụ như sinh một số ngẫu nhiên nằm trong dãy [22, 100] thì phải làm sao? Chúng ta có thể thực hiện một cách khá đơn giản theo công thức

Để tạo một số ngẫu nhiên trong dãy [a, b] ta sử dụng công thức:

a + rand() % (b – a + 1)

Như muốn sinh một số ngẫu nhiên từ dãy [22, 100] ta viết như sau:

int num = 22 + rand() % (100 – 22 + 1);

hay đúng hơn là:

 int num = 22 + rand() % (79);

– Do phần tạo số ngẫu nhiên trong dãy [a, b] một số bạn comment vẫn chưa làm được hoặc kết quả cho ra chưa như ý muốn. Cho nên sau đây tôi xin bổ sung một đoạn chương trình minh họa cho việc sinh số ngẫu nhiêu trong đoạn [22, 100].

 #include <iostream> #include <cstdlib> #include <ctime> using namespace std; int main() { srand(time(0)); int number[200]; for(int i = 1; i <= 200; i++) { number[i] = 1 + rand()%(100-22+1); /* hoac co the viet: number[i] = 1 + rand()%(79); */ } for(int i = 1; i <= 200; i++) { cout<<number[i]<<" "; if(i%20 == 0) cout<<"\n\n"; } cout<<"\n"; system("pause"); return 0; }

kết quả khi chạy chương trình lần 1:

SNAGHTML2f4318

kết quả lần chạy thứ 2:

SNAGHTML31db70

Sở dĩ chương trình của nhiều bạn không thể cho ra kết quả có số 100 (như bạnluphucnguyen đã comment dưới bài viết này) là do chương trình của chưa sử dụng hàm srand(time(0)); để sinh ra số ngẫu nhiên trong mỗi lần chạy chương trình, hoặc có thể tập ngẫu nhiên bạn muốn sinh ra chưa đủ lớn nên xác suất có mặt các số mà bạn mong muốn nhỏ hơn (trong ví dụ trên mình sinh ra tập 200 số ngẫu nhiên trong đoạn [22, 100] nên xác suất có mặt của số 100 sẽ cao hơn Island with a palm tree).

** Lưu ý: Các ví dụ đề đã được chạy thử trên môi trường DevC++.

(Tham khảo Frank Luna,C++ Programming for Games – Module I)

Nhắc lịch làm việc qua tin nhắn SMS (free) với Google Calendar

Rất nhiều người trong chúng ta đã biết đến ứng dụng lịch củaGoogle (Google Calendar). Tuy nhiên ít ai biết đến tính năng nhắc sự kiện thông qua tin nhắn SMS miễn phí của Google Calendar. Tính năng hữu ít này sẽ giúp bạn tạo sự kiện và gửi tin nhắn SMS tới điện thoại của bạn trước thời điểm diễn ra sự kiện theo một khoảng thời gian mà bạn cài đặt. Bài viết này sẽ hướng dẫn từng bước sử dụng tính năng hữu ích & miễn phí nói trên.

1. Vào trang Google Calendar

– Nếu bạn đã có tài khoảng Gmail thì đăng nhập theo tài khoản đó.

– Nếu chưa có tài khoản Gmail thì tiến hành đăng ký một tài khoảng mới.

Giao diện Google Calendar (Google Lịch) sẽ như hình dưới đây:

image

2. Cài đặt số điện thoại di động

– Từ giao điện chính của Google Calendar bạn click vào biểu tượng bánh răng rồi chọn mục “Cài đặt”.

image

– Trên trang tiếp theo hiện ra chọn mục “Cài đặt điện thoại di động”.

image

– Tiếp theo trong ô “Số điện thoại” bạn nhập số điện thoại của mình vào. Sau đó nhấn nút “Gửi mã xác minh” ở bên dưới.

– Ngay lập tức điện thoại của bạn sẽ nhận được tin nhắn có mã xác minh được gửi đến từ Google. Bạn nhập mã vừa nhận được vào ô “Mã xác minh” và nhấn vào nút “Hoàn thành cài đặt”.

image

– Sau khi đã hoàn thành các bước trên, bạn nhấp vào nút “Lưu” phía bên tay trái để quay về giao diện chính của Google Lịch.

3. Tạo sự kiện và nhắc nhở bằng SMS

– Để tạo sự kiện bạn nhấp vào ngày muốn tạo sự kiện và chọn thời gian diễn ra sự kiện.

image

– Sau khi đã tạo sự kiện, bạn click vào sự kiện vừa tạo rồi chọn “Chỉnh sửa sự kiện

image

– Tiếp đó bạn nhập thêm vào các chi tiết sự kiện nếu muốn. Và đặc biệt trong mục “Lời nhắc” bạn nhớ chọn SMS và chỉnh thời gian gửi tin nhắn nhắc nhở trước khi sự kiện diễn ra (có thể nhắc nhở nhiều lần theo ý muốn). Theo mặc định thì Google sẽ gửi tin nhắn cho bạn trước khi sự kiện diễn ra 10 phút. Bạn có thể chỉnh lại theo ý thích.

image

– Sau khi đã chỉnh sửa xong, nhớ nhấp vào nút “Lưu” phía góc trên bên trái màn hình.

image

– Như vậy là bạn đã cài đặt xong tính năng nhắc lịch qua SMS từ Google Calendar.

4. Lưu ý

– Nếu bạn không nhận được mã xác minh sau khi nhập đúng số điện thoại của mình thì có thể tham khảo thêm tại Google Support.

– Thủ thuật này đã được tác giả thử nghiệm trước khi viết bài này.

– Nếu giao diện Google Calendar của bạn không giống như trong bài viết, bạn có thể click vào biểu tượng “Cài đặt” và chọn “Try the new look”.

– Bạn cũng có thể xem video hướng dẫn thủ thuật này trong video dưới đây:

– Video chuyên mục “Chuyển Động Số” được phát hành vào thứ 3 hàng tuần là một chuyên mục đáng được dân yêu thích công nghệ quan tâm. Các bạn có thể xem các video mới của chuyên mục này tại: http://tv.tuoitre.vn/danh-muc/65/khoa-hoc-cong-nghe.

11 mẹo tìm kiếm chuyên nghiệp trên Google, có thể bạn chưa biết!

by thanhcuong1990

image

Google là một phần gần như không thể thiếu trong cuộc sống công nghệ ngày nay. Nhưng việc biết và tận dụng được thế mạnh của Google thì không phải ai cũng biết hoặc khai thác được. Trong bài viết dưới đây sẽ giới thiệu với các bạn một số mẹo, thủ thuật để tăng hiệu suất tìm kiếm với Google.

1. Tìm kiếm chính xác với từ hoặc cụm từ

Một công thức cơ bản và phổ biến nhất mà gần như ai cũng biết, đó là sử dụng dấu ngoặc kép để tìm chính xác thông tin cần thiết. Ví dụ, nếu thực hiện yêu cầu tìm kiếm như dưới đây thì hệ thống sẽ trả về các trang web có chứa từ Hello và từ Word đằng sau:

“Hello World”

image

Tiếp theo là một cách tương tự như trên cũng hoạt động tốt với lệnh query để tìm kiếm chính xác từng từ, ví dụ: nếu bạn muốn tìm thông tin “mining,” thì Google đồng thời sẽ trả về những trang web có chứa từ “miners.” Trước kia thì chúng ta phải thêm dấu cộng ở đằng trước để tìm+mining, nhưng bây giờ chỉ cần đặt từ cần tìm kiếm trong dấu ngoặc kép mà thôi.

“mining”

2. Loại trừ từ khóa

Bên cạnh đó, khi bạn thêm dấu trừ vào phía trước 1 từ bất kỳ trong chuỗi thông tin tìm kiếm thì hệ thống sẽ loại bỏ tất cả các kết quả có chứa từ đó. Ví dụ: nếu muốn tìm kiếm các trang cung cấp thông tin về Linux distribution mà không có liên quan gì đến Ubuntu thì các bạn nhập từ khóa như sau:

linux distributions -ubuntu

image

3. Tìm kiếm trên một website nhất định

Toán tử site: cho phép người dùng tìm toàn bộ thông tin cần thiết trên 1 trang cố định nào đó. Chẳng hạn: nếu muốn tìm thông tin về C++ trên thanhcuong.wordpress.com thì các bạn hãy gõ từ khóa như sau:

site:thanhcuong.wordpress.com c++

image

Bên cạnh đó, chúng ta cũng có thể áp dụng cách này trên 1 domain bất kỳ. Chẳng hạn khi cần tìm các bài viết, đường dẫn có chất lượng thì nên chú tâm vào những tên miền có đuôi edu, như site:.edu để “đẩy” các kết quả từ các trang edu đó lên đầu.

4. Các từ ngữ có liên quan

Ngược lại với ý kiến ở phần 2, các bạn hãy sử dụng dấu ~ để tìm tất cả các từ ngữ có liên quan đến từ khóa nhập vào, bên cạnh chính bản thân keyword đó. Ví dụ: khi gõ ~geek thì chúng ta sẽ nhận được rất nhiều kết quả với nhiều từ có liên quan với từ geek:

image

Theo như trên thì Linux chính là từ “gần gũi” nhất với geek, tiếp theo là Greek, và sau đó là Nerd.

5. Ký tự đặc biệt

Dấu hoa thị được sử dụng trong trường hợp người dùng muốn tìm kiếm nhiều thông tin trùng khớp với bất kỳ từ khóa nào. Ví dụ: nếu muốn biết Google đã bỏ ra bao nhiêu tiền trong việc mua và bán trong thời gian qua, các bạn hãy gõ:

“google purchased * for * dollars”

image

6. Giới hạn trong khoảng thời gian

1 thủ thuật nữa rất hay mà lại ít người để ý tới, đó là tìm trong 1 khoảng thời gian có giới hạn. Ví dụ: nếu muốn tìm kiếm dữ liệu, thông tin về Ubuntu từ năm 2008 cho tới 2010 thì nhập nội dung từ khóa như sau:

ubuntu 2008..2010

image

7. Tìm theo định dạng file

Thông số filetype: cho phép người dùng dễ dàng tìm kiếm bất kỳ định dạng tài liệu nào. Chẳng hạn, nếu muốn tìm tất cả các file PDF có tên Microsoft ASP.NET 4 Step by Step thì gõ:

filetype:pdf Microsoft ASP.NET 4 Step by Step

image

8. Sử dụng OR

Tham số OR có tác dụng tìm kiếm bất kỳ kết quả nào có liên quan tới 1 trong 2 thuật ngữ trước và sau OR. Ví dụ như sau:

ubuntu OR linux

image

9. Định nghĩa theo từ

Rất đơn giản và dễ dàng, nếu muốn tìm nghĩa của 1 từ nào đó thì các bạn chỉ cần gõ từ khóa theo cú pháp chung như sau:

define:word

image

10. Tính toán

Thay vì việc phải dùng Google để tìm kiếm những tiện ích trực tuyến, ứng dụng hỗ trợ để thực hiện phép tính, thì chúng ta chỉ cần sử dụng những ký tự và công thức toán học thông thường ngay trên Google. Ví dụ như sau:

(4 + 2) * (6 / 3)

image

Hoặc vẽ đồ thị với:

(sqrt(cos(x))*cos(400*x)+sqrt(abs(x))-0.4)*(4-x*x) ^0.1

image

11. Chuyển đổi đơn vị

Tương tự như trên, đây là 1 trong nhiều chức năng thú vị nhất của Google tính cho tới thời điểm hiện tại, đó là chuyển đổi giữa nhiều đơn vị khác nhau. Chỉ cần nhập nội dung tương ứng dựa theo công thức: “X [units] in [units]” hoặc “X [units] to [units]” hoặc “X [units] –> [units]”

1 mile to km

image

Rất đơn giản và dễ dàng, chúc các bạn thành công!

3 Trang web hay để bắt đầu với ngôn ngữ lập trình C++ – 3 Websites To Get Started With Learnin g C++ Programming Language

Khi mới bắt đầu học lập trình, việc lựa chọn một tài liệu hay một cuốn sách tốt để đọc là điều hết sức quan trọng. Có nhiều bạn tỏ ra khá thích những cuốn sách kiểu “mì ăn liền” như “Học C++ trong 21 ngày”… Tuy nhiên, những tài liệu như vậy đa phần chỉ là cơ bản và hầu như không giải thích lý do vì sao phải viết chương trình như vậy. Đối với ngôn ngữ lập trình C++ cũng vậy, phải cần có thời gian để nắm bắt và sử dụng thành thạo. Bài viết này sẽ giới thiệu 3 trang web hay để bạn bắt đầu với C++ một cách đúng hướng.

Do cả 3 trang giới thiệu đều sử dụng bằng ngôn ngữ tiếng Anh, nên việc đọc hiểu được tiếng Anh là một điều hết sức cần thiết. (Nếu bạn chưa giỏi tiếng Anh cũng không sao, đa phần các trang đều sử dụng những từ tiếng anh khá dễ hiểu. Đây cũng là một cách bạn tiếp xúc với tiếng Anh nhiều hơn).

C++ là một ngôn ngữ hướng đối tượng mạnh mẽ, đa nền tảng, được hỗ trợ nhiều thư viện giúp lập trình viên viết code linh hoạt hoạt hơn. Do đó, hầu hết những trường đại học hay những trung tâm dạy lập trình đều trang bị cho sinh viên của họ những kiến thức từ cơ bản đến nâng cao của ngôn ngữ lập trình này. C++ cũng là một ngôn ngữ nền tảng giúp bạn nắm bắt các ngôn ngữ lập trình khác tốt hơn (đặc biệt là Java).

Sau đây là 3 website học lập trình C++, được cộng đồng lập trình viên đánh giá cao.

1. LearnCpp.com

image

LearnCpp.com là một nơi tuyệt vời dành cho những người mới bắt đầu cũng như đã có kinh nghiệm lập trình bắt đầu với ngôn ngữ C++. Tác giả của website này có khả năng diễn đạt rất tốt, tất cả các bài viết đều được hướng dẫn và giải thích tỉ mỉ (kể cả việc cài đặt một IDE tốt để viết code). Ngoài ra, LearnCpp.com cũng cập nhật những tính năng sửa đổi cũng như cải tiến của phiên bản C++ 2011.

image

Trong 3 trang web được giới thiệu trong bài viết này thì LearnCpp.com tỏ ra là vượt trội hơn cả. Các bài viết ở trang này được xây dựng theo từng đề mục rất hợp lý, mỗi bài viết cũng không quá dài, cuối mỗi bài viết tác giả còn cung cấp một số câu hỏi để bạn kiểm tra lại kiến thức của mình về bài học. LearnCpp.com thực sự là một website tuyệt vời để bắt đầu với C++.

2. CPlusPlus.com

image

CPlusPlus.com cũng là một trang web tuyệt vời để học C++. CPlusPlus cung cấp khá nhiều bài hướng dẫn cũng như giải thích các hàm, thư viện trong C++. Bạn có thể tìm được vô số khái niệm về lập trình cũng như những bài viết hay về các thủ thuật.

Trang web này không phải là một lựa chọn tối ưu cho những người mới bắt đầu học C++, nhưng tính chất tham khảo khảo các hàm, thư viện của nó hoạt động tốt hơn LearnCPP.

3. CProgramming.com

image

Trong trường hợp hai trang web được giới thiệu ở trên không phù hợp với bạn, thì bạn vẫn còn một sự lựa chọn khác là CProgramming.com. Đây là một trang web khá hay với những bài hướng dẫn chi tiết, có sự so sánh giữa hai ngôn ngữ C và C++. Trong mục “References” của website này chứa nhiều thông tin cũng như các thủ thuật hữu ích giúp bạn lập trình tốt hơn.

Website này cũng vừa xuất bản một ebook có tên gọi là “Jumping into C++”. Đây là một cuốn sách hướng dẫn bạn lập trình C++ theo kiểu từng bước (step-by-step). Nếu bạn quan tâm có thể xem thêm thông tin về ebook tại cprogramming.

Kết luận:

– Hy vọng rằng với những 3 website vừa được giới thiệu sẽ giúp bạn bắt đầu học lập trình với ngôn ngữ C++ một cách tốt hơn, và có định hướng hơn.

– Ngoài ra, bạn cũng có thể tham gia thảo luận, cũng như những kinh nghiệm từ các lập trình viên đi trước trên diễn đàn về lập trình C nổi tiếng của Việt Nam là CongDongCViet.com.

– Sẵn đây mình cũng giới thiệu một website học lập trình web, Javascript, Ruby, Python ngay trên trình duyệt của bạn là http://www.codecademy.com. Trang web này giúp học lập trình theo một hướng tiếp cận mới là ngay trên trình duyệt web của mình. Bạn có thể viết và chạy những dòng code ngay trên trình duyệt của mình. Rất hữu ích phải không? Winking smile

– Nếu bạn biết trang web học lập trình hay nào khác, có thể để lại comment bên dưới bài viết này để chia sẽ với mọi người.