Thứ Năm, 17 tháng 9, 2009

Dynamic Memory- Cấp phát động

Thông thường khi lưu trữ dữ liệu cùng kiểu với số lượng lớn hoặc các đối tượng, bạn dùng mảng.
VD:
int a[100];
Nhưng có 1 trở ngại lớn là khi viết chương trình, bạn không biết cụ thể độ dài của mảng là bao nhiêu. Bạn không thể viết:
cin>>n;
int a[n];
Trình biên dịch yêu cầu n phải là hằng hoặc một define nào đó.



Toán tử new


Trong C++, bạn dùng toán tử new để cấp phát động bộ nhớ có kích thước xác định và trả về con trỏ trỏ tới phần tử đầu tiên của mảng.
VD:
pointer = new type   //Cấp phát 1 phần tử
pointer = new type [n_of_elements] //Cấp phát n phần tử


Cách sử dụng

  • Truy cập phần tử đầu tiên: * pointer hoặc pointer[0]
  • Truy cập phần tử thứ i: *(pointer+i) hoặc pointer[i].
Lưu ý:
  1. Là cách cấp phát bằng toán tử new sử dụng vùng nhớ heap ( theo mình biết là đi ngược từ vùng nhớ nào đó trở về null). Còn mảng trong C/C++ thì ngược lại.
  2. Mình thử viết 1 class ( cố tình khai báo hàm tạo và hàm hủy là private), xài malloc, kết quả là dev-C không báo lỗi, nhưng khi chuyển qua new(), thì kết quả biên dịch lại báo lỗi???
  3. Không cấp phát động được cho class. Mình đã thử mãi mà không được. Giải pháp là bạn override operator (dịch tiếng Việt kì quá). VD: new ClassA();

Kiểm tra

Nếu bộ nhớ không đủ, việc cấp phát sẽ thất bại. Trên cplusplus có nêu ra 2 giải pháp:
  1. Sử dụng hàm bad_alloc
  2. Sử dụng nothrow, trả về con trỏ kiểu Null nếu việc cấp pháp không thành công. Bạn nên nhớ khai báo tiêu đề
VD:
bobby = new (nothrow) int [5]; 
Nothrow còn có nhiều công dụng nữa nhưng mình không biết.
Ngoài ra, trình biên dịch sẽ tự động trả về Null khi cấp phát không thành công.

Toán tử Delete

Bạn dùng toán tử delete để giải phóng vùng nhớ được cấp phát.
delete pointer;
delete [] pointer;
Có 1 đều quan trọng mà ít tài liệu nói tới: Khi cấp phát bởi toán tử new với các biến không có constructor (hàm tạo) thì nó sẽ tự giải phóng khi ra khỏi hàm. Còn đối với class, bạn cần phải viết hàm hủy.

Sự kế thừa và các vấn đề liên quan- Phần 3

Xem thêm Sự kế thừa và các vấn đề liên quan- Phần 2

Những thành phần nào được thừa kế:
Về nguyên tắc, lớp dẫn xuất kế thừa tất cả các member của lớp cơ sở ngoại trừ:
  • Constructor và destructor
  • operator=()
  • Friend functions, friend classes. Cái này mình chưa rõ lắm.
Mặc dù hàm tạo (constructor) và hàm hủy(destructor) không được kế thừa nhưng hàm tạo và hàm hủy mặc định luôn được gọi khi tạo hoặc hủy 1 đối tượng lớp dẫn xuất.
Nếu lớp cở sở không có hàm tạo mặc định hoặc bạn muốn gọi hàm tạo khi tạo 1 đối tượng ở lớp dẫn xuất, bạn có thể định nghĩa nó ở hàm tạo của lớp dẫn xuất theo cú pháp:

derived_constructor_name (parameters) : base_constructor_name (parameters) {...}
Ví dụ:

// constructors and derived classes
#include
using namespace std;

class mother {
public:
mother ()
{ cout << "mother: no parameters\n"; }
mother (int a)
{ cout << "mother: int parameter\n"; }
};

class daughter : public mother {
public:
daughter (int a)
{ cout << "daughter: int parameter\n\n"; }
};

class son : public mother {
public:
son (int a) : mother (a)
{ cout << "son: int parameter\n\n"; }
};

