13
\$\begingroup\$

You step into the restroom, and notice that the toilet paper has missing! It occurs to you that someone had stolen it. Strangely enough, the first thing you would like to know is the amount of toilet paper the thief stole.

Task

You are given three integers \$ I \$, \$ R \$, and \$ G \$, the details of the toilet paper, where \$ I \$ is the radius of the inner ring, \$ R \$ is the number of rotations, and \$ G \$ is the thickness. The task is to return/output out the length of the toilet paper.

The toilet paper can be viewed as an Archimedes' spiral, which starts at coordinate \$ (I, 0) \$, and rotates a total of \$ R \$ times in the counterclockwise direction, with a distance of \$ G \$ between each gap.

More formally, the Archimedes' spiral here is defined as the set of all points whose locations over time move away from the origin counterclockwise at a constant speed and with constant angular velocity.

Due to potential precision issues, your answer will be judged correct if they pass all the sample cases below when rounded to \$ 1 \$ decimal place.

In the diagram below, \$ I = 5 \$, \$ R = 3 \$, \$ G = 4 \$, and the total length is \$ \approx 207.7 \$.

Archimedes spiral

Test Cases

I R G -> answer rounded to 1 decimal place (note that the answer does not have to be rounded)
0 1 1 -> 3.4
5 3 4 -> 207.7
12 9 2 -> 1187.7

This is , so the shortest code in bytes wins!

\$\endgroup\$
11
  • \$\begingroup\$ You probably want to explicitly mention somewhere that the output should be the length of the spiral \$\endgroup\$ Commented May 13, 2020 at 16:50
  • \$\begingroup\$ @mathjunkie Added \$\endgroup\$ Commented May 13, 2020 at 16:54
  • 1
    \$\begingroup\$ Are you sure the answer to the last test case (12, 999, 2) is right? Unless I'm misunderstanding something, the answer should be much larger than 4021.5 (a fast approximation gives a value around 6.35 million). \$\endgroup\$ Commented May 13, 2020 at 21:32
  • 1
    \$\begingroup\$ @MitchellSpector Thanks for bringing this up, it's fixed now (although I can't guarantee the precision is correct). \$\endgroup\$ Commented May 13, 2020 at 22:16
  • 2
    \$\begingroup\$ Hey, since you could not find an image with integer I,G,R, here's one for your second test case: i.imgur.com/rY1Av2Y.png Generated with this very simple Matlab script (Matlab is not on TIO unfortunately) \$\endgroup\$
    – Leo
    Commented May 14, 2020 at 23:47

7 Answers 7

8
\$\begingroup\$

J, 66 bytes

1#.2|@-/\1e3&((-:@[%:_1:)^[:i.1+[*0{])((*{:)+[*(1{])*1e3%~i.@#@[)]

Try it online!

This could be golfed more, but I'm putting it away for now.

Instead of taking an analytic approach I am using complex number arithmetic to break the spiral down into 1000 straight line segments per rotation, and then summing those segments.

I find the 500th root of -1, and keep multiplying it by itself to rotate it and get an approximation to the next spiral point.

Because the spiral also moves outward, we need to take the new vector, normalize it, multiply the normal vector by 1/1000 of the thickness, and then add that little correction to the new vector.

Conceptually, we're doing something similar to approximating a circle's circumference with the short sides of many triangles.

enter image description here

The idea is simple, but the golf part of it boils down to boring bookkeeping and argument parsing which isn't worth going into. In theory the realization of this method could be a lot shorter.

\$\endgroup\$
7
\$\begingroup\$

Python 3, 93 83 76 71 bytes

-9 bytes if we don't explicitly round the results
-1 byte after changing n to 9999
-7 bytes thanks to dingledooper who rearranged the operations
-5 bytes thanks to xnor who changed the square root calculations to taking an absolute value of a complex number

lambda i,r,g:sum(abs(1j+6.283*(t/n+i/g))for t in range(n*r))/n*g
n=9999

Try it online!

This is a straightforward numerical integration of a spiral length, without using any imports. n is chosen so that the results are sufficiently precise.

