Thư viện bài tháng 10 năm 2005

ISPs cần cách tính cước mới

Ngô Quang Hưng | 31 tháng 10, 2005 | Bản để in Bản để in

Tuần rồi tòa đã chấp nhận cho SBC mua AT&T, và Verizon mua MCI. Tiếng Anh có câu “life goes full circle” quả là đúng, SBC hồi xưa là một công ty con bị tách ra từ chàng khổng lồ AT&T. Thế giới các telecom carriers mấy năm gần đây xẻ đôi, hợp lại, X mua Y, A mua B, loạn hết cả lên.

Trong một cuộc phỏng vấn Edward Whitacre, CEO của SBC, có một đoạn rất thú vị:

  • Hỏi: how concerned are you about Internet upstarts like Google, MSN, Vonage, and others?
  • Trả lời: How do you think they’re going to get to customers? Through a broadband pipe. Cable companies have them. We have them. Now what they would like to do is use my pipes free, but I ain’t going to let them do that because we have spent this capital and we have to have a return on it. So there’s going to have to be some mechanism for these people who use these pipes to pay for the portion they’re using. Why should they be allowed to use my pipes?The Internet can’t be free in that sense, because we and the cable companies have made an investment and for a Google or Yahoo! or Vonage or anybody to expect to use these pipes [for] free is nuts!

Anh chàng CEO này phát biểu linh tinh quá đáng. Tuy nhiên, một trong những đề tài nghiên cứu “nóng” hiện nay của mạng máy tính là cách tính cước phí của các ISP. Khi vấn đề tính cước được giải quyết sơ bộ thì các phát biểu như trên sẽ biến dần đi.

Hầu như năm nào ở SIGCOMM, INFOCOM cũng có các bài nghiên cứu về Internet pricing scheme. Giáo sư Andrew Odlyzko viết khá nhiều về “kinh tế mạng“. Nhưng đến nay thì một giải pháp đơn giản như tính tiền theo tổng số bytes gửi lên mạng cũng chưa được hiện thực hóa. Các ISPs vừa muốn làm bánh, vừa muốn ăn bánh. Đó là vấn đề chính. Dĩ nhiên đây cũng là một trong những vấn đề căn cốt khi thiết kế lại Internet.

Chủ đề: CNTT các nước và VN & Mạng máy tính | Bình luận »

Lỗi tràn bộ đệm (6): căn bản về shellcode

Ngô Quang Hưng | 30 tháng 10, 2005 | Bản để in Bản để in

Ta có thể phối hợp lệnh jmp và lệnh call để truy cập đến địa chỉ của một vùng dữ liệu nào đó mà không biết trước địa chỉ vùng dữ liệu đó. Cấu trúc của phép phối hợp này như sau:

  jmp short string
code:
  pop eax     ; now eax has the address of the string 'nhan sinh ...'
  ...         ; having the address, we are ready to poke around
string:
  call code
  db 'Nhan sinh tu co thuy vo tu'

Cái mẹo này rất thông minh và đơn giản. Ta bỏ chuỗi cần dùng ‘nhân sinh tự cổ thùy vô tử’ vào ngay sau lệnh call. Cả call lẫn jmp short đều dùng địa chỉ tương tối để jump hoặc gọi hàm, nên không sợ vấn đề “địa chỉ cứng”. Khi call được gọi, hệ điều hành sẽ pushed địa chỉ lệnh nằm ngay sau call lên stack. Trong trường hợp này, stack sẽ chứa địa chỉ đến chuỗi ‘nhân sinh tự cổ thùy vô tử’. Ta chỉ việc pop nó vào thanh ghi nào đó tùy ý (eax chẳng hạn) và cứ thế mà dùng.

Với ý tưởng này, ta viết lại chương trình “Hello, World!” như sau:

global _start   ; default entry point for ELF linking

_start:
  jmp short string
code:
  pop ecx       ; ecx points to "Hello, World!"
  mov ebx, 1    ; output file descriptor for write
  mov edx, 14   ; length of output string
  mov eax, 4    ; 4 is the system call number of write()
  int 0x80      ; finally, invoke the system call
; prepare for exit(0)
  mov ebx, 0    ;
  mov eax, 1
  int 0x80
string:
  call code
  db 'Hello, World!', 0x0a

Bạn có thể tự kiểm tra là chương trình chạy như ý muốn. Ý tưởng đã sẵn, ta có thể viết đoạn chương trình assembly để dịch ra bytecode như sau:

; file name: hw.asm
USE32           ; tell nasm we're using a 32-bit system
  jmp short string
code:
  pop ecx       ; ecx has the address of "Hello, World!\n"
  mov ebx, 1    ; output file descriptor for write
  mov edx, 14   ; length of output string
  mov eax, 4    ; 4 is the system call number of write
  int 0x80      ; finally, invoke the system call
  mov ebx, 0    ; and exit cleanly
  mov eax, 1
  int 0x80
string:
  call code
  db 'Hello, World!', 0x0a

USE32 (hoặc BITS 32) để báo cho nasm dịch ra mã 32-bit. Giả sử file hw.asm chứa chương trình trên, ta dịch nó ra mã máy bằng
nasm hw.asm
Bây giờ ta đã có file hw chứa mã của đoạn bytecode cần tìm. Làm thế nào để đọc file này ở dạng hex? Có rất nhiều hex editors trên Internet, bạn có thể dùng thử hexedit. Chạy hexedit hw và ta có output như sau:

00000000   EB 1E 59 BB  01 00 00 00  BA 0E 00 00  00 B8 04 00  ..Y.............
00000010   00 00 CD 80  BB 00 00 00  00 B8 01 00  00 00 CD 80  ................
00000020   E8 DD FF FF  FF 48 65 6C  6C 6F 2C 20  57 6F 72 6C  .....Hello, Worl
00000030   64 21 0A                                            d!.

Đây chính là đoạn bytecode trong ví dụ 3. Nhưng chẳng lẽ cứ mỗi lần muốn thử bytecode mới thì lại phải dùng hexedit, copy từng byte từng byte một vào chuỗi bytecode[] trong ví dụ 3, sau đó dịch và chạy ví dụ 3? Mất thì giờ quá. Tôi viết một chương trình nho nhỏ đặt tên là bct (bytecode tester) với chức năng như sau: gọi “bct hw” và nó sẽ chạy code chứa trong hw cho ta, còn gọi “bct -p hw” thì nó sẽ in ra chuỗi hex trong hw để dùng được trong các chương trình C. Cụ thể, bct làm việc như sau:

[NQH]:~/BO$ cat hw.asm

USE32           ; tell nasm we’re using 32-bit system
  jmp short string
code:
  pop ecx       ; write(1, "Hello, World!", 13);
  mov ebx, 1    ; output file descriptor for write
  mov edx, 14   ; length of output string
  mov eax, 4    ; 4 is the system call number of write
  int 0×80      ; finally, invoke the system call
  mov ebx, 0    ;
  mov eax, 1
  int 0×80
string:
  call code
  db ‘Hello, World!’, 0×0a