int main () {
daughter cynthia (0);
son daniel(0);

return 0;
}
Kết quả:
mother: no parameters
daughter: int parameter

mother: int parameter
son: int parameter
Bạn cần chú ý hàm tạo của daughter và son:
daughter (int a)          // nothing specified: call default
son (int a) : mother (a) // constructor specified: call this
Lưu ý: Chỉ có kế thừa trực tiếp thôi.
Nếu kết hợp tốt sự kế thừa và dùng hàm tạo, bạn có thể dùng nó để bẫy lỗi chương trình ( như kiểm tra tham số nhập vào chẳng hạn)

Sự kế thừa và các vấn đề liên quan- Phần 2

Xem thêm bài Sự kế thừa và các vấn đề liên quan- Phần 1
Trích:
• When a base class is inherited by use of public, its public members become public members of the derived class, and its protected members become protected members of the derived class.

• When a base class is inherited by use of protected, its public and protected members become protected members of the derived class.

• When a base class is inherited by use of private, its public and protected members become private members of the derived class.

• In all cases, private members of a base class remain private to the base class, and are not inherited.
Ở đây mình xin nêu những điểm ở bên ngoài thôi nhé: (chỉ nói về public và private, protected thì tương tự)

Sự kế thừa public:
Bạn xem thử 1 ví dụ:

Khi khai báo:
class alpha{};
      class  beta: public alpha{};
Và hàm void func(alpha a){}
Trong void main() ta viết:
Alpha a;
Beta b;
a=b;     //Đối tượng b được truyền vào a. Trường hợp ngược lại thì không.
func(b);            //Chấp nhận.
Ở đây câu lệnh: a=b được chấp nhận vì b là 1 loại đối tượng của a. Tương tự, chúng ta có thể truyền một đối tượng lớp dẫn xuất cho một hàm có đối số lớp cơ sở.
Lưu ý: câu lệnh b=a; không được vì a không phải là 1 loại đối tượng của b.
Sự kế thừa private:
Khi bạn viết
class beta: private alpha{};
Bạn sẽ không thể sử dụng bất kì biến hoặc hàm nào trong alpha dù được khai báo public.
Kết quả là toàn bộ lớp cơ sở ẩn đối với các đối tượng của lớp dẫn xuất. Đối với các lớp dẫn xuất có thể coi như không có lớp cơ sở.
Tốt hơn hết là bạn thay thế sự kế thừa private bằng sự hợp thành. (Bài trước mình đã nêu)
class alpha{};
class beta
{
    private:
                alpha obj;
};
Đối tượng alpha được đặt ở chế độ private nên nó vẫn che đậy đối với đối tượng beta. Sự hợp thành làm cho mối quan hệ 2 lớp rõ ràng hơn, ít phức tạp hơn và đối với lớp dẫn xuất cũng làm việc như vậy. Mình thử nêu 2 ví dụ:
Ví dụ 1: Kế thừa private
#include
using namespace std;
class alpha
{
      public:
      alpha()
      {      cout<<"Alpha\n";    } 
      void hello()
      {
           cout<<"Hello\n";    
      }
};
class beta: private alpha
{
      public:
      beta()
      {     cout<<"Beta\n";
            hello();
      }
};

int main()
{
    beta b;
    //b.hello();      Không hợp lệ vì class beta xem hello là phương thức private
    system("PAUSE");  
}
Ví dụ 2: Sự hợp thành
class alpha{/*....*/};
class beta
{
    private:
             alpha a;
    public:
             /*..
             a.hello();   //Sẽ rõ ràng hơn ví dụ 1
            ..*/
};

Sự kế thừa và các vấn đề liên quan- Phần 1

