Енергопрограмування: O (1 ^ N), O (N ^ 1), O (2 ^ N), O (N ^ 2) всі в одному


65

Напишіть програму (або функцію), яка демонструє чотири загальні великі часові складності, залежно від способу її запуску. У будь-якій формі воно має додатне ціле число N, яке, напевно, можна вважати менше 2 31 .

  1. Коли програма запускається в оригінальному вигляді, вона повинна мати постійну складність. Тобто, складність повинна бути Θ (1) або, рівнозначно, Θ (1 ^ N) .

  2. Коли програма перевернута і запущена, вона повинна мати лінійну складність. Тобто, складність повинна бути Θ (N) або, рівнозначно, Θ (N ^ 1) .
    (Це має сенс , так як N^1це 1^Nнавпаки.)

  3. Коли програма в два рази , тобто зчеплені до себе, і працювати вона повинна мати експонентну складність, а саме 2 N . Тобто складність повинна бути Θ (2 ^ N) .
    (Це має сенс , тому що 2в 2^Nдва рази перевищує 1в 1^N.)

  4. Коли програма подвоюється та обертається та запускається, вона повинна мати складність поліномів , зокрема N 2 . Тобто складність повинна бути Θ (N ^ 2) .
    (Це має сенс , так як N^2це 2^Nнавпаки.)

Ці чотири випадки - єдині, з якими вам потрібно займатися.

Зауважте, що для точності я використовую нотацію великої тети (Θ) замість великої O, оскільки час виконання ваших програм повинен бути обмежений вище і знизу необхідними складностями. В іншому випадку просто написання функції в O (1) задовольнило б усі чотири бали. Тут не надто важливо зрозуміти нюанс. В основному, якщо ваша програма виконує k * f (N) операції для деякої постійної k, то це, швидше за все, Θ (f (N)).

Приклад

Якби оригінальна програма була

ABCDE

то запуск його повинен займати постійний час. Тобто, будь то вхід N 1 або 2147483647 (2 31 -1) або якесь значення між ними, воно повинно закінчуватися приблизно за однаковий проміжок часу.

Зворотна версія програми

EDCBA

повинен зайняти лінійний час з точки зору N. Тобто час, необхідний для його припинення, повинен бути приблизно пропорційним N. Отже, N = 1 займає найменше часу, а N = 2147483647 займає найбільше.

Подвоєна версія програми

ABCDEABCDE

повинні приймати два-к-N раз , з точки зору N. Тобто, час, необхідний для завершення повинна бути приблизно пропорційна 2 N . Отже, якщо N = 1 закінчується приблизно через секунду, для закінчення N = 60 буде потрібно більше часу, ніж вік Всесвіту. (Ні, ви не повинні це тестувати.)

Подвоєна і перевернута версія програми

EDCBAEDCBA

має зайняти квадратний час у співвідношенні Н. Тобто час, необхідний для його припинення, повинен бути приблизно пропорційним N * N. Отже, якщо N = 1 закінчується приблизно через секунду, N = 60 знадобиться близько години, щоб припинити.

Деталі

  • Вам потрібно показати або аргументувати, що ваші програми працюють у складних ситуаціях, про які ви кажете. Надання деяких даних про терміни є хорошою ідеєю, але також спробуйте пояснити, чому теоретично складність є правильною.

  • Це добре, якщо на практиці час, який проводять ваші програми, не є ідеальним представником їх складності (або навіть детермінованої). наприклад, вхід N + 1 іноді може працювати швидше ніж N.

  • Навколишнє середовище, в якому ви запускаєте свої програми, має значення. Ви можете зробити основні припущення про те, як популярні мови ніколи навмисно не витрачають час на алгоритми, але, наприклад, якщо ви знаєте, що ваша конкретна версія Java реалізує сортування міхурів замість швидшого алгоритму сортування , то вам слід це врахувати, якщо ви робите будь-яке сортування. .

  • Припускаючи всі складності тут, ми говоримо про найгірші сценарії , а не найкращі та середні.

  • Простір складності програм не має значення, лише часова складність.

  • Програми можуть виводити що завгодно. Важливо лише те, що вони приймають натуральне число N і мають правильні часові складності.

  • Допускаються коментарі та багаторядкові програми. (Ви можете припустити, що \r\nзворотний вміст призначений \r\nдля сумісності з Windows.)