[NQH]:~/BO$ nasm hw.asm
[NQH]:~/BO$ bct hw
———————-
Calling your code …
———————-
Hello, World!
[NQH]:~/BO$ bct -p hw
———————-
Printing your code …
———————-

char bytecode[] =
   "\xeb\x1e\x59\xbb\x01\x00\x00\x00\xba\x0e\x00\x00\x00\xb8\x04\x00"
   "\x00\x00\xcd\x80\xbb\x00\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80"
   "\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c"
   "\x64\x21\x0a"

Bài tập 1: Viết chương trình bct có chức năng mô tả như trên. Gợi ý: dùng ý tưởng của đoạn chương trình C sau đây

char bytecode[] =
    "\xeb\x1e\x59\xbb\x01\x00\x00\x00\xba\x0e\x00\x00\x00\xb8\x04\x00"
    "\x00\x00\xcd\x80\xbb\x00\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80"
    "\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c"
    "\x64\x21\x0a";

int main() {
  void (* func_ptr)(void);
  func_ptr = (void (*)(void)) bytecode;
  (* func_ptr)();
}

Lần tới ta sẽ viết một đoạn bytecode có chức năng gọi một shell (như /bin/sh hay /bin/bash). Các đoạn bytecode loại này được gọi là shellcode. Nếu chương trình nào đó chạy với suid bằng root mà bị lỗi tràn stack thì cái shellcode sẽ gọi một shell có đặc quyền root. Có thể nói shellcode là một trong những bytecode phổ biến nhất, vì có shell rồi thì làm gì chẳng được.

Chủ đề: Bảo mật và mật mã học | Bình luận »

Lỗi tràn bộ đệm (5): căn bản về shellcode

Ngô Quang Hưng | 29 tháng 10, 2005 | Bản để in Bản để in

5. Căn bản về shellcode

Để tận dụng khả năng thay đổi điều khiển của một chương trình bằng cách thay đổi địa chỉ trả về của một hàm, ta cần biết cách làm thế nào để bỏ một đoạn mã vào stack và bắt chương trình chạy đoạn mã đó. Hãy xét ví dụ sau đây:

/* ---------------------------------------------------------------------
 * Example 3: "Hello, World!" using bytecode
 * ---------------------------------------------------------------------
 */
char bytecode[] =
    "\xeb\x1e\x59\xbb\x01\x00\x00\x00\xba\x0e\x00\x00\x00\xb8\x04\x00"
    "\x00\x00\xcd\x80\xbb\x00\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80"
    "\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c"
    "\x64\x21\x0a";

int main() {
  int *ret;

  ret = (int *) &ret + 2;
  (*ret) = (int) bytecode;
}

Dịch và chạy cho kết quả sau:

[NQH]:~/BO$ make 3
gcc -g ex3.c -o ex3
[NQH]:~/BO$ ./ex3
Hello, World!
[NQH]:~/BO$

Tuyệt đẹp! Ở đây ta thay địa chỉ trả về của hàm main() cho nó trỏ vào đoạn mã bytecode - mã máy. Câu hỏi đầu tiên dĩ nhiên là: làm thế nào để viết mã máy? Chẳng ai có thể thuộc lòng tất cả các ánh xạ từ assembly sang mã máy cả, đó là chưa kể các ánh xạ này thay đổi tùy theo hệ điều hành và CPU.

Muốn biết viết bytecode như thế nào, ta phải biết assembly. Hai assembler thông dụng nhất cho cấu hình IA-32 là gasnasm, trong đó gas dùng ngữ pháp AT&T giống như gdb, còn nasm dùng ngữ pháp Intel. Ngữ pháp Intel dễ đọc hơn, nên tôi sẽ dùng nasm làm ví dụ.

Có rất nhiều cách để tìm mã máy của một đoạn lệnh mà ta muốn thực thi, bao gồm các cách sau:

  1. Cách dài dòng: viết một đoạn C, dùng gdb dịch ra assembly xem thế nào, sau đó viết assembly và dịch ra mã máy.
  2. Cách ngắn: sau một thời gian tìm hiểu bằng C, nếu ta đã khá quen thuộc với assembly thì viết thẳng bằng assembly luôn rồi dịch sang mã máy.
  3. Cách lười: chép bytecode của người khác viết sẵn lấy về dùng (ví dụ bạn có thể lấy đoạn bytecode trên tôi đã viết mà không cần biết chi tiết).
  4. Cách chuyên nghiệp: xây dựng một thư viện bytecode cho riêng mình.

Dùng cách lười thì mình không biết thật sự cái đoạn bytecode đó làm gì, có khi bị chơi khăm có virus trong đó thì tiêu tán thoòng.

Tôi minh họa cách dài trước. Chương trình in “Hello, World!” bằng C trên Linux có thể viết như sau:

int main()  {  write(1, "Hello, World!\n", 14); }

Ở đây ta dùng system call của Unix để sau này minh họa cách viết shellcode (cần system call execve). Hãy xem tiếp (các chú thích sau các dấu “;” là tôi thêm vào để giải thích.)

[NQH]:~/BO$ gcc -static -g hw.c -o hw
[NQH]:~/BO$ ./hw
Hello, World!
[NQH]:~/BO$ gdb hw
GNU gdb 6.2-2.1.101mdk (Mandrakelinux)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i586-mandrake-linux-gnu"…Using host
libthread_db library "/lib/i686/libthread_db.so.1".

(gdb) disas main
Dump of assembler code for function main:
0×080481f4 
: push %ebp 0×080481f5
: mov %esp,%ebp 0×080481f7
: sub $0×8,%esp 0×080481fa
: and $0xfffffff0,%esp 0×080481fd
: mov $0×0,%eax 0×08048202
: add $0xf,%eax 0×08048205
: add $0xf,%eax 0×08048208
: shr $0×4,%eax 0×0804820b
: shl $0×4,%eax 0×0804820e
: sub %eax,%esp 0×08048210
: sub $0×4,%esp ; now, push arguments of write() onto the stack ; last argument (14) of write() 0×08048213
: push $0xe ; next argument of write, pointer to “Hello, World!” 0×08048215
: push $0×808e4a8 ; first argument (1) of write() 0×0804821a
: push $0×1 ; call write 0×0804821c
: call 0×804db80 0×08048221
: add $0×10,%esp 0×08048224
: leave 0×08048225
: ret End of assembler dump. (gdb)

Bạn nhớ dùng liên kết tĩnh (-static), nếu không thì mã của hàm write() sẽ không được viết vào mã xuất mà sẽ được liên kết lúc load chương trình, và như thế thì ta khó dùng gdb để xem write() làm gì.

(gdb) disas write
Dump of assembler code for function write:
0x0804db80 :   cmpl   $0×0,0×80a4844
0×0804db87 :   jne    0×804dbaa 
0×0804db89 :   push   %ebx
                            ; move last argument of write() into %edx
0×0804db8a :  mov    0×10(%esp),%edx
                            ; move next argument into %ecx
0×0804db8e :  mov    0xc(%esp),%ecx
                            ; move first argument into %ebx
0×0804db92 :  mov    0×8(%esp),%ebx
                            ; copy write()’s system call number into %eax
0×0804db96 :  mov    $0×4,%eax
                            ; switch to kernel’s mode