Bạn có thể tham khảo thêm bài Giới thiệu về lập trình hướng đối tượng
Ở bài viết này, mình tổng hợp và bổ sung những gì các cuốn sách còn thiếu. Đầu tiên mình sẽ nói 2 khái niệm cực kì quan trọng là kế thừahợp thành. Sau đó mình sẽ nói tiếp về phạm vi của sự kế thừa.
Sự kế thừa và thiết kế chương trình
Ngoài việc làm cho việc thay đổi các chương trình có sẵn dễ dàng hơn, sự kế thừa còn có 1 lợi ích khác. Nó cung cấp một cách để liên kết một thành phần chương trình này với một thành phần chương trình khác. Mối quan hệ mới này làm cho việc thiết kế chương trình linh động hơn và cấu trúc chương trình phản ánh mối quan hệ thế giới thực chính xác hơn. Sự kế hơn đôi khi còn gọi là quan hệ "loại". Để thấy được ý nghĩa này, đầu tiên chúng ta cùng xét một loại quan hệ khác là quan hệ hợp thành.

  1. Hợp thành: Một quan hệ "có":
    Nếu có một lớp employee ( nhân viên) và một trong những mục dữ liệu trong lớp này là tên của nhân viên thì chúng ta có thể nói rằng đối tượng employee "có" tên. Đối tượng employee cũng có thể "có" lương, mã nhân viên... Loại quan hệ này gọi là sự hợp thành bởi vì đối tượng employee là hợp của các biến trên.
    Dữ liệu thành viên của lớp có thể chưa đối tượng của lớp khác cũng như các biến của các kiểu dữ liệu cơ bản. Chúng ta có thể hình dung một lớp xe đạp bao gồm đối tượng khung, hai đối tượng bánh và 1 đối tượng tay lái.
    Sự hợp thành trong OOP mô hình hóa các tình huống thế giới thực trong đó các đối tượng là hợp thành của các đối tượng khác.
  2. Sự kế thừa: Một quan hệ "loại"
    Sự kế thừa trong OOP phản ánh khái niệm mà chúng ta gọi là sự khái quát quá trong thế giới thực. Nếu chúng ta có một chiếc xe đạp đua, một chiếc xe đạp địa hình và một chiếc xe đạp thiếu nhi thì chúng ta có thể nói những chiếc xe đạp này cụ thể rõ ràng của một khái niệm trổng quát hơn là xe đạp. Tất cả các loại xe đạp đều có đặc điểm: hai bánh, một khung... Nhưng một chiếc xe đạp đua ngoài các đặc điểm chung này còn có đặc điểm là lốp nhỏ và nhẹ. Một chiếc xe đạp địa hình cũng có tất cả những đặc điểm của một chiếc xe đạp, ngoài ra còn có lốp to, dày và phanh tốt.
  3. Không hoàn toàn là một gia tộc:
    Sự kế thừa được so sánh với mối quan hệ gia tộc, đó là lý do nó mang tên là sự kế thừa. Tuy nhiên sự kế thừa trong OOP không hoàn toàn giống sự kế thừa trong gia tộc loài người. Vì một điều, một lớp con có thể kế thừa một lớp cha. Lớp con (lớp dẫn xuất) có xu hướng có nhiều đặc điểm hơn lớp cha (lớp cơ sở) trong khi loài người thường hưởng những phẩm chất có sẵn từ bố và mẹ.
Cú pháp của sự kế thừa:
class base_class_name   //Lớp cơ sở
{/*....*/};
class derived_class_name: public base_class_name   //Lớp dẫn xuất
{ /*...*/ };
Từ khóa public có thể thay thế bằng protected hoặc private. Các thành viên trong cùng một lớp có nhiều quyền truy cập hơn là thành viên lớp dẫn xuất.

Access
public
protected
private
members of the same class
yes
yes
yes
members of derived classes
yes
yes
no
not members
yes
no
no
Nếu chúng ta xác định mức độ truy cập là protected, mọi thành viên public trong lớp cơ sở sẽ được thừa kế thành proctected trong lớp dẫn xuất. Tương tự đối với từ khóa private.

Thứ Hai, 14 tháng 9, 2009

Skip List - đối thủ của cây cân bằng

Khi đề cập đến bài toán tìm kiếm chắc bạn đã ít nhiều biết đến cấu trúc cây cân bằng (balanced tree). Cấu trúc này cho phép thực hiện tìm kiếm với độ phức tạp trung bình là O(log2n). Tuy nhiên, để tránh tình trạng cây suy biến, ta phải cân bằng cây mỗi khi chèn một nút mới. Nói chung, cân bằng cây là một thao tác tương đối phức tạp do phải hoán chuyển nhiều nút và do đó cũng ảnh hưởng đến hiệu suất. Skip List giải quyết vấn đề này khá hiệu quả mà vẫn không ảnh hưởng đáng kể đến tốc độ của phép tìm kiếm.

