Güç programlaması: O (1 ^ N), O (N ^ 1), O (2 ^ N), O (N ^ 2) hepsi bir arada


65

Nasıl çalıştığına bağlı olarak dört ortak büyük O zaman karmaşıklığı gösteren bir program (veya işlev) yazın . Her halükarda, 2 31'den küçük olduğunu varsayabileceğiniz pozitif bir tamsayı N alır .

  1. Program orjinal haliyle çalıştırıldığında sürekli karmaşıklığa sahip olması gerekir . Yani, karmaşıklık Θ (1) veya eşit olarak ently (1 ^ N) olmalıdır .

  2. Program tersine çevrildiğinde ve çalıştırıldığında doğrusal karmaşıklığa sahip olması gerekir . Yani, karmaşıklık Θ (N) veya eşdeğerde Θ (N ^ 1) olmalıdır .
    (Bu normaldi N^1edilir 1^Ntersine çevirdi.)

  3. Program iki katına çıktığında , yani kendi kendine birleştirildiğinde ve çalıştırıldığında, üstel karmaşıklığa, özellikle 2 N'ye sahip olması gerekir . Yani, karmaşıklık Θ (2 ^ N) olmalıdır .
    (Bu normaldi 2de 2^Nçift 1içinde 1^N.)

  4. Program olduğunda iki ve ters ve olması gereken çalıştırmak polinom özellikle karmaşıklığı, N 2 . Yani, karmaşıklık Θ (N ^ 2) olmalıdır .
    (Bu normaldi N^2edilir 2^Ntersine çevirdi.)

Bu dört vaka, üstesinden gelmen gereken tek şey.

Kesinliği için , büyük O yerine büyük teta (Θ) gösterimini kullanıyorum, çünkü programlarınızın çalışma sürelerinin gerekli karmaşıklıklarla hem yukarıda hem de altında tutulması gerektiğine dikkat edin. Aksi takdirde sadece O (1) 'de bir fonksiyon yazmak dört noktayı da tatmin eder. Buradaki nüansı anlamak çok önemli değil. Temel olarak, eğer programınız k * f (N) işlemlerini bazı sabit k için yapıyorsa, muhtemelen Θ (f (N)) 'dedir.

Örnek

Orijinal program olsaydı

ABCDE

sonra çalışan sabit zaman almalıdır. Yani, N girişinin 1 veya 2147483647 (2 31 -1) veya aradaki herhangi bir değer olup olmadığı, kabaca aynı sürede sona ermelidir.

Programın tersine çevrilmiş versiyonu

EDCBA

N cinsinden doğrusal zaman almalıdır, yani, sona erme süresi kabaca N ile orantılı olmalıdır, N = 1 en az zaman alır ve N = 2147483647 en fazla zaman alır.

Programın iki katına çıktı

ABCDEABCDE

N cinsinden iki'ye bir zaman almalıdır, yani, sonlandırması için geçen süre kabaca 2 N ile orantılı olmalıdır . N = 1 yaklaşık bir saniye içinde sona ererse, N = 60'ın sona ermesi, evrenin yaşından daha uzun sürer. (Hayır, test etmeniz gerekmez.)

Programın iki katına ve tersine çevrilmiş hali

EDCBAEDCBA

N cinsinden kare zaman almalıdır, yani, sonlandırılması için geçen süre kabaca N * N ile orantılı olmalıdır. N = 1 yaklaşık bir saniye içinde sona ererse, N = 60'ın sonlandırılması yaklaşık bir saat sürer.