0×0804db9b :  int    $0×80
0×0804db9d :  pop    %ebx
0×0804db9e :  cmp    $0xfffff001,%eax
0×0804dba3 :  jae    0×8050010 <__syscall_error>
0×0804dba9 :  ret
0×0804dbaa :  call   0×804e2a0 <__librt_enable_asynccancel>
0×0804dbaf :  push   %eax
0×0804dbb0 :  push   %ebx
0×0804dbb1 :  mov    0×14(%esp),%edx
0×0804dbb5 :  mov    0×10(%esp),%ecx
0×0804dbb9 :  mov    0xc(%esp),%ebx
0×0804dbbd :  mov    $0×4,%eax
0×0804dbc2 :  int    $0×80
0×0804dbc4 :  pop    %ebx
0×0804dbc5 :  xchg   %eax,(%esp)
0×0804dbc8 :  call   0×804e2e0 <__librt_disable_asynccancel>
0×0804dbcd :  pop    %eax
0×0804dbce :  cmp    $0xfffff001,%eax
0×0804dbd3 :  jae    0×8050010

Xem có vẻ phức tạp, nhưng ý chính rất đơn giản. Để gọi một system call như write(), ta bỏ mã số của write() vào thanh ghi %eax (write có mã số là 4). Sau đó lần lượt chép các tham số còn lại vào %ebx, %ecx, %edx, rồi chuyển sang kernel mode bằng lệnh int 0×80.

Mã số của các system calls có thể tìm được ở đây:

[NQH]:~/BO$ more /usr/include/asm/unistd.h
#ifndef _ASM_I386_UNISTD_H_
#define _ASM_I386_UNISTD_H_

/*
 * This file contains the system call numbers.
 */

#define __NR_restart_syscall      0
#define __NR_exit                 1
#define __NR_fork                 2
#define __NR_read                 3
#define __NR_write                4
#define __NR_open                 5
#define __NR_close                6
…
#define __NR_remap_file_pages   257
#define __NR_set_tid_address    258
#define __NR_timer_create       259
…

Dùng ý tưởng vừa học được này, ta có thể viết trực tiếp chương trình in “Hello, World!” bằng assembly như sau:

section .data ; section declaration

hello db "Hello, World!", 0x0a  ; "Hello, World!\n"

section .text ; section declaration

global _start ; default entry point for ELF linking

_start:
  mov eax, 4     ; write() system call number
  mov ebx, 1     ; 1 is standard output
  mov ecx, hello ; pointer to "Hello, World!\n"
  mov edx, 14    ; length of output string
  int 0x80       ; finally, invoke write()
; prepare for exit(0)
  mov ebx, 0     ; argument for exit()
  mov eax, 1     ; system call number of exit()
  int 0x80       ; invoke exit(0)

Dịch và chạy cho kết quả

[NQH]:~/BO$ nasm -f elf hello_world.asm
[NQH]:~/BO$ ld hello_world.o
[NQH]:~/BO$ ./a.out
Hello, World!
[NQH]:~/BO$

Viết được “Hello, World!” bằng assembly rồi, nhưng có một vấn đề quan trọng ta phải giải quyết trước khi có thể chuyển nó thành bytecode thật sự. Trong bytecode thì ta không thể để chuỗi “Hello, World\n” vào data segment của chương trình đang chạy (vì data segment không ghi lên được). Ta phải tìm cách nào đó viết “Hello, World!” mà không dùng đến data segment.

Như vậy, chuỗi “Hello, World\n” phải được để ở chỗ nào đó trong text segment của chương trình. Nhưng ta lại cần địa chỉ trên bộ nhớ của chuỗi này để bỏ vào ecx trước khi gọi write(). Có vài cách để giải quyết vấn đề này, bao gồm hai cách sau đây. (Bạn có thể tự sáng tạo thêm cách khác.)

  • Phối hợp jmp và call
  • PUSH chuỗi cần dùng lên stack trong thời gian chạy

Trong các bài tới tôi sẽ minh họa cả hai cách.

Chủ đề: Bảo mật và mật mã học | Bình luận »

Hết Schön rồi lại thêm Parijs

Ngô Quang Hưng | 28 tháng 10, 2005 | Bản để in Bản để in

Một associate professor khoa Sinh học của MIT tên là Luk Van Parijs vừa bị sa thải vì làm giả dữ liệu trong các bài báo.

Hồi 2002, vụ Hendrik Schön - một ngôi sao sáng chói của Lucent Bell Labs - làm giả dữ liệu đã gây tai tiếng cực lớn cho một trong những phòng Labs có uy tín nhất thế giới. Trong vòng 2 năm, Schön viết đâu khoảng … 80 bài báo, trong đó có nhiều bài đăng ở Science và Nature. Nhiều người nghĩ Schön sẽ thắng Nobel trong một ngày không xa. (Vài tháng trước đó, Victor Ninov ở Lawrence Berkeley National Lab cũng ở tình trạng tương tự.)

Sớm muộn gì thì cũng có một khoa học gia máy tính dính vào vụ này. Áp lực đăng báo quá lớn và làm giả dữ liệu thì quá dễ.

Chủ đề: Nghiên cứu nghiên kiếc & Nhân vật và sự kiện | Bình luận »

Từ Google Calendar đến Viezilla

Ngô Quang Hưng | 28 tháng 10, 2005 | Bản để in Bản để in

Tôi chờ Google Calendar đã lâu. Hồi tháng trước có tin đồn là khoảng cuối tháng 10 này sẽ có mà hôm nay gần cuối tháng rồi chẳng thấy đâu. Trong khi đó, giới chuyên môn lại nhao nhao về vụ Google chuẩn bị lấn chiếm eBay. Với bao nhiêu là kế hoạch cho các sản phẩm mới (lịch, dịch vụ kiểu eBay, ISP miễn phí, office, sách, …), nhiều người đã phải hỏi “Google có đang bị trải mỏng quá không?

Tại sao dân kỹ thuật thích dùng sản phẩm của Google? (Lý do của tôi chắc cũng không khác của bạn.)

Tôi vẫn còn nhớ hồi giữa những năm 90, bọn Unix gurus yêu Netscape vô cùng (thay vì IE), thế mà bây giờ Netscape 6, 7 chẳng mấy ai dùng (giao diện quá chán). Firefox gần đây ăn mừng download thứ 100 triệu (nhưng vẫn chỉ chiếm khoảng 7% tổng số browsers). Bà con cũng hơi lao xao về Flock, sắp ra. Thế nếu Google cũng nhảy vào làm web-browser thì sao nhi?

Nghiêm túc mà nói thì tôi hoàn toàn không hài lòng với tất cả các browsers hiện có, cho nên Flock hay Googlowers cũng đều đáng đợi.

Thật ra bây giờ viết một cái browser dùng được không phải quá khó, nhất là sau khi Netscape mở nguồn hồi 1998 (và khởi nguồn dự án Mozilla, và Firefox cũng sinh ra từ đó). Tôi tự hỏi nếu khoảng một chục developers tốt nhất Việt Nam quyết định ngồi xuống viết một Viezilla thì sao nhỉ? Wouldn’t that be wonderful?

