Calculate the Super-Logarithm

29

1

This should be a simple challenge.

Given a number n >= 0, output the super-logarithm (or the log*, log-star, or iterated logarithm, which are equivalent since n is never negative for this challenge.) of n.

log*(n) := { 0 if n <= 1; 1 + log*(log(n)) if n > 1 }

This is one of the two inverse functions to tetration. The other is the super-root, which is in a related question.

Examples

Input       Output
0           0
1           0
2           1
3           2
4           2
...
15          2
16          3
...
3814279     3
3814280     4

Rules

  • You do not need to support decimals, though you may.
  • You need to support input of at least 3814280 = ceiling(e^e^e).
  • You may not hard-code the values like 3814280. (Your program must theoretically support higher numbers.) I want an algorithm to be implemented.
  • Shortest code wins.

Related OEIS

mbomb007

Posted 2016-07-19T19:02:11.477

Reputation: 21 944

Related. – Oliver Ni – 2016-11-05T23:53:12.867

Answers

14

Jelly, 8 bytes

ÆlÐĿĊḊi1

Try it online! or verify all test cases.

Background

We start by successively taking natural logarithms of the input and the subsequent results until the result no longer changes. This works because the extension of the natural logarithm to the complex plane has a fixed point; if z = e-W(-1) &approx; 0.318 + 1.337i – where W denotes the Lambert W function – we have log(z) = z.

For input n, after computing [n, log(n), log(log(n)), …, z], we first apply the ceiling function to each of the results. Jelly's implementation (Ċ) actually computes the imaginary part of complex number instead, but we're not interested in these anyway.

Once the kth application of log yields a value less than or equal to 1, Ċ will return 1 for the first time. The 0-based index of that first 1 is the desired result.

The straightforward implementation (compute 1-based index, decrement) fails because of edge case 0, which does not have a 1 in its list of logarithms. In fact, for input 0, the sequence of logarithms is

[0, None]

This is because Jelly's logarithm (Æl) is overloaded; it first tries math.log (real logarithm), then cmath.log (complex logarithm), and finally "gives up" and returns None. Fortunately, Ċ is similarly overloaded and simply returns it argument if it cannot round up or take an imaginary part.

Likewise, input 1 returns

[1, 0, None]

which may create problems in other approaches that do or do not involve Ċ.

One way to fix this problem is apply (dequeue; removes first element) to the array of logarithms. This maps

0ÆlÐĿ -> [0, None]    -> [None]
1ÆlÐĿ -> [1, 0, None] -> [0, None]

so neither list has a 1 now. This way, finding the index of the first 1 will return 0 (not found), which is the desired output for inputs 0 and 1.

How it works

ÆlÐĿĊḊi1  Main link. Argument: n (non-negative integer)

  ÐĿ      Apply the following link until the results are no longer unique.
Æl          Natural logarithm.
          Return the array of all unique results.
    Ċ     Round all resulting real numbers up to the nearest integer. This takes
          the imaginary part of complex numbers and does nothing for non-numbers.
     Ḋ    Dequeue; remove the first item (n) of the array of results.
      i1  Find the first index of 1 (0 if not found).

This is one of the only three atoms in Jelly that are overloaded in a non-obvious manner.

Dennis

Posted 2016-07-19T19:02:11.477

Reputation: 196 637

11

Jelly, 9 bytes

Æl>1$пL’

Try it online!

Test suite. (Slightly modified.)

Explanation

Æl>1$пL’
     п    while loop, collect all intermediate results.
  >1$      condition: z>1
Æl         body: natural logarithm.
       L   length of the array containing all intermediate results,
           meaning number of iterations
        ’  minus one.

Leaky Nun

Posted 2016-07-19T19:02:11.477

Reputation: 45 011

7

Javascript, 45 27 26 bytes

l=a=>a>1&&1+l(Math.log(a))

Here is test suite (3rd rev)

Thanks @LeakyNun for saving 1 byte with conditional and then converting function to lambda, and @Neil for pointing out false is ok return value for <=1 (changed test to be == instead of ===)

CShark

Posted 2016-07-19T19:02:11.477

Reputation: 191

I was doing it without es6, but yeah that would be 1 byte shorter, thanks. – CShark – 2016-07-19T20:07:35.097

