Thời gian chạy của thuật toán đệ quy này là gì?


8

Tôi đã thực hiện chương trình Haskell (chưa được xử lý) sau đây cho thử thách chơi gôn mã khi tính toán n giá trị đầu tiên của A229037 .

Đây là giải pháp đề xuất của tôi để tính giá trị thứ :n

a n | n<1        = 0 
    | n<3        = 1
    | otherwise  = head (goods n)

goods n = [x | x <- [1..], isGood x n]

isGood x n = and [ x - a(n-k) /= a(n-k) - a(n-k-k) || a(n-k-k) == 0 | k <- [1..n] ]

Lưu ý rằng Haskell không tự động lưu trữ hoặc ghi nhớ các giá trị này.

Trang OEIS cho chuỗi cung cấp thực tế là , do đó, có thể được thay thế bởi vì thuật toán sẽ không bao giờ đạt đến lớn hơn .x n + 1a(n)(n+1)/2[1..][1..(n+1)/2]xn+12

Cố gắng đếm các cuộc gọi hàm, tôi đã rút ra giới hạn trên , số lượng cuộc gọi hàm mà thuật toán thực hiện cho một đầu vào :nT(n)n

T(n)=x=1(n+1)/2k=1n2 T(nk)+2 T(n2k)x=1(n+1)/2k=1n T(nk)x=1(n+1)/2k=1n4 T(n1)x=1(n+1)/24 n T(n1)4 n T(n1) n+122 n (n+1) T(n1))

Tôi đã cắm công thức cuối cùng vào Mathematica:

RSolve[{T[n] == 2*T[n - 1]*n*(n + 1), T[1] == 1}, T[n], n]

Và nhận được, sau một chút đơn giản hóa:T(n) 2n n! (n+1)!

Tỷ lệ trung bình giữa thời gian thực hiện chương trình Haskell này, với là và độ lệch chuẩn của các tỷ lệ là khoảng . (Thật kỳ lạ, biểu đồ nhật ký của các tỷ lệ dường như là một đường thẳng).2.0 10 39 6.0 10 39n[12,20]2.010396.01039

Các tỷ lệ với dòng đầu tiên, xác định , có độ lệch trung bình và tiêu chuẩn lần lượt là và , nhưng cốt truyện của nó nhảy rất nhiều.T(n)4.81061.8106

Làm thế nào tôi có thể có được một ràng buộc tốt hơn về độ phức tạp thời gian của thuật toán này?

Đây là thuật toán trong C hợp lệ (trừ khai báo chuyển tiếp), mà tôi tin là gần tương đương với mã Haskell:

int a(int n){
    if (n < 1) {
        return 0;
    } else if (n < 3) {
        return 1;
    } else {
        return lowestValid(n);
    }
}

int lowestValid(int n){
    int possible = 1; // Without checking, we know that this will not exceed (n+1)/2

    while (notGood(possible, n)) {
        possible++;
    }
    return possible;
}

int notGood(int possible, int n){
    int k = 1;

    while (k <= n) {
        if ( ((possible - a(n-k)) == (a(n-k) - a(n-2*k))) && (a(n-2*k) != 0) ) {
            return 1;
        } else {
            k++;
        }
    }
    return 0;
}

Phiên bản C mất khoảng 5 phút để tính toán và phiên bản Haskell mất khoảng tương tự cho .a(17)a(19)

Lần đầu tiên của các phiên bản:

Haskell: [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0e-2,3.0e-2,9.0e-2,0.34,1.42,11.77,71.68,184.37,1815.91]
C:       [2.0e-6, 1.0e-6, 1.0e-6, 2.0e-6, 1.0e-6, 6.0e-6, 0.00003,0.00027, 0.002209, 0.005127, 0.016665, 0.080549, 0.243611, 0.821537, 4.56265, 24.2044, 272.212]

Tôi đã thay đổi thẻ và tiêu đề để làm rõ rằng đây là phân tích thuật toán, không phải là câu hỏi lý thuyết phức tạp. "Giả sử rằng phép nhân và phép cộng là không đáng kể" - bạn có thể không? Thật không Vẫn tốt hơn để nói những gì bạn đang đếm, bởi vì rất có thể bạn không đếm được hầu hết mọi thứ. Xem thêm câu hỏi tham khảo của chúng tôi .
Raphael

O(1)Ta

Cuối cùng, sử dụng các phương pháp phù hợp để rút ra giới hạn Landau có lẽ là vô ích. Bất kỳ chức năng như vậy chỉ có thể phù hợp với một bộ chức năng cố định; Tôi đoán rằng Mathicala đã sử dụng ở các mô hình hàm mũ tồi tệ nhất ở đó, do đó đã làm một công việc tồi tệ để nắm bắt sự tăng trưởng theo cấp số nhân.
Raphael

O(n22)

Câu trả lời:


2

T(n)= =(n+1)(T(n-1)+2T(n-2)+T(n-3)+2T(n-4)+).
T(n)(n+1)T(n-1)T(n)
T(n1)+2T(n2)+T(n1)[1+2n+1n(n1)+2n(n1)(n2)+]=(1+O(1/n))T(n1).
T(n)(n+O(1))T(n1).
T(n)=O((n+O(1))!),
T(n)=O(nO(1)(n/e)n).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.