Bạn muốn Viezilla phải như thế nào? (Chức năng, giao diện, …)

Chủ đề: CNTT các nước và VN | Bình luận (1) »

Vài công ty điện thoại ngăn VoIP traffic

Ngô Quang Hưng | 27 tháng 10, 2005 | Bản để in Bản để in

Có lẽ hầu hết chúng ta đều dùng thẻ điện thoại hoặc một chương trình VoIP nào đó (Skype, Packet8, …) để gọi từ/về Việt Nam.

Một bài báo mới của IEEE Spectrum cho biết nhiều công ty viễn thông đã và đang tiến hành cài đặt một filter mới để lọc các VoIP packets ra khỏi mạng của họ, bao gồm các công ty viễn thông ở Arab Saudi, Egypt, France, Germany, … Thậm chí các công ty truyền hình cáp cũng cài đặt sản phẩm filter này, và FCC cho rằng làm điều này là hợp lệ.

Bước kế tiếp dĩ nhiên là tìm cách mã hóa các gói VoIP để hòa mình vào traffic bình thường, tránh các filter này. Thách thức kỹ thuật là làm thế nào để đảm bảo chất lượng âm thanh. Chắc chắn trong INFOCOM và SIGCOMM hai ba năm tới sẽ có vài bài báo về đề tài này, có khi lại có cả bài của tôi hay một bạn đọc blog này!

Chủ đề: CNTT các nước và VN & Mạng máy tính | Bình luận (1) »

Nói thêm về một số sách về optimization

Nguyễn Xuân Long | 25 tháng 10, 2005 | Bản để in Bản để in

Định chỉ viết một comment ngắn gọn vào bài viết hôm trước của anh Hưng, nhưng tôi thấy vấn đề này đáng một bài blog riêng. Những ai phải đối đầu với những vấn đề cụ thể trong nghiên cứu về kỹ thuật (engineering) đều gặp phải những bài toán tối ưu hóa (optimization) phức tạp, hoặc là rất nhiều chiều (high dimension), hoặc liên quan đến tính không lồi (nonconvexity), hoặc liên quan đến tính phi tuyến tính (nonlinearity), hoặc có tính tổ hợp và rời rạc (combinatorial/discrete), hoặc một vài các yếu tố khó khăn trên gộp lại.

Những kiến thức ban đầu về numerical analysis học từ đại học và phổ thông thì rất hạn chế, không đủ để giải quyết những vấn đề thực tế. Mặt khác, lại có rất nhiều sách về optimization. Một học trò bắt đầu tìm hiểu về optimization thường bị hoa mắt về các loại thuật ngữ programming (như linear programming, nonlinear programming, quadratic programming, convex programming, semi-infinite programming, semidefinite programming, d.c. programming, integer programming v.v)

Phải bắt đầu từ đâu? Cái gì thì cần đọc và cái gì không? Đây là những câu hỏi thường gặp phải với những ai bắt đầu tìm hiểu sâu một chút về optimization. Trừ khi ngành của bạn nghiên cứu chuyên về optimization (như giáo sư Hoàng Tụy), thường thì chúng ta không thể đọc hết mọi thứ một lúc, mà phải có chiến thuật học tập (chủ yếu là tự học). Chỉ nên học những khái niệm căn bản trước, và sau đó tra khảo thêm các phương pháp optimization mới khi cần.

Như anh Hưng đã nói, quyển đầu tiên nên sử dụng là quyển miễn phí của Stephen Boyd and Lieven Vandenberghe.

Convex optimization, S. Boyd and L. Vandenberghe, Cambridge Press, 2002.

Quyển này viết rất rõ ràng dễ hiểu, nhiều minh họa hình học, và chú trọng đặc biệt những khái niệm căn bản về giải tích lồi, trong đó có khái niệm đối ngẫu trong bài toán convex optimization. Thế mạnh của quyển sách là sự tập trung vào những bài toán có thể quy ra thành convex problems. Rất nhiều ví dụ thú vị bắt nguồn từ thực tế hoặc các nghiên cứu các ngành như thống kê hay information theory, nằm trong diện này, và do đó có thể giải quyết hiệu quả bằng interior point method. Phương pháp interior point (của hai nhà toán học người Nga, Nemirovski và Nesterov), có thể nói một trong những nguyên nhân chính góp phần dẫn đến sự ứng dụng rộng rãi của convex optimization methods vào các vấn đề trong engineering.

Xin nói thêm Nemirovski là người có nhiều đóng góp to lớn đối với lớp thuật toán hiện đại về optimization. Ông này cũng có ảnh hưởng đến sự phát triển của thuật toán polynomial time đầu tiên (của Khachyan) cho linear programs vào những năm 70 bằng ellipsoid methods. Thuật toán của Khachyan không được hiệu quả so với thuật toán simplex (dù về mặt lý thuyết có thể là exponential-time) của Dantzig, nhưng gây chấn động trong giới computational complexity. Đến thập niên 80 thì Karmakar mới giới thiệu một thuật toán hiệu quả hơn cho linear programs. Ý tưởng của Karmakar được Nemirovski phát triển lên cho semidefinite programs (cái sau kỳ thực có thể coi như linear programs cho không gian là những ma trận dương tính), và các bài toán convex bậc cao hơn.

Quyển sách của Boyd và Vandenberghe chủ yếu chỉ tập trung vào những ý tưởng thuật toán mới kể trên. Tuy vậy hai tác giả bỏ qua nhiều phương pháp tối ưu cổ điển rất hữu dụng cho cả các bài toán convex và nonconvex. Do đó, theo tôi nên đọc quyển sách của Boyd song song với quyển sách của Bertsekas:

Nonlinear programming. Dimitri P. Bertsekas, 2nd Edition, 2004.

Bertsekas nói rất tỉ mỉ về các vấn đề cụ thể ta thường phải đối phải khi phải sử dụng một thuật toán tối ưu. Ví dụ, nếu dung gradient descent thì cần phải tính đến chuyện điều khiển update stepsize như thế nào, v.v. Có mô tả khá đầy đủ các phương pháp cổ điển khác như conjugate gradient, golden section, v.v.

Đi sâu vào các dạng bài toán convex đặc thù, như linear programming (chuyên sâu về linear constraints và linear cost functions), có một quyển sách rất tốt của hai người đồng hương và đồng nghiệp của Bertsekas:

Linear optimization. Dimitris Bertsimas and John N. Tsitsiklis., 1997 .

Rất nhiều bài toán ta thường gặp hàng ngày và trong nghiên cứu có thể quy về dạng linear programs khá đơn giản (ví dụ như max flow, min cut).

Một dạng bài toán convex khác khá thú vị, liên quan đến max functions (hàm số có thể biểu diễn dưới dạng maximum của một số hàm khác). Bài toán đi tìm giá trị cực tiểu của max functions gọi là bài toán min-max, và hay gặp nhiều trong nhiều tình huống như game theory, decision theory và thống kê. Nếu là max của một số lượng vô hạn các hàm khác, thì bài toán minimax này được gọi là semi-infinite programs. Một quyển sách rất hay đi sâu về lĩnh vực này là:

Optimization: Algorithms and Consistent Approximations, by Elijah Polak, 1997.

Polak và Bertsikas có thể được xếp vào nhóm những người muôn năm cũ, so với lớp hậu sinh như Boyd. Những lớp già thường vẫn có những túi mẹo rất quý. Đọc những quyển sách của họ ta có thể học được nhiều cách nhìn vấn đề thú vị mà không thấy ở quyển sách của Boyd.

Rất nhiều vấn đề ta gặp không thể nào quy được về các dạng lồi cơ bản như kể trên. Tuy vậy những phương pháp optimization cho bài toán convex, và những hiểu biết về giải tích lồi rất cần thiết và hữu ích. Một mảng quan trọng là các vấn đề về combinatorial optimization, hay còn gọi là integer programming. Làm thế nào để sử dụng các phương pháp convex optimization (như linear programming, hay semidefinite programming) để thiết kế các thuật toán xấp xỉ cho các bài toán có tính chất combinatorial này? Đây đang là một hướng nghiên cứu nóng hổi trong thập niên 90 cho tới nay. Như anh Hưng đã nhắc đến, có nhiều đóng góp đáng kể của các tác giả như Lovasz, Schrijver, Jean Lasserre, Michel Goemans, v.v. Đây là một lĩnh vực cực kỳ rộng lớn, bao trùm nhiều ngành từ bên toán, theoretical CS, operations research, AI,… và tôi cũng không biết là bao. Theo tôi biết thì chưa có một quyển sách hệ thống nào về mảng này, đặc biệt nói về sự kết hợp giữa lý thuyết convex optimization cổ điển với combinatorial optimization, ngoài hệ thống lecture notes mà anh Hưng đã nhắc đến.

Một lĩnh vực rất rộng và tổng quát để tấn công các bài toán không convex gọi là global optimization. Chú ý là phần lớn các phương pháp optimization cho các bài toán convex đều dựa vào khái niệm gradient/subgradient, và do đó các update đều mang tính địa phương (local). Các phương pháp update không mang tính local có thể kể ra như các phép cắt, các phép decomposition, các phép branch and bound v.v. Giáo sư Hoàng Tụy chính là một trong những cha đẻ của lĩnh vực này. Một quyển sách rất hay của ông là:

Convex Analysis and Global Optimization (Nonconvex optimization and its applications), Hoang Tuy, Kluwer Academic Publishers, 1998.

Link to amazon.

Giá của quyển này trên amazon thật là kinh khủng. Có lẽ giáo sư Hoàng Tụy phải in thêm nhiều nữa. Quyển sách của Hoàng Tụy có phần đầu giới thiệu những khái niệm cơ bản về giải tích lồi (convex analysis) một cách rất súc tích, rõ ràng và bổ ích. Sau đó tác giả đi vào các khái niệm về d.c. programming (difference-of-convex programming), và các phương pháp global optimization kinh điển, bắt đầu từ nhát cắt Tụy nổi tiếng mà tác giả đã giới thiệu vào năm 1964. Xuyên suốt quyển sách là một tính chất căn bản của các hàm số liên tục (có lẽ tổng quát hơn thế): Tất cả các hàm này, kể cả khi không convex, đều có thể biểu diễn dưới dạng hiệu của hai hàm convex. Với các tập hợp không convex cũng có phát biểu tương tự. Vấn đề là làm sao ta có thể lợi dụng những tính chất về convexity để tìm điểm global optimum của các hàm d.c. Có thể nói có cả một trường phái về D.C. programming rất hung hậu bao gồm nhiều nhà toán học người Việt nam (ở viện Toán ở trong nước và nước ngoài). Đây là lĩnh vực khó, chưa có nhiều thuật toán hiệu quả, và hình như sự hiện diện còn hạn chế trong mainstream của ngành optimization.

Nếu nghề của bạn phải sử dụng nhiều đến optimization algorithms, thì không chóng thì chày cũng phải bỏ thời gian để tìm hiểu quy củ hơn các khái niệm cơ bản về giải tích lồi, như khái niệm đối ngẫu, convex calculus, v.v. Giải tích lồi tuy không khó, và có tính hình học trực quan cao, nhưng tôi vẫn luôn kinh ngạc khi thấy những khái niệm này xuất hiện “bất thình lình” ở mọi ngóc ngách một cách khá sâu sắc trong information theory, trong statistics, trong decision theory,… Hiểu biết sâu về giải tích lồi có thể giúp ta thiết kế và phân tích các thuật toán hiệu quả cho nhiều vấn đề tưởng như rất hóc búa.

Một quyển sách kinh điển về giải tích lồi là của Rockafellar:

Convex Analysis (Princeton Landmarks in mathematics and physics), R. Rockafellar, 1996.

Quyển sách này nên dung để tra khảo khi bạn cần tìm hiểu hoặc sử dụng một cách thấu đáo một khái niệm nào đó về giải tích lồi. Không nên dung để tự học vì sẽ bị ngập ngày vào những chi tiết chưa cần ngay. Để tự học thêm về convex analysis, một quyển sách rất tốt của hai tác giả người Pháp là:

Convex analysis and minimization algorithms, Jean-Baptiste Hiriart-Urruty and Claude Lemarechal. .

Quyển này viết rất sư phạm. Được chia làm hai volumes, phần một dành cho người mới học, phần hai cho cả chuyên gia. Ngoài ra, các tác giả còn có một quyển sách tóm lược cho cả hai cuốn vào một volume. Tất cả đều rất bổ ích.

Chắc chắn còn nhiều quyển sách rất hay về optimization nữa mà tôi chưa biết. Những chuyên gia nghiên cứu về optimization (các nhà toán học ứng dụng) phải mất cả đời để suy nghĩ và sáng tạo các thuật toán mới. Còn với những người sử dụng optimization trong công việc của mình như phần lớn dân học KHMT, có lẽ cũng phải mất cả đời tìm hiểu và mày mò sử dụng, biến tấu chúng vào bài toán cụ thể thì mới thành thạo được.

Vài quyển sách về optimization tôi không muốn giới thiệu cho bất kỳ ai vì các lý do (chủ quan) khác nhau:

Convex analysis and nonlinear optimization: Theory and Examples (by Jonathan Borwein, Adrian Lewis) : Quá ngắn gọn và cô đọng, có lẽ chỉ dành cho chuyên gia.

Combinatorial optimization: algorithms and comnplexity (Christo Papadimitriou and Ken Steiglitz): Không tệ, nhưng hơi hời hợt.

Chủ đề: Giới thiệu sách & Trí tuệ nhân tạo | Bình luận (4) »

Bác Hồ Tú Bảo bàn về đại học ở VN

Nguyễn Xuân Long | 25 tháng 10, 2005 | Bản để in Bản để in

Link của bài báo ở đây.

Chủ đề: Giáo dục | Bình luận »

Sách miễn phí trên Net [3]

Ngô Quang Hưng | 24 tháng 10, 2005 | Bản để in Bản để in

Đề tài lần này là convex và combinatorial optimization. Về convex optimization thì có một quyển rất tốt

