n log n = c. Một số xấp xỉ tốt của điều này là gì?


7

Tôi hiện đang xem xét ký hiệu Big O và độ phức tạp tính toán.

Vấn đề 1.1 trong CLRS hỏi điều gì có vẻ là một câu hỏi cơ bản, đó là để có được một trực giác về mức độ phức tạp thuật toán khác nhau tăng theo kích thước của đầu vào.

Câu hỏi đặt ra:

Đối với mỗi hàm và thời gian trong bảng sau, hãy xác định kích thước lớn nhất của một vấn đề có thể giải quyết trong thời gian , giả sử rằng thuật toán để giải quyết vấn đề mất micro giây.f(n)tntf(n)

Các khoảng thời gian là 1 giây, 1 phút, 1 giờ, 1 ngày, 1 tháng, 1 năm, 1 thế kỷ.

Các hàm có vẻ phức tạp về thời gian thường xuất hiện trong các thuật toán, danh sách này là:f(n)

log2n,n,n,nlog2n,n2,n3,2nandn!

Hầu hết là các thao tác đại số khá đơn giản. Tôi đang vật lộn với hai trong số này, và cả hai vì cùng một lý do:

Nếu là thời gian tính bằng micro giây, thì hai thứ tôi đang vật lộn là c

nlog2n=c
n!2πn(ne)n=c

ChoTôi đã nghĩ đến việc sử dụng xấp xỉ của Stirling.n!

Cả hai đều yêu cầu khả năng giải quyết , với Stirling yêu cầu thao tác nhiều hơn một chút.nlog2n=c

Câu hỏi

  1. Vì không thể giải được bằng các hàm cơ bản (chỉ Lambert W ), một số cách tốt để xấp xỉ gì? Hay làm thế nào để chúng ta thực hiện Lambert W?nlog2nnlog2n
  2. Làm thế nào để chúng ta giải quyết n! = c, nhất thiết phải xấp xỉ khi n phát triển lớn. Có phải đang đi đúng hướng và nếu vậy thì làm thế nào để giải quyết2πn(ne)n=c

Đây là một số mã python tôi đặt cùng nhau để hoàn thành bảng với đầu ra hiện tại của tôi:

EDIT: Dựa trên một vài câu trả lời, tôi đã sử dụng phương pháp tìm kiếm nhị phân (ngoại trừ lg n). Tôi đã chỉnh sửa mã dưới đây để phản ánh điều này:

+---------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
| f(n)    |    1 sec    |    1 min    |    1 Hour   |    1 Day    |   1 Month   |    1 Year   |  1 Century  |
+---------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
| lg n    | 2^(1.0E+06) | 2^(6.0E+07) | 2^(3.6E+09) | 2^(8.6E+10) | 2^(2.6E+12) | 2^(3.2E+13) | 2^(3.2E+15) |
| sqrt(n) |   1.0E+12   |   3.6E+15   |   1.3E+19   |   7.5E+21   |   6.7E+24   |   9.9E+26   |   9.9E+30   |
| n       |   1.0E+06   |   6.0E+07   |   3.6E+09   |   8.6E+10   |   2.6E+12   |   3.2E+13   |   3.2E+15   |
| n log n |    62746    |   2.8E+06   |   1.3E+08   |   2.8E+09   |   7.2E+10   |   8.0E+11   |   6.9E+13   |
| n^2     |     1000    |     7745    |    60000    |    293938   |   1.6E+06   |   5.6E+06   |   5.6E+07   |
| n^3     |     100     |     391     |     1532    |     4420    |    13736    |    31593    |    146645   |
| 2^n     |      19     |      25     |      31     |      36     |      41     |      44     |      51     |
| n!      |      9      |      11     |      12     |      13     |      15     |      16     |      17     |
+---------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+

Mã Python:

import math
import decimal
from prettytable import PrettyTable

def binary_search_guess(f, t, last=1000):
    for i in range(0, last):
        guess = pow(2,i)
        if f(guess) > t:
            return binary_search_function(f, pow(2,i-1), guess, t)

    return -1 

def binary_search_function(f, first, last, target):
    found = False

    while first<=last and not found:
        midpoint = (first + last)//2
            if f(midpoint) <= target and f(midpoint+1) > target:
                found = True
            else:
                if target < f(midpoint):
                    last = midpoint-1
                else:
                    first = midpoint+1
    best_guess = midpoint

    return best_guess

def int_or_sci(x):
    if x >= math.pow(10,6):
        x = '%.1E' % decimal.Decimal(x)
    else:
        x = int(x)

    return x

