n log n = c。これの良い近似は何ですか?


7

私は現在Big O表記法と計算の複雑さを調べています。

CLRSの問題1.1は、基本的な質問のように聞こえます。これは、入力のサイズとともにさまざまなアルゴリズムの複雑さがどのように成長するかについての直感を得ることです。

質問は尋ねます:

次の表の各関数と時間について、問題を解決するアルゴリズムにマイクロ秒かかると仮定して、時間で解決できる問題の最大サイズを決定します。f(n)tntf(n)

期間は、1秒、1分、1時間、1日、1か月、1年、1世紀です。

関数は、アルゴリズムで頻繁に発生する一般的な時間の複雑さであり、リストは次のとおりです。f(n)

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

ほとんどはかなり単純な代数的操作です。私はこれらの2つ、そして同じ理由で両方に苦労しています:

がマイクロ秒の時間である場合、私が苦労している2つは c

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

以下のためスターリングの近似を使用することを考えました。n!

これらはどちらもを解く能力を必要とし、スターリングはもう少し操作が必要です。nlog2n=c

ご質問

  1. 初等関数(のみ使用して解くことができないですランバートWを)、おおよそのにはいくつかの良い方法何?または、ランバートWをどのように実装しますか?nlog2nnlog2n
  2. どのようにnを解くのですか?= c、nが大きくなると、ほぼ必ず。スターリングは正しい道であり、そうであればを解決する方法2πn(ne)n=c

以下は、現在の出力でテーブルを完成させるためにまとめたpythonコードです。

編集:いくつかの回答に基づいて、私は二分探索法を使用しました(lg nを除く)。これを反映するために、以下のコードを編集しました。

+---------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
| 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     |
+---------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+

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
の値に対してバイナリ検索を実行するか、テイラー級数を使用することにより、を概算できます。階乗関数を連続的に、ガンマ関数に拡張でき、おそらくその逆に関する情報を見つけることができますが、スターリング近似を使用したアプローチも同様にうまくいきます。Wnn
トムファンデルザンデン

1
このPDFを確認してください:cs-people.bu.edu/lapets/resource/nlogn.pdf
Sagnik

1
@TomvanderZanden Forgetバイナリ検索を行うだけで質問全体を概算できます!Wn
David Richerby、2015

分数に注意してください... nが大きい場合は3桁の有効数字を使用するのが妥当ですが、nが小さい場合は、最も近い整数に切り捨ててください。
Ben Voigt 2015

@DavidRicherbyこれについて詳しく教えてください。
stats_novice_123 2015

回答:


8

のおおよその逆数はです。実際に、この値を我々は 通常、この近似で十分です。c=nlognn=c/logcn

nlogn=clogclogclogc=clogc(logcloglogc)=c(1loglogclogc).

1

演習を解くために何かを概算する必要はありません。与えられたすべての関数は単調なので、バイナリ検索を使用できます。つまり、解決するためにのため ちょうど推測、使用すると、最初の発見まで という。次に、と 間で通常のバイナリ検索を実行します。解が場合、 評価はおよそかかり。f(n)=cnn=1,2,4,8,k2klog2k>c2k12kx2logxf


lg nのバイナリ検索には、実行不可能な時間がかかりませんか?
stats_novice_123 2015

このアプローチを反映するために、質問と回答の表でコードを編集しました。lg nは非現実的だと思われますが、同意しますか?
stats_novice_123 2015

いいえ、を解くのは問題ありません。あなたが倍増したら回、あなたはオーバーシュートましたが、その後、あなたはとのバイナリ検索を行うと。その範囲は約幅なので、正しい値を見つけるには、バイナリ検索を約回繰り返す必要があります。logn=cc2c2c2cc
David Richerby

しかし、1秒間だけc = 10 ^ 6?1年c = 3.2 x 10 ^ 13?質問によるとf(n)はマイクロ秒
単位なので

1
わかりました。しかし、それでも、必要なのは約回の検索の反復だけであり、効率的なbignumライブラリーがあれば、反復にそれほど時間がかかりません。(そして、ええ、あなたが言うように、はとにかく可逆であるので、問題を2clog
回避
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.