Stephen Boyd and Lieven Vandenberghe, Convex Optimization, Cambridge University Press.

Về combinatorial optimization thì Laci Lovasz và Alexander Schrijver là các tác giả kỳ cựu. Tôi giới thiệu các tài liệu sau:

Alexander Schrijver, On the history of combinatorial optimization (till 1960).
Alexander Schrijver, A course in combinatorial optimization, Lecture Notes.
Laci Lovász, Semi-definite optimization, Lecture notes, 1995-2001.
Laci Lovász, Semidefinite programs and combinatorial optimization, 2000.

Chủ đề: Giới thiệu sách | Bình luận »

Cập nhật glibc cho Mandrake

Ngô Quang Hưng | 23 tháng 10, 2005 | Bản để in Bản để in

Tôi vẫn dùng Mandrake Linux vài năm gần đây và không có gì phàn nàn. Hôm qua cần dịch một chương trình C với static linking và nó bắt đầu có vấn đề. (Bình thường thì cũng chẳng cần phải dịch chương trình liên kết tĩnh vì output file lớn không cần thiết, nhưng tôi cần mã của một system call.)

Thứ nhất, mặc dù đã có các gói rpm glibc và glibc-devel, Mandrake không cài đặt thư viện tĩnh libc.a theo chế độ mặc định lúc cài hệ điều hành. Muốn có libc.a thì tôi cần gói rpm glibc-static-devel. Các gói này tôi đang dùng phiên bản 2.3.3-23.1 (khá cũ).

Tôi muốn nhân cơ hội này cập nhật luôn glibc lên phiên bản mới nhất 2.3.5-5.

root@hanoi (/home/softwares) % rpm -Uvh glibc-2.3.5-5mdk.i586.rpm
error: Failed dependencies:
       glibc = 6:2.3.3 is needed by (installed) locales-2.3.3-8mdk
       glibc = 6:2.3.3-23.1.101mdk is needed by (installed) glibc-devel-2.3.3-23.1.101mdk

Cái gói locales có thể bỏ đi cài lại được, nhưng gói glibc-devel-2.3.3-23.1 thì phiền hơn nhiều:

root@hanoi (/home/softwares) % rpm -ev glibc-devel-2.3.3-23.1.101mdk
error: Failed dependencies:
       devel(libm) is needed by (installed) libgpm1-devel-1.20.1-11mdk
       devel(libm) is needed by (installed) libncurses5-devel-5.4-1.20040529.2mdk
       devel(libm) is needed by (installed) libxml2-devel-2.6.13-1.1.101mdk
       glibc-devel is needed by (installed) XFree86-devel-4.2.1-3mdk
       glibc-devel >= 2.2.5-14mdk is needed by (installed) gcc-3.4.1-4mdk

Tiếp tục kiểu này, gói gcc có rất nhiều các gói khác phụ thuộc vào nó. Như vậy cập nhật bằng phương pháp bổ củi xem ra không phải là giải pháp.

Tôi thử dùng rpmdrake thì nó lại không có chọn lựa cho cập nhật glibc, nhiều khả năng chính là vì hầu hết các gói khác trong hệ thống phụ thuộc vào glibc. Tìm lòng vòng trên mạng thì có nhiều người cũng bị các vấn đề kiểu này khi cập nhật glibc.

Cuối cùng, tôi phải tìm chính gói glibc-static-devel-2.3.3-23.1 để cài. Bạn có biết cách nào tốt nhất để cập nhật glibc mới không? (Đem nguồn mới về dịch lại và cài đặt thì có các vấn đề cực kỳ lắt nhắt về sự không nhất quán của các header files và các phụ thuộc khác.)

Gần đây OpenBSD sắp ra phiên bản mới có tính bảo mật cao, chưa kể các *BSD nổi tiếng là được viết tốt (clean code) hơn *Linux (cả Dennis Richie cũng đã có lần nói vậy, tôi không tìm lại được link đến phỏng vấn này). Tìm được khoảng thời gian trống nào tôi sẽ thay Mandrake bằng một *BSD nào đó.

Chủ đề: Các hệ thống máy tính | Bình luận (2) »

Lạm bàn về thiết kế chương trình đào tạo KHMT (4)

Ngô Quang Hưng | 22 tháng 10, 2005 | Bản để in Bản để in

  1. Dạy gì trong các lớp đầu tiên của KHMT?

Như đã viết, ba phương pháp dạy lập trình cơ bản hiện nay là (1) ngôn ngữ thủ tục trước (C/Pascal/Basic/…), (2) ngôn ngữ hướng đối tượng trước (C++/Java/Smalltalk/…), và (3) ngôn ngữ hàm trước (Scheme/Lisp). Việc chọn lựa phương pháp nào trong ba phương pháp này nên đồng nhất với 3 mục tiêu đã liệt kê, trong đó cá nhân tôi cho rằng mục tiêu số 1 là quan trọng nhất: truyền tải cho sinh viên cái thần của KHMT. Chữ “thần” này rất rộng, và có lẽ mỗi cá nhân tiếp xúc với KHMT sẽ có cảm nhận và cách hiểu khác nhau về nó.

Đối với sinh viên mới bước vào ngành, ta cần phát triển cho họ các mô hình nhận thức (cognitive models) trong não về các khái niệm cơ bản của KHMT. Thay vì định nghĩa cụ thể, ta xét một vài ví dụ về cảm nhận cá nhân với KHMT:

  • Trong bài chung qui chỉ tại Cantor, tôi có viết sơ bộ về ý nghĩa của khái niệm tính toán (computation), một trong những đóng góp sâu sắc nhất của KHMT cho kho tàng tri thức của nhân loại. Tính toán không bị ràng buộc bởi một mô hình cụ thể nào đó (như mô hình máy Turing, mô hình RAM/Bus/CPU, …). Không ít các nhà khoa học tin rằng các quá trình sinh học và vật lý là các quá trình tính toán, thiên nhiên có các thuật toán của nó. Không phải ngẫu nhiên mà KHMT đang vươn cánh tay non trẻ của nó sang sinh học, vật lý, kinh tế, toán học, với các phân ngành phát triển cực nhanh những năm gần đây như computational biology, computational physics, quantum computing, computational game theory, computational chemistry, computational neuroscience, … Giải Nobel Kinh Tế 2005 được trao cho các game theorists, khoảng 30/40 năm nữa sẽ có Nobel kinh tế cho một computational game theorist, tôi tin chắc là như thế! Còn Nobel vật lý liên quan đến tính toán có lẽ sẽ đến sớm hơn nữa. Chú ý rằng các phân ngành kể trên không đơn thuần là ứng dụng máy tính vào một khoa học khác. Rất nhiều các quá trình của tự nhiên về cơ bản là quá trình tính toán, và chúng ta mới chỉ bắt đầu khám phá các quá trình tính toán trong tự nhiên.
  • KHMT không phải là Java, C++, hay lập trình web. Thật đáng buồn khi một sinh viên máy tính nghĩ về ngành máy tính bằng ánh mắt của một lập trình viên. Lập trình có thể là công việc ta phải làm hàng ngày, nhưng mỗi chúng ta cần phải hiểu rằng lập trình chỉ là một phương pháp bổ củi để hiện thực hóa các ý tưởng thiết kế tuyệt đẹp, các thuật toán sâu sắc, hay các hacks thông minh. Bổ củi và khắc gỗ hiển nhiên là khác nhau.
  • Cũng thật đáng buồn nếu tầm nhìn của dân máy tính bị gói gọn từ Bill Gates sang Steve Jobs, từ IBM đến Oracle, hay từ search engines đến spreadsheets. Khi nghĩ đến Vật Lý ta không chỉ liên tưởng đến CEO của các công ty làm máy tăng tốc hạt, các xưởng sản xuất bom nguyên tử, hay làm vật liệu bán dẫn. KHMT còn rất trẻ so với Vật Lý, nhưng KHMT cũng có một câu chuyện cực kỳ truyền cảm để kể. Ảnh hưởng về mặt khái niệm của KHMT đến xã hội và các ngành khoa học khác nhiều không kể xiết.