ayrıntılar

  • Programlarınızın, söylediğiniz karmaşıklıklarda çalıştığını göstermeniz veya tartışmanız gerekir. Bazı zamanlama verilerinin verilmesi iyi bir fikir ancak aynı zamanda karmaşıklığın neden doğru olduğunu teorik olarak açıklamaya çalışın.

  • Uygulamada programlarınızın geçen zamanlarının karmaşıklıklarını (veya hatta deterministlerini) mükemmel şekilde temsil etmemesi halinde sorun yoktur. örneğin N + 1 girişi bazen N'den daha hızlı çalışabilir.

  • İçinde programlarınızı çalıştırdığınız çevre yapar olsun. Popüler dillerin kasıtlı olarak asla algoritmalarda zaman kaybetmediği konusunda temel varsayımlarda bulunabilirsiniz, ancak örneğin, Java'nın kendi sürümünüzün daha hızlı bir sıralama algoritması yerine kabarcık sıralaması uyguladığını biliyorsanız, herhangi bir sıralama yaparsanız bunu hesaba katmalısınız. .

  • Buradaki tüm karmaşıklıklar için , en iyi durumdan veya ortalama durumdan değil, en kötü durum senaryolarından bahsettiğimizi varsayın .

  • Programların uzay karmaşıklığı önemli değil, sadece zaman karmaşıklığı.

  • Programlar herhangi bir şey verebilir. Yalnızca pozitif tamsayı N'yi almaları ve doğru zaman karmaşıklıklarına sahip olmaları önemlidir.

  • Yorumlar ve çok satırlı programlara izin verilir. ( \r\nTersine çevrildiğini, \r\nWindows uyumluluğu içindir.)

Büyük O Hatırlatıcılar

En hızlıdan en yavaşına kadar O(1), O(N), O(N^2), O(2^N)(yukarıda sipariş 1, 2, 4, 3).

Daha yavaş terimler daima egemendir, örneğin O(2^N + N^2 + N) = O(2^N).

O(k*f(N)) = O(f(N))sabiti için k. Öyleyse O(2) = O(30) = O(1)ve O(2*N) = O(0.1*N) = O(N).

Unutmayın O(N^2) != O(N^3)ve O(2^N) != O(3^N).

Düzgün büyük O kopya kağıdı.

puanlama

Bu normal kod golfü. Bayt cinsinden en kısa orijinal program (sabit süre bir) kazanır.


Mükemmel soru! Küçük nokta: En kötü durum / en iyi durum / ortalama durum belirtmek zorunda değiliz, çünkü N büyüklüğünü sayan tek girdi N sayısıdır (bu BTW, girdi büyüklüğünün olağan kavramı değildir: ki N girişi, log büyüklüğü N olarak kabul edilir). Yani her N için aynı anda en iyi, en kötü ve ortalama durum olan tek bir vaka var .
ShreevatsaR

5
Her zamanki karmaşıklık tanımlarından sapmışsınız gibi görünüyor. Bir algoritmanın zaman karmaşıklığını her zaman girdi büyüklüğünün bir fonksiyonu olarak tanımlarız . Girişin bir sayı olması durumunda, girişin büyüklüğü, bu sayının base-2 logaritmasıdır. Bu nedenle programın n = input(); for i in xrange(n): passüstel bir karmaşıklığı var, çünkü 2 ** kadım atıyor, k = log_2(n)girdi boyutu nerede . Gereklilikleri önemli ölçüde değiştirdiği için durumun böyle olup olmadığını netleştirmelisiniz.
wchargin

Yanıtlar:


36

Python 3 , 102 bayt

try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt

Çevrimiçi deneyin!

Bu O (1 ^ n) 'dir, çünkü programın yaptığı şey bu:

  • girişi değerlendirmek
  • diziyi yarat [0]
  • yazdır

ters:


try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt

Çevrimiçi deneyin!

Bu, O (n ^ 1) 'dir, çünkü programın yaptığı budur:

  • girişi değerlendirmek
  • [0] * input dizisini yaratın (0, giriş kadar tekrarlandı)
  • yazdır

Doubled:

try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt
try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt

Çevrimiçi deneyin!

Bu O (2 ^ n) 'dir, çünkü programın yaptığı budur:

  • girişi değerlendirmek
  • diziyi yarat [0]
  • yazdır
  • girişi değerlendirmeye çalış
  • başarısız
  • [0] * (2 ^ giriş) dizisini yaratın (0, 2 ^ giriş kadar tekrarlanır)
  • yazdır

İki katına ve tersine döndü:


try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt
try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt

Çevrimiçi deneyin!

Bu, O (n ^ 2) 'dir, çünkü programın yaptığı budur:

  • girişi değerlendirmek
  • [0] * input dizisini yaratın (0, giriş kadar tekrarlandı)
  • yazdır
  • girişi değerlendirmeye çalış
  • başarısız
  • [0] * dizisini yaratın (giriş ^ 2) (0, giriş karesi kadar tekrarlanır)
  • yazdır

Birden fazla arama yapıldığında neden kullanıcı etkileşimi beklenmiyor input()?
Jonathan Allan,

1
İletimin sonunda "iletimin sonu" iletilen bir boşluk mu?
Sızdıran Rahibe,

1
Bunu açıklayabilir misin?
Brain Guider,

1
Re: "dosyanın sonu" argümanı, geriye bakıyorsunuz. Bir terminal kullanırken, buna bağlı bir şey olduğundan giriş talepleri askıda kalıyor; ctrl-D açıkça hiçbir girdi gönderilmemesi için gönderilebilir. Giriş boş bir dosyaya bağlıysa (giriş kutusunu boş bırakırsanız TIO'nun yaptığı gibi) veya tüm girişin okunduğu bir dosyaya bağlıysa, girişin herhangi bir şey yapmasına gerek yoktur; girdi olmadığını zaten biliyor. UNIX / Linux'ta "dosyanın sonu" ve "giriş yok" normal dosyalarda ayırt edilemez.

3
İlk kolarak, girdi mi, lbiri mi, yani hala hesaplıyorsunuz 1**k, değil mi? O(log(k))Siz ve ben ve herkesin bunun olduğunu önceden bildiği gerçeğine rağmen, hangisi zaman almalı ?
Richard Rast,

18

Perl 5, 82 73 71 + 1 (-n bayrağı için) = 72 bayt

Bundan daha çok golf oynayabileceğime eminim, ama yatma vakti, olduğu gibi hata ayıklamak için yeterince zaman harcadım ve şimdiye kadar sahip olduğum şeyden gurur duyuyorum.

#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;

Programın kendisi girdiyi kullanmaz ve sadece yorum ile başlayan bir dizgeyi değerlendirir ve sonra tek bir dize değişimini yapar, bu nedenle bu açıkça sabit bir zamandır. Temelde şuna eşdeğerdir:

$x="#";
eval $x;
$x=~s/#//;

Doubled:

#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;
#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;

Aslında üstel zaman alan bit ikinci değerlendirmedir say for(1..2**$_): 1'den 2'ye kadar olan tüm sayıları listeleyen komutu değerlendirir, ^ N, üstel zaman açıktır.

ters:

;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#

Bu (naif olarak), açıkça lineer zaman alan girdilerin toplamını hesaplar (çünkü her ekleme sabit zamandadır). Gerçekte çalıştırılan kod şuna eşittir:

$x+=$_ for(1..$_);
$_=$x;

Son satır sadece bu komutların tekrarlanması durumunda ikinci dereceden zaman alacaktır.

Tersine döndü ve iki katına çıktı:

;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#
;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#

Bu şimdi girişin toplamının toplamını alır (ve girişin toplamına ekler, ama her neyse). İlaveler sipariş verdiğinden N^2, bu ikinci dereceden zaman alır. Temelde şuna eşdeğerdir:

$x=0;
$x+=$_ for(1..$_);
$_=$x;
$x+=$_ for(1..$_);
$_=$x;

İkinci satır, girdilerin toplamını Nekler, summation(N)ekler yapar, dördüncü ise ekler yapar O(N^2).


Harika! Bunu bir ana dilde yapmak zor olurdu! Bu benim artı oyum var!
Arjun

Aferin, bu oldukça hoş. Muhtemelen demek $x.=q;##say...yerine $x.=q;#say...(ikisiyle olsa #yerine 1). (Bu neden 75 yerine 76 bayt saydığınızı açıklar). Ayrıca, -nprogramınız onsuz pek bir şey yapmadığından bayt sayınızdaki bayrağını saymalısınız.
Dada

@Dada yanlışlıkla aktarılmış evalve s///komutları. İlkini yaparsan evalsadece birine ihtiyacın var #. İyi yakalama!
Chris

@Chris Doğru, gerçekten çalışıyor. Sonuncusunu atlatabilirsiniz #: $x=~s/#//;tersine çevrilmiş ürünler ;//#/s~=x$, bağlamınızda hiçbir şey yapmaz, öncüde olduğu gibi #. (Yine de test etmedim). Ne olursa olsun, + 1'leyin!
Dada

@Dada Güzel bir kez daha yakala!
Chris,

17

Aslında , 20 bayt

";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"

Çevrimiçi deneyin!

Giriş: 5

Çıktı:

rⁿ@╜╖1(;
[0]
5

ters:

";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"

Çevrimiçi deneyin!

Çıktı:

rⁿ╜╖1(;
[0, 1, 2, 3, 4]
5

Doubled:

";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"

Çevrimiçi deneyin!

Çıktı:

rⁿ@╜╖1(;
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
rⁿ@╜╖1(;
rⁿ@╜╖1(;
[0]

İki katına ve tersine döndü:

";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"

Çevrimiçi deneyin!

Çıktı:

rⁿ╜╖1(;
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
rⁿ╜╖1(;
rⁿ╜╖1(;
[0, 1, 2, 3, 4]

Ana fikir

Aslında yığın tabanlı bir dildir.

  • abcO (1 n ) karmaşıklığı olan ve çiftinin O (2 n ) karmaşıklığı olan bir programdır .
  • defO (n 1 ) karmaşıklığına sahip bir programdır ve iki katı da O (n 2 ) karmaşıklığına sahiptir.

Öyleyse benim önerim "abc"ƒ"fed"nerede ƒdeğerlendirilir. Bu şekilde "fed"değerlendirilmeyecek.

Bireysel programın oluşturulması

İlk bileşenin sözde kodu ;(1╖╜ⁿr:

register += 1 # register is default 0
print(range(register**input))

İkinci bileşenin sözde kodu ;(1╖╜ⁿ@r:

register += 1 # register is default 0
print(range(input**register))

Bunun mümkün olacağını hiç düşünmedim! Harika işti efendim! +1
Arjun

@Arjun Takdir ettiğiniz için teşekkür ederiz.
Sızdıran Rahibe,

Bu mükemmeldir ve IMO yorumlarını kullanmamak gerçekten zor bir meseledir. Korku veren!
ShreevatsaR

1
Peki bu aslında yorumlar var ... dizeleri değerlenmemiş ve NOP'lar ...
Leaky Nun

4

Jöle , 20 bayt

Kısmen Leaky Nun'un Aslında çözümünden esinlenmiştir .

Önde gelen ve takip eden yeni satırlar önemlidir.

Normal:


⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

Çevrimiçi deneyin!

Giriş: 5

Çıktı:

610

ters:


⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

Çevrimiçi deneyin!

Giriş: 5

Çıktı:

[1, 2, 3, 4, 5]10

Doubled


⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

Çevrimiçi deneyin!

Giriş: 5

Çıktı:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]10

İki Kat ve Tersine Döndü


⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

Çevrimiçi deneyin!

Giriş: 5

Çıktı:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]10

açıklama

Buradaki anahtar Ŀ, "n dizinindeki bağlantıyı monad olarak çağırmak" anlamına gelir. Bağlantılar, ana bağlantı hariç (en alttaki) hariç, 1'den başlayarak baştan sona indekslenir. Ŀmodüler olduğundan, 5 bağlantıdan 7 numaralı bağlantıyı aramayı denerseniz, aslında bağlantı 2'yi arayacaksınız.

Bu programda çağrılan bağlantı, programın hangi sürümü olursa olsun , her zaman 10 ( ) endeksindeki bağlantıdır . Ancak, hangi bağlantı dizinde 10 ise sürümüne bağlıdır.

Bu , her Ŀbirinin orada göründüğü için ters çevrildiğinde kırılmaz. Önceden bir numara yoksa, program ayrıştırma sırasında hata verir Ŀ. Ardından sahip olmak , doğrudan çıktıya giden bir yer dışı niladdır.

Orijinal sürüm n + 1 değerini hesaplayan bağlantıyı çağırır .

Tersine çevrilen sürümR , 1 .. aralığını oluşturan bağlantıyı çağırır .

İki katına çıkan sürüm2*R , 2 n'yi hesaplayan ve 1 .. 2 n aralığını oluşturan bağlantıyı çağırır .

Katına ve ters versiyonu linki çağıran ²Rn hesaplar, 2 .. ve aralığı 1 üretir n 2 .


4

Befunge-98 , 50 bayt

Normal

\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;

Bu, 4'ün en basit programıdır - yürütülen tek komut aşağıdaki gibidir:

\+]
  !
  : @
&$< ^&;

Bu program bir "sağa dön" komutu ( ]) ve bir ok vurmadan önce bazı düzeltici şeyler yapar . Sonra 2 "giriş al" komutuna basar . Girişte sadece 1 sayı olduğundan ve TIO'nun nasıl davrandığından &dolayı program 60 saniye sonra çıkar. Eğer 2 giriş varsa (ve bayt eklemeden yapabildiğim için), IP "program sonu" fonksiyonuna gider.

Çevrimiçi deneyin!

ters

;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\

Bu biraz daha karmaşık. ilgili komutlar aşağıdaki gibidir:

;&^  $
  >:*[
;< $#]#; :. 1-:!k@
  :

hangi eşdeğer

;&^                   Takes input and sends the IP up. the ; is a no-op
  :                   Duplicates the input.
  >:*[                Duplicates and multiplies, so that the stack is [N, N^2
     $                Drops the top of the stack, so that the top is N
     ]#;              Turns right, into the loop
         :.           Prints, because we have space and it's nice to do
            1-        Subtracts 1 from N
              :!k@    If (N=0), end the program. This is repeated until N=0
;< $#]#;              This bit is skipped on a loop because of the ;s, which comment out things

Buradaki önemli kısım :. 1-:!k@biraz. Bu, kullanışlıdır, çünkü daha düşük bir zaman karmaşıklığında çalıştırmadan önce doğru karmaşıklığı istifin üzerine ittiğimiz sürece, istenen olanı elde edebiliriz. Bu, kalan 2 programda bu şekilde kullanılacaktır.

Çevrimiçi deneyin!

Doubled

\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;

Ve ilgili komut:

\+]
  !
  :
&$< ^&;\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;

Bu program 2 döngüye giriyor. Birincisi, 1 ve N'yi yığına iten normal programla aynı yolu izler, ancak ikinciye sarılmak yerine &, IP bir yorum üzerine iterek bir döngüye atlar 2^N:

        vk!:    If N is 0, go to the next loop.
      -1        Subtract 1 from N
 +  :\          Pulls the 1 up to the top of the stack and doubles it
  ]#            A no-op
\               Pulls N-1 to the top again

4. satırdaki diğer bitler ;s kullanılarak atlanır

(2 ^ N) istifin üzerine bastırıldıktan sonra, yukarıda belirtilen baskı döngüsünün içine doğru bir çizgi çekilir. İlk döngü nedeniyle, zaman karmaşıklığı Θ (N + 2 ^ N), ancak Θ (2 ^ N) değerine düşüyor.

Çevrimiçi deneyin!

İki Kat ve Tersine Döndü

;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\

İlgili komutlar:

;&^

;< $#]#; :. 1-:!k@

 @>:*[

  :

Bu neredeyse tersine çevrilmiş programla aynı şekilde çalışır, ancak programın N^2ikinci kopyasının ilk satırı ilk satırın son satırına eklenir, yani drop komutunun ( $) asla çalıştırılmadığı anlamına gelir , bu yüzden N^2yığının üstündeki baskı döngüsüne giriyoruz .

Çevrimiçi deneyin!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.