Why would you not use lambda? – Leaky Nun – 2016-07-19T20:08:01.010

no good reason, i just haven't used it that much, so it's not my first instinct – CShark – 2016-07-19T20:09:41.160

Apparently we're allowed to return false instead of 0 (as it auto-converts to 0 in an integer expression) in which case you can drop the |0. – Neil – 2016-07-19T20:17:26.600

That would save 1 byte, but what do you mean by "it auto-converts to 0"? What is "it"? – CShark – 2016-07-19T20:21:12.673

@CShark false is stored as zero behind the scenes. – mbomb007 – 2016-07-19T20:26:28.000

Ok if returning false instead of 0 is fine, then sweet (type doesn't matter). – CShark – 2016-07-19T20:29:00.780

7

C, 38 bytes

f(double n){return n>1?1+f(log(n)):0;}

Pretty self-explanatory.

Try it on ideone.

owacoder

Posted 2016-07-19T19:02:11.477

Reputation: 1 556

6

Mathematica, 21 bytes

If[#>1,1+#0@Log@#,0]&

Recursive anonymous function. Takes an integer as input and returns its super-logarithm as output. Just uses the given definition.

LegionMammal978

Posted 2016-07-19T19:02:11.477

Reputation: 15 731

3I actually looked ahead of time to see if there was a built-in. I was surprised when there wasn't. :D – mbomb007 – 2016-07-19T20:32:17.437

5

Pyth, 10 bytes

L&>b1hy.lb

Test suite.

This defines a function.

Leaky Nun

Posted 2016-07-19T19:02:11.477

Reputation: 45 011

I don't see any output in your test suite. Just a bunch of empty lines in the output. – mbomb007 – 2016-07-19T19:13:25.080

@mbomb007 Fixed. – Leaky Nun – 2016-07-19T19:19:04.180

Way cooler: tl.u?>N1.l ;-) – Jakube – 2016-07-19T21:52:09.217

@Jakube You could post that! – Leaky Nun – 2016-07-20T02:53:56.220

5

Dyalog APL, 13 bytes

Direct translation of OP:

{⍵≤1:0⋄1+∇⍟⍵}

TryAPL online!

Adám

Posted 2016-07-19T19:02:11.477

Reputation: 37 779

5

Haskell, 23 bytes

l x|x>1=1+l(log x)|1<2=0

Usage example: l 3814280 -> 4.

nimi

Posted 2016-07-19T19:02:11.477

Reputation: 34 639

4

05AB1E, 16 13 bytes

[Dî2‹#¼žr.n]¾

Explanation

              # implicit input n
[          ]  # infinite loop
 Dî2‹#        # break if n rounded up is less than 2
      ¼       # else, increase counter
       žr.n   # set next n = log(n)
            ¾ # push counter and implicitly print

Try it online

Emigna

Posted 2016-07-19T19:02:11.477

Reputation: 50 798

4

Python 3, 45 bytes

import math
s=lambda x:x>1and-~s(math.log(x))

For x <= 1, this returns False (which is == 0 in Python).

Lynn

Posted 2016-07-19T19:02:11.477

Reputation: 55 648

Yes, False can be used for 0. – mbomb007 – 2016-07-19T20:22:48.497

Also, you beat my naive implementation (by using and rather than if else). Grats. – mbomb007 – 2016-07-19T20:24:31.583

3

MATL, 15 12 bytes

0`ZetG>~}x@q

Try it online! Or verify all test cases (slightly modified version to handle several inputs).

How it works

Starting with 0, apply iterated exponentiation until exceeding the input. The output is the number of iterations minus 1.

0       % Push 0
`       % Do...while loop
  Ze    %   Exponential
  t     %   Duplicate
  G     %   Push input
  >~    %   Is current value less than or equal to the input? If so: next iteration
}       % Finally (code executed at the end of the last iteration)
  x     %   Delete
  @q    %   Iteration index minus 1
        % Implicitly end loop
        % Implicitly display stack

Luis Mendo

Posted 2016-07-19T19:02:11.477

Reputation: 87 464

3

J, 21 19 18 16 bytes

Saved 2 bytes to Leaky Nun, 1 byte to Galen Ivanov, and 2 bytes to FrownyFrog!

2#@}.(0>.^.)^:a:

Try it online!

Test cases

ls =: >:@$:@^.`0:@.(<:&1)
   ls 0
0
   ls 1
0
   ls 2
1
   ls 3
2
   ls 4
2
   ls 15
2
   ls 16
3
   ls 3814280
4

Conor O'Brien

Posted 2016-07-19T19:02:11.477

Reputation: 36 228

Here's my 18 bytes solution: 2#@}.^.^:(0<])^:a: (I started sovling what turned out to be a dup of this problem.)

– Galen Ivanov – 2018-02-04T10:18:58.977

2#@}.(0>.^.)^:a: seems to work. – FrownyFrog – 2018-02-04T20:19:47.533

Not sure if it’s equivalent though. – FrownyFrog – 2018-02-04T20:47:33.093

2

R, 34 bytes

f=pryr::f(`if`(n>1,1+f(log(n)),0))

Try it online!

A non-recursive approach is possible: 36 bytes and takes input from stdin.

n=scan()
while((n=log(n))>0)F=F+1
+F

rturnbull

Posted 2016-07-19T19:02:11.477

Reputation: 3 689

2

Julia, 17 bytes

!x=x>1&&1+!log(x)

Try it online!

Dennis

Posted 2016-07-19T19:02:11.477

Reputation: 196 637

2

R, 38 37 bytes

f=function(x)if(x>1)1+f(log(x))else 0

Thanks @user5957401 for the extra byte!

Test cases:

> f(0)
[1] 0
> f(1)
[1] 0
> f(2)
[1] 1
> f(3)
[1] 2
> f(4)
[1] 2
> f(3814279)
[1] 3
> f(3814280)
[1] 4

plannapus

Posted 2016-07-19T19:02:11.477

Reputation: 8 610

I think you can save a byte by using a literal if, else statement. i.e. if(x>1)1+f(log(x))else 0 is one byte shorter. – user5957401 – 2016-08-18T14:54:59.380

2

MATLAB / Octave, 44 bytes

function a=g(n);a=0;if n>1;a=1+g(log(n));end

Tried to do it all as one anonymous function, but I forgot that MATLAB/Octave continues to evaluate expressions even if they are multiplied by a boolean false (zero) value:

f=@(n)(n>1)*(1+f(log(n)))

costrom

Posted 2016-07-19T19:02:11.477

Reputation: 478

Yes, it would be nice to have a short-circuiting product :-) – Luis Mendo – 2016-07-21T22:41:05.163

2

Java 7, 47 bytes

int c(double n){return n>1?1+c(Math.log(n)):0;}

Try it online.

The recursive Java 7 style method above is 2 bytes shorter than an iterative Java 8 style lambda:

n->{int c=0;for(;n>1;c++)n=Math.log(n);return c;}

Try it online.

Explanation:

int c(double n){      // Method with double parameter and integer return-type
  return n>1?         //  If the input is larger than 1:
    1+                //   Return 1 +
      c(Math.log(n))  //   A recursive call with log(input)
   :                  //  Else:
    0;                //   Return 0 instead

n->{                  // Method with double parameter and integer return-type
  int c=0;            //  Create a counter, starting at 0
  for(;n>1;           //  Loop as long as the input is still larger than 1:
    c++)              //   Increase the counter by 1
    n=Math.log(n);    //   And update the input to log(input)
  return c;}          //  After the loop: return the counter as result

Kevin Cruijssen

Posted 2016-07-19T19:02:11.477

Reputation: 67 575

You might get it shorter with a Java 8 lambda. – mbomb007 – 2016-07-21T21:29:53.030

@mbomb007 responding three years later, haha.. (at the time I was only code-golfing in Java 7), but to still answer your question: no, unfortunately a Java 8 lambda is 2 bytes longer than the recursive method. I've added it to my answer, and I've also added an explanation. – Kevin Cruijssen – 2019-08-09T11:18:27.043

So you can't do recursive lambdas? – mbomb007 – 2019-08-09T14:42:14.980

@mbomb007 No, in Java unfortunately not. In Python, JavaScript, and I think C# .NET as well, recursive lambdas are possible, but in Java not for some reason.. – Kevin Cruijssen – 2019-08-09T15:43:16.913

1

Emacs Lisp, 38 bytes

(defun l(n)(if(> n 1)(1+(l(log n)))0))

Testcases:

(mapcar 'l '(0 1 2 3 4 15 16 3814279 3814280))
;; (0 0 1 2 2 2 3 3 4)

Lord Yuuma

Posted 2016-07-19T19:02:11.477

Reputation: 587

1

Jelly, 8 bytes

-Ælß$Ị?‘

Straightforward implementation of the definition. Try it online! or verify all test cases.

How it works

-Ælß$Ị?‘  Main link. Argument: x

     Ị    Insignificant; test if |x| ≤ 1.
      ?   If the result is 1:
-           Return -1.
          Else:
   $        Execute the monadic chain formed by the two links to the left.
Æl            Apply natural logarithm to x.
  ß           Recursively call the main link.
       ‘  Increment the result.

Dennis

Posted 2016-07-19T19:02:11.477

Reputation: 196 637

1

Perl 5, 35 bytes

Very simple, requires -M5.016 (which is free) to enable the __SUB__ keyword for anonymous recursion.

sub{$_[0]>1?1+__SUB__->(log pop):0}

Another alternative is

sub{$_[0]>1?1+__SUB__->(log pop):0}

which is 34 bytes, and gives the same output for all inputs > 1, but returns the special false value for inputs <= 1. False is numerically equal to zero, but prints as "" (empty string), so it probably doesn't qualify.

hobbs

Posted 2016-07-19T19:02:11.477

Reputation: 2 403

Great answer. You can win 1 byte by doing sub{($_=pop)>1?1+__SUB__->(log):0} though – Dada – 2016-07-22T11:09:02.163

1

CJam (16 bytes)

rd{_1>}{_ml}w],(

Online demo

Simple while loop with pre-condition. (What I really want here is a Golfscript-style unfold operation, but CJam doesn't have one, and floating point in GolfScript is messy and not at all golfy).

Peter Taylor

Posted 2016-07-19T19:02:11.477

Reputation: 41 901

As an aside, this is my 80th answer in [tag:math] and earned me my second tag badge today. – Peter Taylor – 2016-07-20T08:01:25.623

1

PARI/GP, 24 bytes

Just the straightforward recursion.

f(n)=if(n>1,1+f(log(n)))

Charles

Posted 2016-07-19T19:02:11.477

Reputation: 2 435

1

Racket, 61 bytes

(λ(x)(letrec([a(λ(b)(if(> b 1)(+ 1 (a(log b)))0))])(a x)))

Steven H.

Posted 2016-07-19T19:02:11.477

Reputation: 2 841

1

Maple, 32,30 29 bytes

f:=x->`if`(x>1,1+f(log(x)),0)

Test cases:

> f(0.);
  0
> f(1.);
  0
> f(2.);
  1
> f(3.);
  2
> f(4.);
  2
> f(3814279.);
  3
> f(3814280.);
  4

DSkoog

Posted 2016-07-19T19:02:11.477

Reputation: 560

1

R, 36 bytes

Slightly different approach from Plannapus

->n;a=0;while(n>1){a=a+1;n=log(n)};a

Uses a right assign to run the code -- so the desired number must precede it. i.e.

10->n;a=0;while(n>1){a=a+1;n=log(n)};a

user5957401

Posted 2016-07-19T19:02:11.477

Reputation: 699

0

Perl 6, 21 bytes

{($_,*.log...1>=*)-1}

Try it online!

The parenthesized expression is a sequence. $_, the argument to the function, is the first element. *.log generates each successive element by taking the log of the previous element. The sequence continues until the ending condition, 1 >= *, is true: 1 is greater than or equal to the current element. Subtracting 1 from the sequence coerces it to a number: its length.

Sean

Posted 2016-07-19T19:02:11.477

Reputation: 4 136

0

Mathematica, 29 bytes

Simple as all hell, and works for comically large as well as negative inputs:

f[x_]:=If[x>1,1+f[Log[x]],0]

enter image description here

Landak

Posted 2016-07-19T19:02:11.477

Reputation: 101

0

Ruby, 29 bytes

l=->n{n<=1?0:1+l[Math.log n]}

Seims

Posted 2016-07-19T19:02:11.477

Reputation: 631

-1 byte by rewriting n<=1?a:b as n>1?b:a and -1 more byte with anonymous lambda functions.

– Simply Beautiful Art – 2017-11-23T02:05:35.883