Великі О нагадування

Від найшвидшого до найповільнішого O(1), O(N), O(N^2), O(2^N)(порядку 1, 2, 4, 3 вище).

Повільні терміни завжди переважають, наприклад O(2^N + N^2 + N) = O(2^N).

O(k*f(N)) = O(f(N))для постійної k. Так O(2) = O(30) = O(1)і O(2*N) = O(0.1*N) = O(N).

Пам'ятайте O(N^2) != O(N^3)і O(2^N) != O(3^N).

Акуратний великий O шпаргалка.

Оцінка балів

Це звичайний код гольфу. Виграє найкоротша оригінальна програма (постійний час) у байтах.


Відмінне запитання! Незначна точка: нам не потрібно вказувати найгірший / найкращий / середній регістр, тому що єдиним введенням, який вважається розміром N, є саме число N (яке BTW не є звичайним поняттям розміру вводу: це було б трактувати вхід N як журнал розміру N). Таким чином, існує лише один випадок для кожного N, який є одночасно найкращим, найгіршим та середнім випадком.
ShreevatsaR

5
Здається, ви відхилилися від звичних визначень складності. Ми завжди визначаємо часову складність алгоритму як функцію від величини його введення . У випадку, коли вхід є числом, розмір вхідного сигналу є логарифмом бази-2 цього числа. Таким чином, програма n = input(); for i in xrange(n): passмає експоненціальну складність, оскільки вона здійснює 2 ** kкроки, де k = log_2(n)розмір вводу. Вам слід уточнити, чи так це, оскільки це кардинально змінює вимоги.
wchargin

Відповіді:


36

Python 3 , 102 байти

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

Спробуйте в Інтернеті!

Це з O (1 ^ n), оскільки це те, що робить програма:

  • оцінити вхід
  • створити масив [0]
  • роздрукувати його

Відмінено:


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

Спробуйте в Інтернеті!

Це з O (n ^ 1), оскільки програма робить це:

  • оцінити вхід
  • створити масив [0] * вхід (0 повторений стільки разів, скільки вхід)
  • роздрукувати його

Удвічі:

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

Спробуйте в Інтернеті!

Це з O (2 ^ n), оскільки це те, що робить програма:

  • оцінити вхід
  • створити масив [0]
  • роздрукувати його
  • спробуйте оцінити вхід
  • невдача
  • створити масив [0] * (2 ^ введення) (0 повторення стільки разів, скільки 2 ^ введення)
  • роздрукувати його

Удвічі та назад:


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

Спробуйте в Інтернеті!

Це з O (n ^ 2), оскільки програма робить це:

  • оцінити вхід
  • створити масив [0] * вхід (0 повторений стільки разів, скільки вхід)
  • роздрукувати його
  • спробуйте оцінити вхід
  • невдача
  • створити масив [0] * (вхід ^ 2) (0 повторюється стільки разів, скільки вводиться квадрат)
  • роздрукувати його

Чому він не висить в очікуванні взаємодії з користувачем, коли є кілька дзвінків input()?
Джонатан Аллан

1
Це лазівка, що "кінець передачі" передається в кінці передачі?
Лина монашка

1
Ви можете пояснити це?
Brain Guider

1
Re: аргумент "кінець файлу", ви дивитесь на речі назад. Коли ви використовуєте термінал, запити на введення висять, оскільки з ним щось підключено; ctrl-D можна надіслати, щоб явно не надсилати жодного вводу. Якщо вхід підключений до порожнього файлу (як TIO робить, якщо поле введення залишене порожнім), або якщо він підключений до файлу, де було прочитано весь вхід, у запиті введення нічого не потрібно робити; він уже знає, що немає вводу. У UNIX / Linux "кінець файлу" та "відсутні вхідні дані" не відрізняються від звичайних файлів.