def input_size_calc():
    #Create Pretty Table Header
    tbl = PrettyTable(["f(n)", "1 sec", "1 min", "1 Hour", "1 Day", "1 Month", "1 Year", "1 Century"])
    tbl.align["f(n)"] = "l" # Left align city names
    tbl.padding_width = 1 # One space between column edges and contents (default)

    #Each Time Interval in Microseconds
    tsec = pow(10,6)
    tmin = 60 * tsec
    thour = 3600 * tsec
    tday = 86400 * tsec
    tmonth = 30 * tday
    tyear = 365 * tday
    tcentury = 100 * tyear

    tlist = [tsec,tmin,thour,tday,tmonth,tyear,tcentury]
    #print tlist

    #Add rows   
    #lg n
    f = lambda x : math.log(x,2)
    fn_list = []
    for t in tlist:
        #This would take too long for binary search method
        ans = int_or_sci(t)
        fn_list.append("2^(%s)" % ans)
    tbl.add_row(["lg n",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #sqrt(n)
    f = lambda x : math.pow(x,1/2.0)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["sqrt(n)",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #n
    f = lambda x : x
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["n",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #n log n
    f = lambda x : x * math.log(x,2)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["n log n",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #n^2
    f = lambda x : math.pow(x,2)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["n^2",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #n^3
    f = lambda x : math.pow(x,3)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["n^3",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #2^n
    f = lambda x : math.pow(2,x)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["2^n",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    #n!
    f = lambda x : math.factorial(x)
    fn_list = []
    for t in tlist:
        fn_list.append(int_or_sci(binary_search_guess(f, t)))
    tbl.add_row(["n!",fn_list[0], fn_list[1], fn_list[2], fn_list[3], fn_list[4], fn_list[5], fn_list[6]])

    print tbl

#PROGRAM BEGIN
input_size_calc()

3
Bạn có thể ước tính bằng cách thực hiện tìm kiếm nhị phân trên giá trị của hoặc sử dụng chuỗi Taylor . Bạn có thể mở rộng chức năng giai thừa thành liên tục, đến chức năng gamma và bạn có thể tìm thấy một số thông tin về nghịch đảo của nó - nhưng cách tiếp cận của bạn bằng cách sử dụng xấp xỉ Stirling cũng có vẻ tốt. Wnn
Tom van der Zanden


1
@TomvanderZanden Quên Wn- bạn có thể ước chừng toàn bộ câu hỏi chỉ bằng cách thực hiện tìm kiếm nhị phân!
David Richerby

Hãy cẩn thận về phân số ... sử dụng ba chữ số có nghĩa là hợp lý khi n lớn, nhưng đối với n nhỏ, hãy đảm bảo làm tròn xuống số nguyên gần nhất.
Ben Voigt

@DavidR Richby Bạn có thể mở rộng về điều này không?
stats_novice_123

Câu trả lời:


8

Nghịch đảo gần đúng của c=nlognn=c/logc. Thật vậy, đối với giá trị này củan chúng ta có

nlogn=clogclogclogc=clogc(logcloglogc)=c(1loglogclogc).
Xấp xỉ này thường là đủ tốt.

1

Bạn không cần phải ước chừng bất cứ điều gì để giải bài tập. Tất cả các chức năng bạn cung cấp là đơn điệu để bạn chỉ có thể sử dụng tìm kiếm nhị phân. Đó là, để giải quyếtf(n)=c cho n, chỉ cần đoán n=1,2,4,8, cho đến khi bạn tìm thấy đầu tiên k cái đó 2klog2k>c. Sau đó, thực hiện tìm kiếm nhị phân thông thường giữa2k1 và 2k. Nếu giải pháp là x, điều này mất khoảng 2logx đánh giá của f.


Sẽ không một tìm kiếm nhị phân trên lg n mất một lượng thời gian không thể?
stats_novice_123

Tôi đã chỉnh sửa mã của mình trong bảng câu hỏi và câu trả lời để phản ánh cách tiếp cận này. Có vẻ không thực tế cho lg n, tuy nhiên, bạn có đồng ý không?
stats_novice_123

Không, nó ổn để giải quyết logn=c. Khi bạn đã nhân đôic lần, bạn đã overshot, và sau đó bạn thực hiện tìm kiếm nhị phân giữa 2c2c. Phạm vi đó là về2c rộng, vì vậy nó cần khoảng cLặp lại tìm kiếm nhị phân để tìm giá trị chính xác.
David Richerby

Nhưng chỉ trong 1 giây, c = 10 ^ 6? 1 năm c = 3,2 x 10 ^ 13? vì f (n) tính bằng micrô giây theo câu hỏi
stats_novice_123

1
OK, tôi thấy quan điểm của bạn. Tuy nhiên, bạn vẫn chỉ cần về2cLặp lại tìm kiếm và, với một thư viện bignum hiệu quả, việc lặp lại sẽ không mất quá nhiều thời gian. (Và, vâng, như bạn nói,logdù sao thì cũng không thể đảo ngược để bạn có thể giải quyết vấn đề.)
David Richerby
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.