The length of a spiral is a sum $$L = \sum_{t=1}^n L_t,$$ where Lt is calculated as a hypothenuse in a roughly right triangle with sides $$\frac{2\pi}{n} r_t = \frac{2\pi}{n}\left(i+\frac{gt}{n}\right)$$ and $$r_{t+1}-r_t = \frac{g}{n}$$.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Here's an improvement to 76 bytes by rearranging. \$\endgroup\$ Commented May 14, 2020 at 4:28
  • \$\begingroup\$ And taking the /n outside cuts a space thanks to Python's lax parsing:Try it online! \$\endgroup\$
    – xnor
    Commented May 14, 2020 at 7:55
  • \$\begingroup\$ Using complex numbers for 71: Try it online! \$\endgroup\$
    – xnor
    Commented May 14, 2020 at 8:04
5
\$\begingroup\$

Python 3, 83 bytes

Thanks @xnor for finding some important approximation, notably h(t)=t*t+log(2*t+.5)

lambda i,r,g:g/2/T*(h(T*(i/g+r))-h(T*i/g))
h=lambda t:t*t+99*(2*t+.5)**.01
T=6.2832

Try it online!

The same solution as below, but uses several approximations:

  • \$2\pi \approx 6.2832\$
  • \$ \theta\sqrt{1+\theta^2}+\sinh^{-1}\theta + C \$
    \$ \approx \theta^2 + \ln(2\theta+0.5) +C \$
    \$ \approx \theta^2 + 99(2\theta+0.5)^{0.01} + C\$

"Exact" solution

Python 3, 104 100 bytes

-3 bytes thanks to @mathjunkie!
-1 byte thanks to @xnor!

lambda i,r,g:g/4/pi*(h(2*pi*(i/g+r))-h(2*pi*i/g))
from math import*
h=lambda t:t*hypot(t,1)+asinh(t)

Try it online!

This uses the exact formula. Maybe a good Taylor series approximation can be shorter.

The formula for the length of the spiral is: $$ L=\frac{G}{2\pi} \int_{\frac{2\pi I}{G}}^{\frac{2\pi(I+GR)}{G}}\sqrt{1+\theta^2}d\theta $$ and the formula for the integral is: $$ \int \sqrt{1+\theta^2}d\theta=\frac{1}{2}\left(\theta\sqrt{1+\theta^2}+\sinh^{-1}\theta \right) $$

\$\endgroup\$
8
  • 1
    \$\begingroup\$ Which functions here require numpy? Can you not use the math library and the shorter asinh function? \$\endgroup\$ Commented May 13, 2020 at 23:02
  • 1
    \$\begingroup\$ archsinh(x) is ln(x+sqrt(1+x*x)), which makes me wonder if you can approximate ln and pi accurately enough to not need any imports. \$\endgroup\$
    – xnor
    Commented May 13, 2020 at 23:05
  • 1
    \$\begingroup\$ Tricks for taking the difference of two parallel expressions might come in useful here. \$\endgroup\$
    – xnor
    Commented May 13, 2020 at 23:11
  • 1
    \$\begingroup\$ Since you're using math, there's hypot(t,1) for (1+t*t)**.5. \$\endgroup\$
    – xnor
    Commented May 13, 2020 at 23:21
  • 1
    \$\begingroup\$ It looks like h(t)=t*t+log(2*t+.5) is a good enough approximation to get within 0.1 on the test cases, though I might be overfitting a bit here. I'm ignoring the constant term since you're taking the difference of two values of h. Try it online! \$\endgroup\$
    – xnor
    Commented May 13, 2020 at 23:34
5
\$\begingroup\$

Python 3, 60 bytes

lambda I,R,G:6.2831*R*(I+R*G/2)+G*8*((1+R/(I/G+.05))**.01-1)

Try it online!

An approximate method. Approximates log without an import using Surculose Sputum's nifty approximation

This approximation is quite accurate, achieving within 0.02 on all the test cases and within 0.1 on all single-digit inputs. It's possible to use looser approximations that work for all the test cases, but I'm not sure at what point this is overfitting. In the extreme, hardcoding the outputs would be very short. So, I'd like to exclude this answer from my bounty on outgolfing me since I'm not clear what golfs what be valid.

The first summand \$2\pi(I+RG/2)R\$ is what we get if we approximate the toilet paper spiral as instead being concentric circles with the same inner radius, thickness, and number of turns. The average of the circumferences of these \$R\$ circles is \$2\pi(I+RG/2)\$. This is \$2\pi\$ times the average of their radii of \$I+RG/2\$, or equivalently the average of the inner and outer radius.