3
У першому випадку k- це вхід, і lце один, тож ви все ще обчислюєте 1**k, чи не так? На що має знадобитися O(log(k))час, незважаючи на той факт, що ми з вами і всі знаємо заздалегідь, що це одне?
Річард Раст

18

Perl 5, 82 73 71 + 1 (для прапора -n) = 72 байти

Я впевнений, що можу пограти в це (набагато) більше, але це перед сном, я витратив достатньо часу на налагодження, як є, і я пишаюся тим, що маю поки що.

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

Сама програма не використовує введення, а просто оцінює рядок, починаючи з коментаря, а потім робить одну заміну рядка, так що це явно за постійним часом. Це в основному еквівалентно:

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

Удвічі:

#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/#//;

Біт, який насправді займає експоненціальний час, - це другий евал: він оцінює команду say for(1..2**$_), в якій перераховані всі числа від 1 до 2 ^ N, яка, очевидно, займає експоненціальний час.

Відмінено:

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

Це (наївно) обчислює підсумок вводу, який чітко займає лінійний час (оскільки кожне додавання відбувається в постійному часі). Код, який фактично запускається, еквівалентний:

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

Останній рядок якраз такий, що коли ці команди повторюються, це займе квадратичний час.

Зворотний і подвоєний:

;//#/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#

Тепер це приймає підсумок підсумовування вхідних даних (і додає його до підсумків введення, але що б там не було). Оскільки він впорядковує N^2доповнення, це займає квадратичний час. Це в основному еквівалентно:

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

Другий рядок знаходить підсумок введення, роблячи Nдоповнення, а четвертий - summation(N)додавання, який є O(N^2).


Чудово! Зробити це мовою загальної мови було б важко! Це моє підсумки!
Арджун

Молодці, це досить приємно. Ви, мабуть, мали на увазі $x.=q;##say...замість $x.=q;#say...хоч (з двома #замість 1). (Це пояснило б, чому ви порахували 76 байт замість 75). Крім того, вам слід порахувати -nпрапор у своєму рахунку, оскільки ваша програма не обходиться без нього.
Дада

@Dada я випадково перемістив команди evalі s///команди. Якщо ви робите evalперше, вам знадобиться лише той #. Гарний улов!
Кріс

@Chris Правильно, це справді працює. Можливо, ви зможете опустити останнє #: $x=~s/#//;зворотна продукція ;//#/s~=x$, яка у вашому контексті не робить нічого, як це було б із ведучим #. (Я не перевіряв цього, хоча). Незалежно, майте +1!
Дада

@Dada Приємний улов ще раз!
Кріс

17

Власне , 20 байт

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

Спробуйте в Інтернеті!

Вхід: 5

Вихід:

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

Відмінено:

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

Спробуйте в Інтернеті!

Вихід:

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

Удвічі:

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

Спробуйте в Інтернеті!

Вихід:

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]

Удвічі та назад:

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

Спробуйте в Інтернеті!

Вихід:

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]

Головна думка

Насправді це мова на основі стека.

  • abcце програма, яка має складність O (1 n ), а її подвійність має складність O (2 n ).
  • defце програма, яка має складність O (n 1 ), а її подвійність має складність O (n 2 ).

Потім, моє подання - "abc"ƒ"fed"де ƒоцінювати. Таким чином, "fed"не буде оцінюватися.

Генерація індивідуальної програми