SkipList được giáo sư William Pugh thuộc trường đại học MaryLand giới thiệu vào khoảng tháng 8 năm 1989 trong một bài báo tham dự hội nghị về thuật toán và cấu trúc dữ liệu tại Ottawa Canada.



Skip List là một danh sách liên kết đơn mở rộng

Skip List chỉ là một mở rộng của danh sách liên kết đơn mà chúng ta đã rất quen thuộc. Hình bên dưới minh họa một danh sách liên kết đơn được sắp xếp tăng dần theo khóa.

Để tìm vị trí của một phần tử x trong danh sách liên kết đơn (đã được sắp xếp), ta phải duyệt từ đầu danh sách cho đến khi gặp nút có khóa cần tìm hoặc gặp nút có khóa lớn hơn khóa cần tìm. Trường hợp xấu nhất là phải duyệt qua tất cả nút trong danh sách (trong trường hợp khóa cần tìm lớn hơn tất cả các khóa trong danh sách).
Ý tưởng sơ khởi của Skip List là: để tăng hiệu quả của phép tìm kiếm, ta sẽ thêm vào các con trỏ tại một vài nút cho phép trỏ đến những nút nằm ở “xa” hơn. Chẳng hạn như ở hình dưới đây, các nút 5, 9, 16, 25 sẽ có các con trỏ phụ trỏ đến nút kế tiếp thứ hai (hay nói cách khác là “nhảy” – skip – hai nút một lần)

Với cấu trúc kiểu này, bạn có thể dễ dàng cảm nhận được là trung bình ta sẽ tiết kiệm được một nửa số bước tìm kiếm so với danh sách liên kết đơn bình thường. Nhìn vào hình ảnh, bạn sẽ thấy danh sách của ta bây giờ giống như có hai tầng (level). Tầng 1 là danh sách bình thường. Tầng 2 ở trên cũng là một danh sách liên kết đơn gồm có 5, 9, 16, 25 (nhảy 2 nút).

Dĩ nhiên là chúng ta có thể thêm vào một tầng nữa gồm các phần tử 9,25 (nhảy 4 nút) như hình dưới đây:

Hình ảnh
Để tìm kiếm một phần tử, ta xuất phát từ tầng cao nhất, duyệt qua các nút ở tầng hiện tại cho đến khi nút kế tiếp có khóa lớn hơn nút cần tìm. Lúc đó ta sẽ giảm đi một tầng. Nếu tầng hiện tại là 1 mà nút kế tiếp có giá trị lớn hơn nút cần tìm thì có nghĩa là nút cần tìm không có trong danh sách.
Hình trên minh họa cho quá trình tìm kiếm giá trị khóa 18. Ta khởi đầu với tầng 3, ta lần theo các nút ở tầng 3 cho đến khi gặp nút 9. Ta thấy nút kế tiếp là 25 lớn hơn 18 nên ta sẽ “xuống” tầng 2. Theo con trỏ ở tầng 2, ta sẽ đến nút 16. Nút kế tiếp là 25 lớn hơn 18 nên ta sẽ “xuống” tầng 1. Theo con trỏ ở tầng 1 ta gặp nút 18 chính là khóa cần tìm.

Hình ảnh
Một cách cảm tính là nếu như mỗi tầng ta giảm đi một nửa số phần tử thì cấu trúc này sẽ cho thời gian tìm kiếm y hệt như cây nhị phân tìm kiếm. Tuy nhiên, cấu trúc mở rộng này cũng gặp cùng một vấn đề như cây nhị phân là khi chèn một phần tử mới vào, ta lại phải mất công biến đổi để đảm bảo được “cấu trúc” của nó.

Skip List là cấu trúc dữ liệu có xác suất

Điểm chính giúp tốc độ tìm kiếm trung bình được tăng lên là do chúng ta giảm số nút ở mỗi tầng (để thực hiện phép tìm kiếm bằng cách nhảy). Tính chất “cách đều” chỉ đảm bảo được hiệu quả tìm kiếm lúc nào cũng như nhau.
Như vậy ta chỉ cần đảm bảo một tính chất là: số phần tử ở tầng trên phải xấp xỉ bằng một nửa (không cần chính xác) của tầng dưới. Nếu có 3 tầng, 100% phần tử ở tầng 1, khoảng 50% phần tử ở tầng 2, khoảng 25% phần tử ở tầng 3 và cứ thế. (Số nút ở hai tầng cao nhất sẽ xấp xỉ bằng nhau)