Trở lại với ba kiểu dạy lập trình. Kiểu 1 và 2 có ưu điểm là các ngôn ngữ C/C++/Java được dùng rất phổ thông trong thực tế. Lập trình hướng đối tượng minh họa rất tốt nhiều khái niệm quan trọng như data abstraction, information hiding, inheritance, vân vân. Khuyết điểm lớn nhất của dạy kiểu 1 hay 2 là sinh viên thường phải lọ mọ trong mớ hỗn độn các cú pháp, các hàm, lớp, cấu trúc phức tạp của các ngôn ngữ này. Sự phức tạp không cần thiết của các cấu trúc cơ bản làm giảm đáng kể sự nhạy cảm về tầm quan trọng của ý tưởng. Thường thì các chương trình chập chững trong C/C++/Java được viết theo kiểu chắp vá, ý tưởng về thuật toán rất lộn xộn là vì lý do này.

Cá nhân tôi cho rằng kiểu 3: dạy ngôn ngữ hàm trước – đặc biệt là Scheme – là phương pháp hay nhất hiện có. Quyển Structure and Interpretation of Computer Program của Hal Abelson và Jerry/Julie Sussman là một trong những quyển sách cơ bản về KHMT hay nhất mà tôi biết. (Chú ý rằng quyển này bây giờ miễn phí trên web!) Các đề tài sâu sắc của KHMT được minh họa bằng Scheme, ngôn ngữ rất đơn giản về cú pháp nhưng rất hùng mạnh về khả năng biểu cảm các khái niệm. Khuyết điểm lớn nhất của phương pháp dạy Scheme trước là nó “thích” các sinh viên thông minh hơn.

Các phân tích ở trên cũng được áp dụng cho việc xác định các đề tài dạy trong lớp giới thiệu ngành. Có lẽ không cần phải viết gì thêm.

Chủ đề: Giáo dục | Bình luận (4) »

Mã bí mật trong các trang in

Ngô Quang Hưng | 20 tháng 10, 2005 | Bản để in Bản để in

Một số nhà nghiên cứu ở EFF đã khám phá ra rằng một số máy in laser màu được thiết kế để cố tình in ra các chấm nhỏ li ti trong mỗi trang in. Các chấm này là một mật mã cho biết các thông tin như ngày giờ in, và serial number của máy in. Secret Service của Mỹ nhận rằng họ đã liên kết với một số nhà sản xuất máy in laser màu để làm việc này.

Đây là danh sách các máy in, và hướng dẫn để thấy các chấm. Hai ảnh sau link từ bài của EFF:

Chủ đề: Bảo mật và mật mã học & Nhân vật và sự kiện | Bình luận »

Estonia là nước đầu tiên có bầu cử qua Internet

Ngô Quang Hưng | 19 tháng 10, 2005 | Bản để in Bản để in

Mặc dù cuộc bầu này chỉ có khoảng 10 nghìn người tham gia, thành viên cũ của CCCP này rất lạc quan về tương lai.

Bầu cử điện tử (e-voting) là một đề tài nghiên cứu rất hay, từ các vấn đề công nghệ, bảo mật, luật pháp, đến các khía cạnh lý thuyết của mật mã học.

Chủ đề: Bảo mật và mật mã học & Nhân vật và sự kiện | Bình luận (1) »

Tiếu : tốc độ ánh sáng

Ngô Quang Hưng | 18 tháng 10, 2005 | Bản để in Bản để in

  • Hỏi: có thể nào đi nhanh hơn tốc độ ánh sáng không?
  • Trả lời: dĩ nhiên rồi!
    Emails được gửi bằng tín hiệu điện.
    Tín hiệu điện chạy với tốc độ ánh sáng.
    AOL gửi emails.
    Hầu như tất cả mọi thứ đều chạy nhanh hơn AOL.
    Do đó, hầu như mọi thứ đều chạy nhanh hơn tốc độ ánh sáng.

  • Hỏi: tại sao tốc độ ánh sáng chỉ có 300000 km/h? Chẳng lẽ các khoa học gia không làm tốt hơn được hay sao?
  • Trả lời: có chứ. Các nhà khoa học đã chế ra ô tô chạy nhanh hơn 300000 km/h nhưng không bán được vì đèn pha yếu quá.
    Đây cũng là lý do tại sao nhiều nhà khoa học không có bằng lái.

[ Nguồn: ở đây ]

Chủ đề: Vui - Giải Trí | Bình luận »

Lạm bàn về thiết kế chương trình đào tạo KHMT (3)

Ngô Quang Hưng | 18 tháng 10, 2005 | Bản để in Bản để in

  1. KHMT: bắt đầu như thế nào?

Như đã viết, một CT đào tạo KHMT có hai phần chính: phần lõi (core) là phần bắt buộc, và phần tự chọn (elective) để sinh viên cá nhân hóa theo sở thích (và đôi khi, nhu cầu thị trường).

Phần tự chọn, như đã phân tích, là một trong các thành tố trụ cột của các nguyên tắc 1, 2, và 4. Phần tự chọn làm CT “dẻo” hơn, cho sinh viên sự độc lập tự chủ trong hướng nghiệp. Mặt khác, phần này cũng làm phức tạp hóa vai trò của người thầy. Ở Mỹ thì mỗi sinh viên đại học đều có một giáo sư hướng dẫn (advisor) với vai trò chủ yếu là hướng dẫn sinh viên chọn các lớp elective sao cho phù hợp với khả năng, sở thích của sinh viên cùng với các yêu cầu hiện tại của công nghệ. Vai trò này không giống với vai trò hướng dẫn nghiên cứu sinh, dù rằng chúng có phần giao khác rỗng. Để làm tốt việc hướng nghiệp cho sinh viên, các giáo sư hướng dẫn và các cơ cấu tổ chức khác trong khoa, trong trường (như các chương trình internship, co-op, …) cần có sự phối hợp nhịp nhàng nhưng không được cứng nhắc. Làm tốt công việc này đòi hỏi người thầy phải tâm lý, có cái nhìn thoáng và rộng, và nếu có thể - có nhiều quan hệ với các công ty để giới thiệu sinh viên.