Псевдокод першого компонента ;(1╖╜ⁿr:

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

Псевдокод другого компонента ;(1╖╜ⁿ@r:

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

Я ніколи не думав, що це стане навіть можливим! Чудова робота, сер! +1
Арджун

@Arjun Дякую за вдячність
Leaky Nun

Це чудово і справді постає перед проблемою, не використовуючи коментарів IMO. Дивовижно!
ShreevatsaR

1
Ну це насправді має коментарі ... рядки не оцінені і є НОП ...
Leaky Nun

4

Желе , 20 байт

Частково натхненний фактичним рішенням Leaky Nun .

Провідні та останні лінійки важливі.

Звичайний:


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

Спробуйте в Інтернеті!

Вхід: 5

Вихід:

610

Відмінено:


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

Спробуйте в Інтернеті!

Вхід: 5

Вихід:

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

Удвічі


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

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

Спробуйте в Інтернеті!

Вхід: 5

Вихід:

[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

Подвоєний і зворотний


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

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

Спробуйте в Інтернеті!

Вхід: 5

Вихід:

[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

Пояснення

Ключ тут в Ŀ, що означає "викликає посилання в індексі n як монада". Посилання індексуються зверху вниз, починаючи з 1, виключаючи основне посилання (найнижнє). Ŀтакож є модульним, тому якщо ви спробуєте зателефонувати за номером 7 із 5 посилань, ви фактично зателефонуєте на посилання 2.

Посилання, що викликається в цій програмі, завжди є посиланням в індексі 10 ( ), незалежно від версії програми. Однак, яке посилання знаходиться в індексі 10, залежить від версії.

Те, що з’являється після кожного, Ŀє там, щоб не розірватись, коли повернути його назад. Програма помилиться під час розбору, якщо до цього немає номера Ŀ. Маючи після цього, це поза місцем, яке просто йде прямо на вихід.

Оригінальна версія називає посилання , яке обчислює n + 1.

Зворотна версія викликає посилання R, яке генерує діапазон 1 .. n.

Подвоєна версія називає посилання 2*R, яке обчислює 2 n і генерує діапазон 1 .. 2 n .

Подвоєна і зворотна версія називає посилання ²R, яке обчислює n 2 і генерує діапазон 1 .. n 2 .


4

Befunge-98 , 50 байт

Нормальний

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

Це, безумовно, найпростіша програма з 4-х - єдиними командами, які виконуються, є такі:

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

Ця програма виконує деякі непоправні речі, перш ніж натиснути на команду "повернути вправо" ( ]) та стрілку. Потім він потрапляє на 2 команди "прийняти вхід". Оскільки на введенні є лише 1 число і через те, як TIO поводиться &з, програма вимикається через 60 секунд. Якщо є два входи (і тому, що я можу без додавання байтів), IP буде переходити до функції "кінцевої програми".

Спробуйте в Інтернеті!

Зворотний

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

Цей трохи складніший. відповідні команди такі:

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

що еквівалентно

;&^                   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

Тут важлива частина :. 1-:!k@. Це корисно, тому що, поки ми натиснемо правильну складність на стек перед тим, як виконати з меншою часовою складністю, ми можемо отримати бажану. Це використовуватиметься у двох інших програмах таким чином.

Спробуйте в Інтернеті!

Удвічі

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

І відповідною командою є:

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

Ця програма переходить у 2 петлі. По-перше, він проходить той самий шлях, що і звичайна програма, яка натискає 1 і N на стек, але замість того, щоб обертатися навколо другого &, IP переходить через коментар у цикл, який натискає 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 пропускаються за допомогою ;s

Після того, як (2 ^ N) буде натиснуто на стек, ми рухаємося вниз по рядку у вищезгадану цикл друку. Через першу петлю часова складність дорівнює Θ (N + 2 ^ N), але це зводиться до Θ (2 ^ N).

Спробуйте в Інтернеті!

Подвоєний і зворотний

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

Відповідні команди:

;&^

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

 @>:*[

  :

Це функціонує майже однаково до перевернутої програми, але N^2не вискочується з стека, оскільки перший рядок другої копії програми додається до останнього рядка першого, що означає, що команда drop ( $) ніколи не виконується , тому ми переходимо до циклу друку з N^2вершиною стопки.

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.