Hình ảnh
Sự đơn giản hóa này sẽ khiến việc chèn một phần tử vào danh sách trở nên rất đơn giản. Quan trọng nhất là chúng ta sẽ không cần phải sắp xếp lại danh sách nữa.

Tuy nhiên, để tiện cài đặt, ta sẽ dùng thuật ngữ khác đi một tí. Thay vì nhìn danh sách theo tầng, ta sẽ dùng khái niệm cấp của nút. Nếu một nút chỉ có một con trỏ, nó sẽ có cấp 1, nếu có hai con trỏ, nó có cấp 2 và cứ thế. Một cách tổng quát, sẽ có khoảng 50% số nút có cấp 1, 25% số nút có cấp 2, 12.5% số nút có cấp 3 và cứ thế.
Để chèn một nút vào danh sách, ta phát sinh ngẫu nhiên cấp của nó theo nguyên tắc là 50% khả năng có cấp 1, 25% có cấp 2, 12.5% có cấp 3 và cứ thế. Sau đó, tìm vị trí cần chèn (theo thuật toán tìm kiếm) rồi chỉ việc điều chỉnh lại các con trỏ là xong.

Phát sinh ngẫu nhiên cấp của một nút

int GenerateLevel(int max_level)
{
level=1;
while ((rand() < 0.5) && (level < max_level))
level++;
return level;
}

Để hiểu hoạt động của hàm này, ta chú ý đến biểu thức

rand() < 0.5

Hàm rand() phát sinh một số thực không âm ngẫu nhiên nhỏ hơn một. Nghĩa là 0≤rand() <1. Xác suất để biểu thức rand()<0.5 đúng hoặc sai đều là 50%.

Dễ dàng thấy được là xác suất để (level = 1 là đúng) là 50%.

Để có level = 2, biểu thức rand()<0.5 phải sai với level = 1 và đúng với level = 2. Xác suất để có level = 1 là sai là 50% và xác suất để có level = 2 là đúng cũng là 50%. Vậy xác suất để có level = 2 là đúng là : 50%×50% = 25%.

Tương tự, để có level = 3, biểu thức rand()<0.5 phải sai với level = 1 và sai với level = 2 và đúng với level = 3. Xác suất để có điều này là: 50%×50%×50% = 12.5%.

Hàm này phụ thuộc vào giá trị max_level (số tầng tối đa của SkipList). Một cách cảm tính, từ cây nhị phân cân bằng, ta thấy rằng max_level nên là log2(n) với n là tổng số nút của danh sách. Để tránh bị phụ thuộc vào số lượng nút, ta có thể dùng hằng số log2(N) trong đó N là chặn trên của n. Trên thực tế max_level = 32 (cho một list có tối đa khoảng 2^32 ~ 4 tỷ nút) là đủ dùng cho hầu hết các trường hợp.

Tìm kiếm

SkipListNode* Search(SkipList *list, int searchKey)
{
x = list->head;
// điều kiện dừng: (x->forward[i]->key >= searchKey) và (level=1)
for (i=list->level;i>1;i--)
while (x->forward[i]->key < searchKey)
x = x->forward[i];
x = x->forward[1];
if (x->key == searchKey)
return x;
else
return 0;
}

Chèn nút vào danh sách

