Зробіть авто-супер-логарифм


18

З огляду на позитивне ціле число п і число А , тим птетраци з визначається як ^ ( ^ ( ^ (... ^ ))), де ^ означає зведення в ступінь (або потужність) і вираз містить число рівно п раз.

Іншими словами, тетрація є право-асоціативною ітераційною експоненцією. Для n = 4 та a = 1.6 тетрація дорівнює 1,6 ^ (1,6 ^ (1,6 ^ 1,6)) ≈ 3,55743.

Зворотна функція тетрації по відношенню до n є супер-логарифмом . У попередньому прикладі 4 - це супер логарифм 3,55743 із "супер-базою" 1.6.

Змагання

Давши додатне ціле число n , знайдіть x таким, що n є супер-логарифмом самого себе в супер-базовому x . Тобто знайдіть x таким, що x ^ ( x ^ ( x ^ (... ^ x )))) (з x з'являється n разів) дорівнює n .

Правила

Програма або функція дозволена.

Формати введення та виведення є гнучкими, як зазвичай.

Алгоритм повинен теоретично працювати для всіх натуральних чисел. На практиці введення може бути обмежене максимальним значенням через обмеження пам'яті, часу чи типу даних. Однак код повинен працювати на введення 100не менше ніж за хвилину.

Алгоритм повинен теоретично дати результат з 0.001точністю. На практиці точність виводу може бути гіршою через накопичені помилки в числових обчисленнях. Однак вихід повинен бути точним до 0.001вказаних тестових випадків.

Найкоротший код виграє.

Тестові справи

1    ->  1
3    ->  1.635078
6    ->  1.568644
10   ->  1.508498
25   ->  1.458582
50   ->  1.448504
100  ->  1.445673

Довідкова реалізація

Ось посилання на реалізацію в Matlab / Octave (спробуйте в Ideone ).

N = 10; % input
t = .0001:.0001:2; % range of possible values: [.0001 .0002 ... 2]
r = t;
for k = 2:N
    r = t.^r; % repeated exponentiation, element-wise
end
[~, ind] = min(abs(r-N)); % index of entry of r that is closest to N
result = t(ind);
disp(result)

За N = 10це дає result = 1.5085.

Наступний код - це перевірка точності на виході з використанням арифметики змінної точності:

N = 10;
x = 1.5085; % result to be tested for that N. Add or subtract 1e-3 to see that
            % the obtained y is farther from N
s = num2str(x); % string representation
se = s;
for n = 2:N;
    se = [s '^(' se ')']; % build string that evaluates to iterated exponentiation
end
y = vpa(se, 1000) % evaluate with variable-precision arithmetic

Це дає:

  • Для x = 1.5085:y = 10.00173...
  • Для x = 1.5085 + .001:y = 10.9075
  • Бо x = 1.5085 - .001дає y = 9.23248.

так 1.5085це правильне рішення з .001точністю.


Пов'язані . Відмінності полягають у тому, що (супер-) база супер-логарифму тут не є фіксованою, а результат взагалі не є цілим числом.
Луїс Мендо

Здається, що функція зближується досить швидко. Якщо наш алгоритм просто відповідає кривій, що знаходиться в межах 0,001, чи вважається це теоретично таким, що працює для всіх натуральних чисел?
xnor

2
Гм, у wolframalpha вже є проблеми з тестовим випадком 6 .. " Стандартний час обчислення перевищено ... "
Кевін Круїссен

@KevinCruijssen У мене є посилання на реалізацію в Matlab на основі двійкового пошуку, який є досить швидким. Я можу розмістити його, якщо це корисно
Луїс Мендо

1
Чи xсходяться у міру nнаближення до нескінченності?
mbomb007

Відповіді:


3

Діалог APL , 33 25 байт

Потреби, ⎕IO←0які за замовчуванням застосовуються у багатьох системах.

⌈/⎕(⊢×⊣≥(*/⍴)¨)(1+⍳÷⊢)1E4

Теоретично обчислює всі цілі числа, але практично обмежується лише дуже малим.

СпробуйтеAPL онлайн!


Чи працює він досить швидко на вході 100?
Грег Мартін

@GregMartin Не вистачає пам'яті.
Adám

10

Haskell, 55 54 52 байти

s n=[x|x<-[2,1.9999..],n>iterate(x**)1!!floor n]!!0

Використання:

> s 100
1.445600000000061

Дякуємо @nimi за 1 байт!
Дякуємо @xnor за 2!


1
[ ]!!0замість head[ ]збереження байта
німі

1
s n=[x|x<-[2,1.9999..],n>iterate(x**)1!!n]!!0буде коротше, якби ви могли змусити Haskell прийняти його типи.
xnor

@xnor Я грав із ітерацією, коли писав це насправді, але якимось чином вийшов довше
BlackCap

6

Javascript, ES6: 77 байт / ES7: 57 53 байт

ES6

n=>eval("for(x=n,s='x';--x;s=`Math.pow(x,${s})`);for(x=2;eval(s)>n;)x-=.001")

ES7

Використання, **як запропонував DanTheMan:

n=>eval("for(x=2;eval('x**'.repeat(n)+1)>n;)x-=.001")

Приклад

let f =
n=>eval("for(x=n,s='x';--x;s=`Math.pow(x,${s})`);for(x=2;eval(s)>n;)x-=.001")

console.log(f(25));


Якщо ви використовуєте ES7, ви можете використовувати **замість Math.pow.
DanTheMan

4

Математика, 40 33 байт

Завдяки murphy за майже 20% економії!

1//.x_:>x+.001/;Nest[x^#&,1,#]<#&

Nest[x^#&,1,n]виробляє n-ту тетрацію x. ТакNest[x^#&,1,#]<# перевіряємо, чи менша (вхідна) тетрація x менша (вхідна). Ми просто починаємо з x = 1 і додаємо 0,001 кілька разів, поки тетрація не буде занадто великою, а потім виводимо останнє значення x (тому відповідь гарантовано буде більшим за точне значення, але в межах 0,001).

Оскільки я повільно навчаюся: //.x_:>y/;zабо //.x_/;z:>yозначає "шукати все, що відповідає шаблону x, але тільки те, для чого тест z повертає істину; а потім працювати на x за правилом y; повторно, поки нічого не зміниться". Тут шаблон x_- це просто "будь-яке число, яке я бачу", хоча в інших контекстах його можна ще обмежити.

Коли вхід становить щонайменше 45, тетрація збільшується настільки швидко, що останній крок викликає помилку переповнення; але значення x все одно оновлюється та виводиться правильно. Зменшення розміру кроку від 0,001 до 0,0001 виправляє цю проблему для входів до 112 і дає більш точну відповідь на завантаження (і все ще працює швидко, приблизно за чверть секунди). Але це один зайвий байт, так що забудьте про це!

Оригінальна версія:

x=1;(While[Nest[x^#&,1,#]<#,x+=.001];x)&

Трохи пограли в гольф:1//.x_:>x+.001/;Nest[x^#&,1,#]<#&
murphy

@murphy: чудово! Клянусь, я все-таки доберуся до того, що я можу використовуватись //.без допомоги :)
Грег Мартін

4

J, 39 31 28 байт

(>./@(]*[>^/@#"0)1+i.%])&1e4

Виходячи з виконання посилань. Це точно до трьох знаків після коми.

Збережені 8 байт , використовуючи метод з @ Адамов рішення .

Використання

Додаткові команди, які використовуються для форматування декількох входів / виходів.

   f =: (>./@(]*[>^/@#"0)1+i.%])&1e4
   (,.f"0) 1 3 6 10 25 50 100
  1      0
  3  1.635
  6 1.5686
 10 1.5084
 25 1.4585
 50 1.4485
100 1.4456
   f 1000
1.4446

Пояснення

(>./@(]*[>^/@#"0)1+i.%])&1e4  Input: n
                         1e4  The constant 10000
(                      )      Operate on n (LHS) and 10000 (RHS)
                   i.           The range [0, 10000)
                      ]         Get (RHS) 10000
                     %          Divide each in the range by 10000
                 1+             Add 1 to each
     (          )               Operate on n (LHS) and the range (RHS)
             #"0                  For each in the range, create a list of n copies
          ^/@                     Reduce each list of copies using exponentation
                                  J parses from right-to-left which makes this
                                  equivalent to the tetration
        [                         Get n
         >                        Test if each value is less than n
      ]                           Get the initial range
       *                          Multiply elementwise
 >./@                           Reduce using max and return

4

Пітон, 184 байти

def s(n):
 def j(b,i):
  if i<0.1**12:
   return b
  m = b+i
  try:
   v = reduce(lambda a,b:b**a,[m]*n)
  except:
   v = n+1
  return j(m,i/2) if v<n else j(b,i/2)
 return j(1.0,0.5)

Тестовий вихід (пропуск фактичних тверджень про друк):

   s(1) 1.0
   s(3) 1.63507847464
   s(6) 1.5686440646
  s(10) 1.50849792026
  s(25) 1.45858186605
  s(50) 1.44850389566
 s(100) 1.44567285047


Він обчислюється s(1000000)досить швидко
mbomb007

3

Ракетка 187 байт

(define(f x n)(define o 1)(for((i n))(set! o(expt x o)))o)
(define(ur y(l 0.1)(u 10))(define t(/(+ l u)2))(define o(f t y))
(cond[(<(abs(- o y)) 0.1)t][(> o y)(ur y l t)][else(ur y t u)]))

Тестування:

(ur 1)
(ur 3)
(ur 6)
(ur 10)
(ur 25)
(ur 50)
(ur 100)

Вихід:

1.028125
1.6275390625
1.5695312499999998
1.5085021972656247
1.4585809230804445
1.4485038772225378
1.4456728475168346

Детальна версія:

(define (f x n)
  (define out 1)
  (for((i n))
    (set! out(expt x out)))
  out)

(define (uniroot y (lower 0.1) (upper 10))
  (define trying (/ (+ lower upper) 2))
  (define out (f trying y))
  (cond
    [(<(abs(- out y)) 0.1)
     trying]
    [(> out y)
     (uniroot y lower trying)]
    [else
      (uniroot y trying upper)]))

2

Perl 6 , 42 байти

{(0,.00012).min:{abs $_-[**] $^r xx$_}}

(Переклад прикладу коду Matlab)

Тест:

#! /usr/bin/env perl6
use v6.c;
use Test;

my &code = {(0,.00012).min:{abs $_-[**] $^r xx$_}}

my @tests = (
  1   => 1,
  3   => 1.635078,
  6   => 1.568644,
  10  => 1.508498,
  25  => 1.458582,
  50  => 1.448504,
  100 => 1.445673,
);

plan +@tests + 1;

my $start-time = now;

for @tests -> $_ ( :key($input), :value($expected) ) {
  my $result = code $input;
  is-approx $result, $expected, "$result ≅ $expected", :abs-tol(0.001)
}

my $finish-time = now;
my $total-time = $finish-time - $start-time;
cmp-ok $total-time, &[<], 60, "$total-time.fmt('%.3f') is less than a minute";
1..8
ok 1 - 1  1
ok 2 - 1.6351  1.635078
ok 3 - 1.5686  1.568644
ok 4 - 1.5085  1.508498
ok 5 - 1.4586  1.458582
ok 6 - 1.4485  1.448504
ok 7 - 1.4456  1.445673
ok 8 - 53.302 seconds is less than a minute


1

Аксіома 587 байт

l(a,b)==(local i;i:=1;r:=a;repeat(if i>=b then break;r:=a^r;i:=i+1);r);g(q,n)==(local r,y,y1,y2,t,v,e,d,i;n<=0 or n>1000 or q>1000 or q<0 => 0;e:=1/(10**(digits()-3));v:=0.01; d:=0.01;repeat(if l(v,n)>=q then break;v:=v+d;if v>=1 and n>25 then d:=0.001;if v>=1.4 and n>40 then d:=0.0001;if v>=1.44 and n>80 then d:=0.00001;if v>=1.445 and n>85 then d:=0.000001);if l(v-d,n)>q then y1:=0.0 else y1:=v-d;y2:=v;y:=l(v,n);i:=1;if abs(y-q)>e then repeat(t:=(y2-y1)/2.0;v:=y1+t;y:=l(v,n);i:=i+1;if i>100 then break;if t<=e then break;if y<q then y1:=v else y2:=v);if i>100 then output "#i#";v)

менше гольфу + номери

l(a,b)==
  local i
  i:=1;r:=a;repeat(if i>=b then break;r:=a^r;i:=i+1)
  r
g(q,n)==
 local r, y, y1,y2,t,v,e,d, i
 n<=0 or n>1000 or q>1000 or q<0 => 0  
 e:=1/(10**(digits()-3))
 v:=0.01; d:=0.01  
 repeat  --cerco dove vi e' il punto di cambiamento di segno di l(v,n)-q
    if l(v,n)>=q then break
    v:=v+d 
    if v>=1     and n>25 then d:=0.001
    if v>=1.4   and n>40 then d:=0.0001
    if v>=1.44  and n>80 then d:=0.00001
    if v>=1.445 and n>85 then d:=0.000001
 if l(v-d,n)>q then y1:=0.0
 else               y1:=v-d 
 y2:=v; y:=l(v,n); i:=1  -- applico il metodo della ricerca binaria
 if abs(y-q)>e then      -- con la variabile i di sicurezza
    repeat 
       t:=(y2-y1)/2.0; v:=y1+t; y:=l(v,n)
       i:=i+1
       if i>100 then break
       if t<=e  then break 
       if  y<q  then y1:=v
       else          y2:=v
 if i>100 then output "#i#"
 v

(3) -> [g(1,1), g(3,3), g(6,6), g(10,10), g(25,25), g(50,50), g(100,100)]
   Compiling function l with type (Float,PositiveInteger) -> Float
   Compiling function g with type (PositiveInteger,PositiveInteger) ->
      Float

   (3)
   [1.0000000000 000000001, 1.6350784746 363752387, 1.5686440646 047324687,
    1.5084979202 595960768, 1.4585818660 492876919, 1.4485038956 661040907,
    1.4456728504 738144738]
                                                             Type: List Float

1

Лист звичайний, 207 байт

(defun superlog(n)(let((a 1d0)(i 0.5))(loop until(< i 1d-12)do(let((v(or(ignore-errors(reduce #'expt(loop for q below n collect(+ a i)):from-end t))(1+ n))))(when(< v n)(setq a (+ a i)))(setq i(/ i 2)))) a))

Використання reduceз :from-end tухиляється від необхідності робити проміжну лямбда "зворотної експоненції" (в основному (lambda (x y) (expt y x)), економлячи 14 байт (12, якщо ви видаляєте знімні пробіли).

Нам все ще потрібно обробляти float overflow, але ignore-errorsформа повертається, nilякщо сталася помилка, тому ми можемо використовувати orдля надання значення за замовчуванням.

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