Hotline: 024.62511017

024.62511081

  Trang chủ   Sản phẩm   Phần mềm Dành cho nhà trường   Phần mềm Hỗ trợ học tập   Kho phần mềm   Liên hệ   Đăng nhập | Đăng ký

Tìm kiếm

School@net
 
Xem bài viết theo các chủ đề hiện có
  • Hoạt động của công ty (726 bài viết)
  • Hỗ trợ khách hàng (498 bài viết)
  • Thông tin tuyển dụng (57 bài viết)
  • Thông tin khuyến mại (80 bài viết)
  • Sản phẩm mới (216 bài viết)
  • Dành cho Giáo viên (549 bài viết)
  • Lập trình Scratch (3 bài viết)
  • Mô hình & Giải pháp (156 bài viết)
  • IQB và mô hình Ngân hàng đề kiểm tra (127 bài viết)
  • TKB và bài toán xếp Thời khóa biểu (242 bài viết)
  • Học tiếng Việt (183 bài viết)
  • Download - Archive- Update (289 bài viết)
  • Các Website hữu ích (70 bài viết)
  • Cùng học (92 bài viết)
  • Learning Math: Tin học hỗ trợ học Toán trong nhà trường (78 bài viết)
  • School@net 15 năm (154 bài viết)
  • Mỗi ngày một phần mềm (7 bài viết)
  • Dành cho cha mẹ học sinh (124 bài viết)
  • Khám phá phần mềm (122 bài viết)
  • GeoMath: Giải pháp hỗ trợ học dạy môn Toán trong trường phổ thông (36 bài viết)
  • Phần mềm cho em (13 bài viết)
  • ĐỐ VUI - THƯ GIÃN (363 bài viết)
  • Các vấn đề giáo dục (1210 bài viết)
  • Bài học trực tuyến (1037 bài viết)
  • Hoàng Sa - Trường Sa (17 bài viết)
  • Vui học đường (275 bài viết)
  • Tin học và Toán học (220 bài viết)
  • Truyện cổ tích - Truyện thiếu nhi (180 bài viết)
  • Việt Nam - 4000 năm lịch sử (97 bài viết)
  • Xem toàn bộ bài viết (8223 bài viết)
  •  
    Đăng nhập/Đăng ký
    Bí danh
    Mật khẩu
    Mã kiểm traMã kiểm tra
    Lặp lại mã kiểm tra
    Ghi nhớ
     
    Quên mật khẩu | Đăng ký mới
     
    Thành viên có mặt
    Khách: 5
    Thành viên: 0
    Tổng cộng: 5
     
    Số người truy cập
    Hiện đã có 89517167 lượt người đến thăm trang Web của chúng tôi.

    Số bè bạn và số hoàn hảo

    Ngày gửi bài: 07/05/2008
    Số lượt đọc: 3452

    Trong quá trình học lập trình và thuật toán, đa số trong chúng ta đã từng nghe tới và làm các bài tập về các số bè bạn (friend numbers) và số hoàn hảo (perfect numbers). Các số bè bạn n, m là hai số nguyên mà tổng ước số của n bằng m và tổng các ước của m bằng n, ví dụ như cặp số bè bạn nhỏ nhất là 220 và 284. Các số hoàn hảo là các số mà tổng các ước thực sự của nó bằng chính nó, ví dụ như các số hoàn thiện đầu tiên là 6, 28 ...

    Bài toán thường liên quan tới các số bè bạn và số hoàn hảo là in ra tất cả các cặp số bè bạn và các số hoàn hảo không vượt quá một số nguyên N nào đó. Đây là hai bài toán khác nhau song lại có liên quan mật thiết với nhau vì trong quá trình xây dựng giải thuật cho bài toán chúng ta đều cần đến khái niệm gọi là tổng các ước của một số nguyên. Trong trường hợp N là một số nguyên nhỏ (N < 10000) thì một thuật toán đơn giản và hết sức trực quan cũng tạm chấp nhận được là:

    • Xây dựng một hàm tính tổng ước của một số nguyên

    • Duyệt các số từ 2 tới N, nếu tồn tại hai số i, j khác nhau mà tổng ước của i bằng j và tổng ước của j bằng i thì in ra i, j. Khi đó i, j chính là một cặp số bè bạn.

    • Duyệt các số từ 2 tới N, nếu tổng ước của số đó bằng chính nó thì in ra màn hình, đó chính là một số hoàn thiện cần tìm.

    Trước hết ta đi xây dựng hàm tính tổng các ước số của một số nguyên m: ta đã biết các ước số của m, nếu có sẽ không vượt quá căn bậc 2 của m (tức là sqrt(m)), và khi a là một ước của m thì m/a (m chia a) cũng là một ước của m (phần này xin đọc thêm bài báo “Số nguyên tố” để biết thêm chi tiết) nên một thuật toán hiệu quả sẽ xét các ứng cử viên có thể là ước của m nằm trong khoảng 1 tới sqrt(m) (gọi ước này là a), sau đó cộng a và m/a vào tổng ước của số m. Cài đặt cụ thể bằng ngôn ngữ C của thuật toán như sau:

    Sau khi đã có hàm tính tổng ước số của một số nguyên, việc in ra các số hoàn thiện trở nên dễ dàng với 1 vòng lặp, nên ta chỉ xét đoạn chương trình in ra các cặp số hoàn thiện phân biệt (không kể thứ tự của các số):

    Hầu hết các lập trình viên mới học lập trình và thuật toán sẽ dừng lại ở đây. Thuật toán in ra các cặp số bè bạn có hai vòng lặp lồng nhau, thuật toán sẽ chạy cỡ khoảng O(N) = N*(N+1)/2 bước, mỗi bước gọi tới hàm tong_uocso() hai lần để thực hiện so sánh nên độ phức tạp của thuật toán sẽ là O(N) = N2 * O(hàm tong_uocso()). Nếu N = 1000000 thì chắc chắn thuật toán không thể chạy trong thời gian 3 giây (thời gian test tối đa của các bài toán trong các cuộc thi lập trình).

    Tuy nhiên quan sát kỹ thuật toán, ta thấy rằng mỗi lần xét i trong vòng lặp thứ nhất, ta không cần thiết phải chạy một vòng lặp để xét các ứng cử viên có thể làm thành một cặp số bè bạn với i, mà chỉ cần xét số z = tong_uocso(i). Vì vậy một thuật toán hiệu quả hơn nhiều sẽ là như sau:

    Ta cần thêm điều kiện z > i để in ra các cặp số khác nhau (vì z bây giờ đóng vai trò thay j trong thuật toán trước).

    Rõ ràng thuật toán thứ hai này có độ phức tạp nhỏ hơn nhiều so với thuật toán thứ nhất, thay vì chạy hai vòng lặp, ta chỉ cần một vòng lặp là đủ. Tất nhiên cải tiến này không thể áp dụng cho bài toán số hoàn hảo vì ta cũng chỉ cần một vòng lặp để in ra tất cả các số hoàn hảo không vượt quá N.

    Rõ ràng bây giờ các cải tiến của chúng ta, nếu có, sẽ không thể tập trung vào các vòng lặp, mà chỉ có thể nằm ở việc tính tổng ước số của mỗi số nguyên.

    Chúng ta đã biết thuật toán sàng Erastosthene để đánh dấu các số nguyên không phải là số nguyên tố (các số còn lại không được đánh dấu sẽ là số nguyên tố - có thể tham khảo trong bài báo số nguyên tố). Thuật toán sàng Erastosthene dựa trên một chi tiết hết sức tinh tế sau: nếu z là một số nguyên tố (hay hợp số) thì các bội số của z sẽ là các hợp số. Chúng ta hoàn toàn có thể áp dụng chi tiết này cho việc tính tổng ước số của các số nguyên: với bất kỳ giá trị z (z  N) nào, z sẽ là ước số của các bội số của z nên ta sẽ cộng z với các biến chứa các tổng các ước số tương ứng với các bội số của z đó. Cũng tương tự như thuật toán sàng Erastosthene, ta sẽ sử dụng một mảng để lưu tổng ước cho tất cả các số nguyên không vượt quá N. Cụ thể thuật toán được cài đặt bằng ngôn ngữ C như sau:

    Thực ra trong cài đặt trên, chúng ta có thể thay vòng lặp while trong thân vòng lặp for chính bằng một vòng lặp for, nhưng khi đó sẽ cần sử dụng các phép nhân, chậm hơn so với dùng vòng lặp while và phép cộng tích lũy vào biến z.

    Để in ra các số hoàn hảo chúng ta có thể áp dụng thuật toán tương tự với một chút sửa đổi nho nhỏ, và phần này xin dành cho bạn đọc như một bài tập.

    Chúng ta cũng có thể đo thời gian thực hiện của các thuật toán 1, 2, 3 ở trên để thấy được hiệu quả khác nhau của chúng, chẳng hạn như sau:

                float time;

                time_t start, finish; // kiểu time_t nằm trong thư viện time.h

                start = clock(); // hàm tính thời gian clock() nằm trong thư viện time.h

                sobeban1(n);

                finish = clock();

                time = (finish - start)/CLK_TCK; // hằng số CLK_TCK nằm trong thư viện time.h

    Việc tính thời gian của các thuật toán 2, 3 được thực hiện tương tự. Tôi đã chạy thử nghiệm chương trình với DevCpp và kết quả là thuật toán 3 có thể chạy tới N = 2000000 trong vòng 2 giây.

    Chú thích:

    1. O(N): ký hiệu độ phức tạp của thuật toán, thường là số phép toán thực hiện của thuật toán, với dữ liệu input có kích thước là N

    Mặc dù đã hết sức thận trọng và xem xét kỹ các ví dụ đưa ra trong bài viết, tuy vậy vẫn có thể không tránh khỏi các sai sót, rất mong nhận được sự đóng góp ý kiến của các bạn độc giả. Mọi góp ý, thắc mắc xin gửi về địa chỉ email: tuannhtn@yahoo.com.

    School@net (Theo THNT)



     Bản để in  Lưu dạng file  Gửi tin qua email


    Những bài viết khác:



    Lên đầu trang

     
    CÔNG TY CÔNG NGHỆ TIN HỌC NHÀ TRƯỜNG
     
    Phòng 804 - Nhà 17T1 - Khu Trung Hoà Nhân Chính - Quận Cầu Giấy - Hà Nội
    Phone: 024.62511017 - 024.62511081
    Email: kinhdoanh@schoolnet.vn


    Bản quyền thông tin trên trang điện tử này thuộc về công ty School@net
    Ghi rõ nguồn www.vnschool.net khi bạn phát hành lại thông tin từ website này
    Site xây dựng trên cơ sở hệ thống NukeViet - phát triển từ PHP-Nuke, lưu hành theo giấy phép của GNU/GPL.