void Insert(SkipList *list, int searchKey, int newValue)
{
SkipListNode* update[max_level+1];
//tìm vị trí nút cần chèn và ghi nhận các nút
//trỏ đến vị trí này
x = list->head;
for (i=list->level;i>1;i--)
{
while (x->forward[i]->key < searchKey)
x = x->forward[i];
update[i] = x; //ghi nhận nút trỏ đến vị trí chèn
}

x = x->forward[1];
if (x->key == searchKey)
x->value = newValue; //cập nhật giá trị nút nếu khóa đã tồn tại
else
{
newLevel = GenerateLevel(max_level);
if (newLevel > list->level)
{
for (i=list->level + 1;i<=newLevel;i++)
update[i] = list->head;
list->level = newLevel;
}

// tạo một nút mới với cấp = , khóa =
x = makeNode(newLevel,searchKey,value);
// cập nhật các nút trỏ đến nút mới và các con trỏ forward của nút mới
for (i=1;i<=newLevel;i++)
{
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
}
}

Khởi tạo SkipList

SkipList rỗng bao gồm hai nút đặc biệt. Một nút head và nút tail (hay NIL). Cấp của SkipList lúc khởi tạo là 1. Khóa của tail phải luôn luôn lớn hơn khóa của tất cả các nút.
Hình ảnh
Xóa một nút

void Delete(SkipList * list, int searchKey)
{
SkipListNode* update[max_level+1];
//tìm nút cần xóa và ghi nhận các nút
//trỏ đến nút này
SkipListNode *x = list->head;
for (i=list->level;i>1;i--)
{
while (x->forward[i]->key < searchKey)
x = x->forward[i];
update[i] = x; //ghi nhận các nút trỏ đến nút cần xóa
}
x = x->forward[1];
if (x->key == searchKey)
{
for (i=1;i<=list->level;i++)
{
if (update[i]->forward[i] != x) break;
update[i]->forward[i] = x->forward[i];
}
free(x);
while ( (list->level > 1) && (list->head->forward[list->level] == 0) )
list->level--;
}
}

Một số bàn luận

Thay vì giới hạn ở hằng số 50% cho biết số lượng nút cấp i+1 bằng khoảng 50% số lượng nút cấp i. Pugh đề nghị kết hợp một giá trị p < 1 vào SkipList để quy định tỷ lệ nút giữa các tầng. Nếu p=0.5, tầng i+1 sẽ có số nút bằng khoảng ½ so với tầng i. Nếu p=0.25, tầng i+1 sẽ có số nút bằng khoảng ¼ so với tầng i (nếu p=1/4, số nút cấp 1 sẽ là 75%, số nút cấp 2 là 25%×75% = 18.75%, số nút cấp 3 sẽ là: 25%×25%×75% = 4.6875%).

Rõ ràng là vì các nút không cách đều nhau nên hiệu suất tìm kiếm sẽ không là hằng số. Nếu quá xui, chẳng hạn như trường hợp tất cả nút đều có cùng cấp, thì hiệu suất tìm kiếm sẽ bị giảm đáng kể. Tuy nhiên, bằng toán học, giáo sư Pugh đã tính ra được ràng khả năng xảy ra những trường hợp xấu như vậy là rất thấp. Chẳng hạn với một SkipList có 4096 nút, p=0.5, khả năng để một thời gian tìm kiếm chậm hơn 3 lần (so với thời gian tìm kiếm trung bình) là một phần 200 triệu !
Một điểm cần lưu ý nữa là khi số lượng nút càng lớn (điều thường xảy ra trong thực tế), hiệu quả trung bình của SkipList càng sát với hiệu suất của cây cân bằng. Pugh cũng đề nghị nên sử dụng p=0.25 trong đa số trường hợp. Nếu như chúng ta đặt nặng tính ổn định của SkipList thì Pugh đề nghị nên dùng p=0.5. Điểm thú vị là với p=0.25, một nút trung bình chỉ có 1.33 con trỏ (trong khi đó cây cân bằng có 2 con trỏ). Điều này làm cho SkipList sử dụng ít bộ nhớ hơn cây cân bằng.

Tài liệu tham khảo

1) William Pugh, A Skip List Cook Book, 6/1990, Department of Computer Science, University of MaryLand, College Park.

2) Thomas A. Anastasio, Skip Lists, 22/12/1999, http://www.csee.umbc.edu/courses/underg ... lists.html

3) Xem ví dụ được thể hiện bằng đồ họa với Skip List Apple, http://iamwww.unibe.ch/~wenger/DA/SkipList/

Nguồn Codepro.vn

Chủ Nhật, 13 tháng 9, 2009

1 đồng nữa biến đi đâu?

"Có ba người vào khách sạn thuê phòng. Họ thuê chung một phòng với giá 30 đồng. Như vậy, mỗi người chỉ phải trả góp 10 đồng. Tên bồi cầm 30 đồng khách đóng đến nộp cho ông chủ khách sạn. Nhưng ông chủ chỉ lấy 25 đồng và bảo tên bồi đem trả lại cho khách 5 đồng. Cầm lấy 5 đồng, tên bồi nghĩ hoài vẫn không biết làm sao chia đều cho ba người, cuối cùng, nó giấu đi 2 đồng, và với 3 đồng còn lại nó trả đều cho mỗi người khách một đồng...