Quay trở lại với hai phần chính của CT. Tài liệu CS2001 có liệt kê rất cụ thể những đơn vị nào là đơn vị lõi của KHMT. Chú ý rằng “lõi” không đồng nghĩa với “sơ cấp”. Có nhiều đơn vị lõi không thể nào dạy ở các lớp bắt đầu của CT. Một CT KHMT ở Mỹ thường được chia thành 3 giai đoạn:

  • Giai đoạn giới thiệu: các lớp bắt đầu, giới thiệu ngành, thường học trong 2 năm đầu của CT 4 năm.
  • Giai đoạn chuyển tiếp: các lớp năm 2 và năm 3, thường dùng để đóng móng kiến thức nền.
  • Giai đoạn nâng cao: các lớp năm 4, dùng cho chuyên môn hóa.

Trục giai đoạn và trục lõi/tự chọn vuông góc với nhau. Giai đoạn nào cũng có thể có phần lõi và phần tự chọn. Tuy nhiên, thường thì phần tự chọn nằm ở giai đoạn chuyển tiếp và giai đoạn nâng cao để tránh phức tạp hóa quá mức cho sinh viên mới vào ngành.

Trong phần này của bài viết, ta đi sâu vào giai đoạn giới thiệu. Đây là giai đoạn có nhiều cách tiếp cận nhất và gây nhiều tranh cãi nhất trong giới chuyên môn. Trước khi bàn thảo cụ thể các tranh cãi này, ta hãy thống nhất xem mục tiêu của giai đoạn giới thiệu là gì? Theo quan điểm cá nhân tôi, giai đoạn này có các mục tiêu sau đây:

  • Mục tiêu 1: truyền tải cho sinh viên cái tinh thần của ngành KHMT, bào gồm các khía cạnh kỹ thuật, khoa học, xã hội, và lịch sử của KHMT
  • Mục tiêu 2: làm bộ lọc, loại bớt khỏi CT các sinh viên vốn không thích hợp cho việc học KHMT
  • Mục tiêu 3: chuẩn bị kiến thức cho sinh viên qua giai đoạn chuyển tiếp

Bàn thêm một chút về mục tiêu số 2. Không phải ai cũng có khả năng làm một kỹ sư/khoa học gia máy tính, tương tự như không phải ai cũng có khả năng là nhà văn, nhà toán học, kiến trúc sư, v. v. Có lẽ chính xác hơn thì phải nói là “không phải ai cũng có thể viết văn tốt, giải toán khó, và thiết kế giỏi”. Điểm này khá tinh tế, tôi không muốn viết quá nhiều, để các bạn thảo luận thiêm thì có lẽ hay hơn.

Xin đơn giản hóa một cực của mệnh đề này bằng: “không phải ai cứ học mãi rồi sẽ thành một lập trình viên khá”. Đào tạo những người vốn không thể là lập trình viên tốt sẽ có các hậu quả khó chịu: phần mềm viết đầy bugs, chạy sai thiết kế, hoàn tất chậm, v.v. (Các lập trình viên tốt có thể sẽ nói: chính bọn lập trình viên dỏm làm chúng tôi có nhiều việc để làm J, điều này có thể không xa sự thật, nhưng không phải là mục tiêu của CT đào tạo.) Viết các chương trình tốt thật sự là một nghệ thuật! Và dĩ nhiên không phải ai cũng làm nghệ sĩ được.

Hiện nay ở Mỹ có 6 phương pháp chính để thiết kết giai đoạn giới thiệu của CT:

  1. Ngôn ngữ thủ tục trước (imperative first). (Chú thích: tôi không rõ dịch “imperative language” thành “ngôn ngữ thủ tục” có chính xác không, xin góp ý. Đây là các ngôn ngữ như kiểu Pascal, C, …)
  2. Ngôn ngữ hướng đối tượng trước (objects first).
  3. Ngôn ngữ hàm trước (functional first).
  4. Giới thiệu chiều rộng trước (breadth first).
  5. Giải thuật trước (algorithms first).
  6. Phần cứng trước (hardware first).

Người ta đã tranh luận rất nhiều về việc có nên dạy lập trình trong lớp đầu tiên của KHMT không, và khi bắt đầu dạy lập trình thì chọn cái nào trong ba phương pháp 1, 2, 3.

Dạy lập trình trước có nhiều lợi điểm, bao gồm:

  • Lập trình là một kỹ năng mà bất kỳ sinh viên KHMT nào cũng phải học, phù hợp với mục tiêu 3.
  • Trong một số trường hợp, khó giới thiệu các đề tài bề rộng của KHMT nếu sinh viên không biết gì về lập trình.
  • Dạy lập trình trước thì các sinh viên không có khả năng lập trình sẽ chuyển ngành khác, đỡ tốn thời gian của họ, phù hợp với mục tiêu 2; Nếu không dạy lập trình, để sang một, hai năm sau, sinh viên nào không có khả năng lập trình sẽ bị mất một năm để khám phá ra KHMT không phải dành cho họ.

Ngược lại, lập trình trước cũng có các khuyết điểm, bao gồm:

  • Nó làm cho người mới học bị “dội”, tưởng rằng KHMT chỉ có lập trình, không truyền tải được cái “thần” của ngành như mục tiêu 1 yêu cầu. Điểm này tương tự như môn giải tích (calculus) trong toán học. Giải tích là công cụ rất quan trọng, nhưng dạy giải tích từ đầu có thể làm chán các sinh viên vốn thích có cái nhìn phóng khoáng hơn về môn Toán.
  • Về mặt kỹ thuật, dạy lập trình rất dễ làm sinh viên bị vướng víu bởi các lỗi cú pháp, sa lầy vào phần cơ của một ngôn ngữ nào đó.
  • Có thể làm sinh viên nghĩ rằng lập trình là cách duy nhất để giải quyết các vấn đề thực tế bằng máy tính. Điều này đặc biệt quan trọng cho các sinh viên không phải chuyên ngành KHMT, chỉ lấy một lớp giới thiệu KHMT để biết nó là gì.

Giới thiệu chiều rộng trước thì phục vụ mục tiêu 1 tốt, nhưng không phục vụ mục tiêu 2 và 3 tốt bằng. Các phương pháp “thuật toán trước” và “phần cứng trước” cũng có các giới hạn và lợi ích của chúng, tuy nhiên tôi không thích chúng nên không bàn thêm.

Tài liệu CS2001 khuyên rằng ta nên có 3 lớp cho giai đoạn khởi đầu, trong đó có một lớp giới thiệu bề rộng, và hai lớp lập trình. Ngoài ra, cần phải yêu cầu sinh viên học một lớp toán rời rạc trong năm đầu tiên. Tôi hoàn toàn đồng ý với lời khuyên này.

Lần tới tôi sẽ thảo luận 2 câu hỏi: (1) dạy gì trong lớp giới thiệu bề rộng, và (2) dùng phương pháp nào (1, 2, 3) cho hai lớp lập trình? Dĩ nhiên, câu trả lời cho hai câu hỏi này phải phù hợp với ba mục tiêu kể trên của giai đoạn giới thiệu.

Chủ đề: Giáo dục | Bình luận »

Các bài kế »