The above approximation is already pretty good for the test cases, with error within 0.4. The second term approximates the additional circumference due to the spiral moving radially, rather than just tangentially as circles do. I got this from looking at the integral for arc length suggested by svavil. We then get rid of approximate log without an import using Surculose Sputum's approximation The constant of 8 is an approximation for \$100/(4\pi)\$, with 100 being inverse of the exponent 0.01 chosen for the log approximation. The +0.05 fixes an approximation failing near \$I/G=0\$, which would otherwise cause a divide-by-zero. I originally calculated the desired values as \$1/(4\pi)=0.08\$, but heuristically 0.05 does better.

\$\endgroup\$
2
\$\begingroup\$

C (gcc) -lm -m32, 129 120 117 114 bytes

#define F float
F i(F u,F g){F s=hypot(u,g/=6.2832);return(u*s/g+g*log(s+u))/2;}
#define f(I,R,G)i(I+G*R,G)-i(I,G)

Try it online!

5 bytes off thanks to ceilingcat (by using the hypot library function).

4 bytes off thanks to dingledooper (who suggested using a typedef to shorten the float declarations -- I ended up using a macro instead to implement the same idea).

3 bytes off by using an approximation to \$2\pi\$ that's sufficient for the accuracy required by the challenge.

3 more bytes thanks to ceilingcat (turning f from a function into a macro).

The auxiliary function i computes the appropriate indefinite integral, and then f computes the desired definite integral by evaluating the indefinite integral at the two endpoints and subtracting.

\$\endgroup\$
0
1
\$\begingroup\$

05AB1E, 23 bytes

4°©*Ý*®/+nŽ›Ñ₄/*²n+tO®/

Port of @svavil's Python answer (their revision without complex number), so make sure to upvote them!

Input-order as r,g,i.

Try it online or verify all test cases.

Explanation:

4°                      # Push 10**4: 10000
  ©                     # Store it in variable `®` (without popping)
   *                    # Multiply it by the first (implicit) input `r`
    Ý                   # Push a list in the range [0, 10000r]
     *                  # Multiply each value by the second (implicit) input `g`
      ®/                # Divide each by `®`
        +               # Add the third (implicit) input `i` to each value
         n              # Take the square of that
          Ž›Ñ           # Push compressed integer 39478
             ₄/         # Divide it by 1000: 39.478
               *        # Multiply it by each value
                ²       # Push the second input `g` again
                 n      # Square it
                  +     # And add it to each value as well
                   t    # Take the square-root of each value
                    O   # Sum everything together
                     ®/ # And divide it by `®`
                        # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why Ž›Ñ is 39478.

\$\endgroup\$
4
  • \$\begingroup\$ Fwiw I have a suspicion my method might be sub 20 bytes in the hands of a skilled 05AB13 programmer \$\endgroup\$
    – Jonah
    Commented May 14, 2020 at 12:20
  • \$\begingroup\$ @Jonah Perhaps, but unfortunately I can't read J and couldn't really understand how your explanation would translate into code to be completely honest. I also see you've mentioned complex numbers at the start, which 05AB1E lacks. I understood your approach in general, but I have no idea what it looks like in (pseudo)-code. I can read Python, JavaScript, C, etc. a bit which helps in making ports sometimes, but I can't really read J (just like you probably wouldn't be able to read my 05AB1E answers if I would skip the explanation). I'm also not too skilled in most mathematics in general tbch.. \$\endgroup\$ Commented May 14, 2020 at 12:32
  • 1
    \$\begingroup\$ Ah ok Id add a more detailed explanation but without complex numbers it wouldn’t do any good. I didn’t realize that \$\endgroup\$
    – Jonah
    Commented May 14, 2020 at 12:34
  • 1
    \$\begingroup\$ @Jonah Yeah, it's unfortunate, but 05AB1E lacks complex/imaginary numbers I'm afraid. I'm sure it's possible to use calculations to mimic them, since I've seen it a few times before (here and here for example). It's one of 05AB1E's weaknesses, in addition to indexing and manipulation of matrices; lack of regex; date-builtins; etc. There are definitely alternatives for it, like what I did here, but it's usually pretty costy. ;) \$\endgroup\$ Commented May 14, 2020 at 12:46
1
\$\begingroup\$

SageMath, 70 bytes

lambda I,R,G:N(G/2/pi*(sqrt(1+x^2)).integral(x,2*pi*I/G,2*pi*(I/G+R)))

Try it online!

How

Uses the formula for the length of the spiral: $$ L=\frac{G}{2\pi} \int_{\frac{2\pi I}{G}}^{\frac{2\pi(I+GR)}{G}}\sqrt{1+\theta^2}d\theta $$

from Surculose Sputum's Python answer.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.