Như vậy là mỗi người khách chỉ đóng có 9 đồng. Chín đồng nhân với 3 người khách thành 27 đồng, đúng chưa? Hai bảy đồng cộng với hai đồng tên bồi giấu đi là 29 đồng cả thảy. Vậy còn 1 đồng nữa biến đi đâu ? "

Thứ Bảy, 12 tháng 9, 2009

Autorun Typhoon Professional



Autorun Typhoon Professional Tạo những bài thuyết trình CD/DVD chuyên nghiệp, an toàn. Giao diện dễ sử dụng của Autorun và hệ thống giúp đỡ trong giúp việc tạo các đề án nhanh chóng và dễ dàng. Từ việc đảm bảo các tập tin của bạn sẽ hoạt động trên mọi hệ thống, đến việc tạo các Menus và Buttons như ý, Autorun sẽ thay bạn làm tất cả. Nếu bạn muốn đem các Menu, trang web, tập tin PDF, bài thuyệt trình bằng PowerPoint, video, SlideShow của bạn, hay bất cứ gì vào một đĩa CD-ROM, Autorun là một công cụ hoàn hảo mà bạn cần.


Tạo Menus và Buttons như ý:
* Kéo-và-thả Menu Creator của Autorun cho phép bạn tạo bất kỳ Menu nào bạn muốn. Menu Themes sẽ giúp công việc của bạn nhẹ bớt phần nào.
* Bạn có thể tạo Button riêng cho mình hoặc chọn từ 14 button có sẵn.

Bảo vệ tập tin và nội dung của bạn. Autorun có 2 loại bảo vệ: cho đĩa và tập tin.
* Disk Security sẽ không cho phép người khác xem CD của bạn trừ phi bạn đồng ý. Có 6 loại bảo vệ bao gồm Serial Number, License Agreement, và CD Expiry Date.
* File Security không cho những tập tin đề án của bạn bị nén lại thành một tập tin duy nhất. Những tập tin của bạn sẽ được trích ra khi cần thiết và được xóa ngay sau đó. Điều này ngăn chặn người khác xem nội dung trong CD của bạn.

Bảo đảm các tập tin của bạn được xem bởi số khán giả cao nhất
* Không phải tất cả các người tiêu dùng đều có phần mềm mới nhất hay giữ phiên bản của họ cập nhật. Failsafe Program sẽ giải quyết vấn đề này. Autorun sẽ phát hiện khi một tập tin không thể chạy và cài đặt một phần mềm thích hợp để các khan giả của bạn xem được đề án.
* Ví dụ cụ thể là mở một tập tin PDF, với Acrobat như Failsafe Program.

10 Viewer mạnh để chọn
* Autorun có 10 Viewer để giúp bạn tạo hầu hết các đề án mà bạn hằng mong ước.: File, Splash Screen, Folder, Web Page, Slide Show, Music JukeBox, Video Player, Flash Animations, Zip Extractor and Menu Creator.
* Hệ thống giúp đỡ có sẵn sẽ chỉ dẫn bạn một cách chi tiết khi tạo các đế án và giải thích cặn kẽ mọi thứ chỉ với một cái nhấn chuột.

Hỗ trợ :

Windows Vista
Windows 2003
Windows XP
Windows 2000
Windows ME
Windows NT 4.0
Windows 98 SE
Windows 98
Windows 95

Cách sử dụng rất đơn giản, các công cụ cần thiết để tạo Autorun được hiện rõ trên cửa sổ của chương trình:

- CD-ROM Base Folder: chọn thư mục chứa file cần tạo autorun.
- Target File(s): chọn file muốn cho autorun khi nạp CD-ROM vào.
– CD-ROM Icon: chọn biểu tượng cho CD.
- Splash Screen: tạo một file hình, nhạc chào mừng trước khi chạy ứng dụng của bạn.
- Project Security: tạo mật khẩu.
- Additional Viewer Checking: nếu bạn lo lắng CD-ROM của bạn khi đưa qua máy khác sẽ không xem được (vì máy đó không có phần mềm để xem), bạn có thể bổ sung chương trình của bạn vào CD rồi sử dụng mục này để chỉ định file chương trình.

Download bản Portable
http://www.mediafire.com/?sbmjm1oyhcu

Bài đăng phổ biến