Is it within the Cantor set?

20

4

The Challenge

For this challenge, you are supposed to determine if a given number is in the Cantor set. So first, let's define the Cantor set.

First, start with the numbers between 0 and 1. Any numbers outside this range are not in the Cantor set. Now, let's divide the numbers into three equal parts: [0,1/3], [1/3,2/3], [2/3, 1]. Any numbers not inside the ranges of the first and last parts are not in the Cantor set. Now, you repeat this process for the segments [0,1/3] and [2/3, 1]. Then you repeat on what is leftover. You keep on doing this forever. In the end, all numbers that are remaining are in the Cantor set. Here is a diagram of the first six iterations:

Cantor diagram


Input

Two integer x and y.
0 < y < 2^15
0 <= x <= y
The greatest common denominator of x and y is 1, unless x == 0.


Output

Truthy if x/y is in the Cantor set.
Falsy if x/y is not in the Cantor set.


Examples

Now, let's see some examples of numbers that are in the Cantor set.

1/3 -> true  

It is on a boundary, and boundaries are never removed.

1/4 -> true  

1/4 is never in the middle third of a segment, though it is never on the boundary either. If you follow it's path, you will actually find that it alternates between being in the first and last thirds of a section.

1/13 -> true  

1/13 alternates between the first, first, and last sections.

1/5 -> false

1/5 falls into the first empty block of the third row in the above diagram, between 1/9 and 2/9.

Other test cases:

0/4 -> true
3/10 -> true
3/4 -> true
10/13 -> true
1/1 -> true
12/19 -> false
5/17 -> false
3/5 -> false
1/7 -> false
1/2 -> false

You can try other numbers with this snippet:

function isCantor(t,r){var n=new Set;if(0>r)return isCantor(-t,-r);if(0==r)return!1;if(0>t||t>r)return!1;for(;;){if(t%r===0)return!0;if(1===Math.floor(t/r))return!1;if(n.has(t))return!0;n.add(t),t=t%r*3}}$("#test").click(function(){var t=parseInt($("#x").val()),r=parseInt($("#y").val());if(isNaN(t)||isNaN(r)||0==r)return void $("#result").text("Invalid input");var n=isCantor(t,r),i=(n?"I":"Not i")+"n the Cantor set.";$("#result").text(i)});
input{width:30px}button{margin-left:7px}
<script src=https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js></script><input id=x>/<input id=y><button id=test>Test</button><p id=result>

Objective

The person with the least bytes wins.

TheNumberOne

Posted 2017-02-01T06:35:59.987

Reputation: 10 855

Are we guaranteed that the input is not (0,0)? Is the fraction given in simplest form? – xnor – 2017-02-01T06:43:35.377

1@xnor look at the range given for y. I'm going to say that the fraction is in simplest form unless x == 0 – TheNumberOne – 2017-02-01T06:44:05.653

Some test cases where x!=1 would be good. Also, your snippet says 1/3 is not in the cantor set. – xnor – 2017-02-01T06:48:21.473

@xnor Added and fixed ;) – TheNumberOne – 2017-02-01T06:58:52.590

Can we take the input directly as a fraction x/y? – Greg Martin – 2017-02-01T07:39:19.373

If i enter 1/13 in your test snipet i get a recursion error – Roman Gräf – 2017-02-01T10:15:44.737

@RomanGräf Odd, I don't have that problem. – TheNumberOne – 2017-02-01T14:46:43.473

@RomanGräf Tell me if you have that problem again. The snippet no longer uses recursion. – TheNumberOne – 2017-02-01T16:14:16.413

6Cantor can it be found? – mbomb007 – 2017-02-01T16:16:23.993

@mbomb007 Lol, that's pretty good. – TheNumberOne – 2017-02-01T16:17:47.860

Related: OEIS A054591

– Engineer Toast – 2017-02-01T19:42:38.267

Not sure if "within" in the title is the best word considering that the Cantor set is meagre and its interior is empty. Maybe use "in" instead (as you do in the text)?

– Luis Mendo – 2017-02-01T23:26:15.263

Answers

13

Mathematica, 54 bytes

If[Last@#===1,Most@#,#]&@RealDigits[#,3][[1]]~FreeQ~1&

Unnamed function taking a fraction x/y as input, where y > 0 and 0 ≤ x ≤ y, and returning True or False.

A real number between 0 and 1 is in the Cantor set precisely when none of the digits in its base-3 expansion equals 1; the exception is that a fraction whose denominator is a power of 3 (whose base-3 expansion therefore terminates) is allowed to end in a 1.

RealDigits[#,3][[1]] gives all of the digits in the base-3 expansion of the fractional input #, in a form like {1, 0, 2, {0, 1, 0, 2}}: the last list is the periodic part of the expansion, while the integers beforehand are the digits before the periodicity starts. If the base-3 expansion is periodic right away, the output is like {{0, 1, 0, 2}}; if the base-3 expansion terminates, the form is like {1, 0, 2}.

So we want to check, using ~FreeQ~1, whether the list is free of 1s or not. However, because of the terminating expansion thing, we want to delete the last element of the list if it equals 1; that's what If[Last@#===1,Most@#,#] accomplishes. (The === is needed to compare a potential list with 1: == alone remains unevaluated in that situation.)

Greg Martin

Posted 2017-02-01T06:35:59.987

Reputation: 13 940

4

Im surprised that Mathematica does not have IsCantorNumber but has a function to determine is something is a goat.

– Brain Guider – 2017-02-01T14:59:09.807

3Well, I dunno, which come up more in real life: goats or fractals? ;) – Greg Martin – 2017-02-01T17:39:29.390

FreeQ[RealDigits[#,3][[1]]/.{{x___,1}}:>{x},1]& – ngenisis – 2017-02-02T00:05:21.993

Such a rule would also strip off trailing 1s in the periodic part, which leads to incorrect answers. For example, the base-3 expansion of 7/8 is .21212121...., or {{2,1}}; but the suggested rule would change that to {{2}}, which is free of 1s but shouldn't be. – Greg Martin – 2017-02-02T00:23:05.050

Touché. How about #==0||FreeQ[RealDigits[#,3]/.{{x___,1},_}:>{x},1]&? If it's terminating and nonzero RealDigits[#,3] will be of the form {{__Integer},-1} and if it's repeating it will be of the form {{___Integer,{__Integer}},-1}, right? I'm on mobile so it's hard to test right now. If this works, using infix notation for RealDigits might work as well. – ngenisis – 2017-02-02T01:03:08.927

9

C (gcc), 61 59 58 bytes

f(x,y,i){for(i=y;i--&&x<y;)x=(y-x<x?y-x:x)*3;return x<=y;}

Exploits the symmetry of the Cantor set. Breaks after y iterations to avoid an infinite loop.

Try it online!

nwellnhof

Posted 2017-02-01T06:35:59.987

Reputation: 10 037

7

Jelly, 22 17 16 15 bytes

,ạµ%⁹×3µÐĿ:⁹Ḅ3ḟ

Prints 3 for truthy, nothing for falsy.

Try it online!

Background

A well-known property of the Cantor set is that it contains precisely those numbers between 0 and 1 that can be written without 1's in their ternary expansion.

Note that some numbers – precisely the right edges of the closed intervals involved in the set's construction – can be written either with a single (trailing) 1 or with an infinite amount of trailing 2's. For example, 1 = 13 = 0.22222…3 and 1/3 = 0.13 = 0.022222…3, just as 0.510 = 0.499999…10.

To avoid special-casing the right edges, we can check for 1's is the shortest decimal expansion in both x/y and 1 - x/y = (y - x)/y, where x/y is a right edge iff (y - x)/y is a left edge. If at least one of them contains no 1's, x/y belongs to the Cantor set.

How it works

,ạµ%⁹×3µÐĿ:⁹Ḅ3ḟ  Main link. Left argument: x. Right argument: y

 ạ               Absolute difference; yield y - x.
,                Pair; yield [x, y - x].
       µ         Begin a new, monadic chain with argument [a, b] := [x, y - x].
  µ     ÐĿ       Repeatedly execute the links in between until the results are no
                 longer unique, updating a and b after each execution. Return the
                 array of all unique results.
   %⁹              Compute [a % y, b % y].
     ×3            Compute [3(a % y), 3(b % y)].
                 This yields all unique dividends of the long division of x by y in
                 base 3.
          :⁹     Divide all dividends by y to get the corresponding ternary digits.
            Ḅ    Unbinary; turn [1, 1] into 3, other pairs into other numbers.
             3ḟ  Remove all occurrences of the resulting numbers from [3], leaving
                 an empty array if and only if one pair [a, b] is equal to [1, 1].

Dennis

Posted 2017-02-01T06:35:59.987

Reputation: 196 637

3 is the true true +1. – Magic Octopus Urn – 2017-02-01T17:32:37.497

3

JavaScript (ES6), 65 67

Edit 2 bytes saved thx @Luke

n=>d=>(z=>{for(q=0;~-q*n*!z[n];n=n%d*3)z[n]=1,q=n/d|0})([])|q!=1|!n

Less golfed

n=>d=>{
  z = []; // to check for repeating partial result -> periodic number
  for(q = 0; q != 1 && n != 0 && !z[n]; )
    z[n] = 1,
    q = n / d | 0,
    n = n % d * 3
  return q!=1 | n==0
}

Test

f=
n=>d=>(z=>{for(q=0;~-q*n*!z[n=n%d*3];q=n/d|0)z[n]=1})([])|q!=1|!n
  

console.log(
'Truthy: 1/3 1/4 1/13 0/4 3/10 3/4 10/13 1/1\nFalsey: 1/5 12/19 5/17 3/5 1/7 1/2'.replace(/(\d+).(\d+)/g,(a,x,y)=>a+':'+f(x)(y))
)  

edc65

Posted 2017-02-01T06:35:59.987

Reputation: 31 086

I think you can replace n=n%d*3 with q=n/d|0 and then replace z[n] with z[n=n%d*3] – Luke – 2017-02-01T11:32:15.757

2

JavaScript (ES6), 55 bytes

y=>f=(x,z,n=~y,q=x/y|0)=>n?!z|!q&&f(x%y*3,z|q==1,n+1):1

Use by currying in the denominator first and the numerator second. The standard form is a byte longer:

f=(x,y,z,n=~y,q=x/y|0)=>n?!z|!q&&f(x%y*3,y,z|q==1,n+1):1

Explanation

If a fraction is not in the Cantor set, it must fall into one of the middle sections at some point; therefore, its representation in base 3 must contain a 1 followed at some point by a non-zero digit. That's how this works:

  • z keeps track of whether we've found a 1.
  • q is the current digit in base 3.
  • !z|!q is true if z is false (we have not found a 1) or q is false (the current digit is 0).

If n runs down to zero before we find a non-zero digit somewhere after a 1, the fraction is in the Cantor set and we return 1.

ETHproductions

Posted 2017-02-01T06:35:59.987

Reputation: 47 880

2

Bash + GNU utilities, 62 bytes

dc -e3o9d$2^$1*$2/p|tr -cd 012|grep -P "1(?!(0{$2,}|2{$2,})$)"

Try it online!

Pass it two integer arguments with arg1 <= arg2 and 0 < arg2.

The output is returned in the exit code (0 for falsy, 1 for truthy), as allowed by PPCG I/O methods.

I suspect the regex can be golfed further, maybe even eliminating the tr command in favor of using grep -z, but this is the shortest I've been able to come up with. (Unfortunately, grep -z is incompatible with grep -P, and the -P option to get perl-style regexes is required for the ?! syntax.)

Testbed program and output:

for x in '1 3' '1 4' '1 13' '1 5' '0 4' '3 10' '3 4' '10 13' '1 1' '12 19' '5 17' '3 5' '1 7' '1 2'
  do
    printf %-6s "$x "
    ./cantor $x >/dev/null && echo F || echo T
  done

1 3   T
1 4   T
1 13  T
1 5   F
0 4   T
3 10  T
3 4   T
10 13 T
1 1   T
12 19 F
5 17  F
3 5   F
1 7   F
1 2   F

Explanation

dc part (the arguments are x and y):

3o     Set output base to 3.
9      Push 9 on the stack.
d      Duplicate the top of the stack. (The extra 9 on the stack isn't actually used, but I need some delimiter in the code here so that the 9 doesn't run into the number coming up next.  If I used a space as a no-op, then I'd need to quote it for the shell, adding an extra character.)
$2^    Raise 9 to the y power. This number in base 3 is 1 followed by 2y 0's.
$1*$2/ Multiply the base-3 number 10....0 (with 2y 0's in it) by x and then divide by y (truncating). This computes 2y digits (after an implied ternary point) of x/y.  That's enough digits so that the repeating part of the rational number is there at least twice.
p      Print the result, piping it to tr.

tr and grep part:

A minor issue is that, although dc handles arbitrarily large integers, when dc prints a large number, it will break it up into 69-character lines, with each line except the last ending with a backslash, and with a newline after each line.

The tr command deletes any backslashes and newlines. This leaves just a single line.

The grep command then uses a perl-style regex (-P option, which is a GNU extension). The regex matches if the line contains a 1 not followed by at least y 0's or at least y 2's which then end the string.

This is exactly what's needed to identify x/y as not being in the Cantor set, because the repeating part of the base-3 representation of the rational number x/y can be viewed as starting at digit #y+1 after the ternary point, and is at most y digits long.

Mitchell Spector

Posted 2017-02-01T06:35:59.987

Reputation: 3 392

1

CJam (19 bytes)

{_@3@#*\/3b0-W<1&!}

Online test suite

This is an anonymous block (function) which takes two arguments on the stack and leaves 0 or 1 on the stack. It works by base converting the fraction x/y into y base 3 digits and returning true iff they contain no 1 or the only 1 is part of a suffix 1 0 0 0 ....

{            e# Begin a block
  _@3@#*\/3b e#   Base conversion
   0-W<      e#   Remove all zeros and the final non-zero digit
   1&!       e#   True iff no 1s are left
}

Peter Taylor

Posted 2017-02-01T06:35:59.987

Reputation: 41 901

1

Pyth, 14 bytes

gu*3hS,G-QGQE0

Based on my C solution. y on first input line, x on second.

                Q = y
 u         QE   G = x
                loop y times
  *3                x = 3 *
    hS,G                min(x,
        -QG                 y-x)
g            0  return x >= 0

If x/y is within the Cantor set, x stays between 0 and y. Otherwise, x becomes greater than y at one point, then diverges to negative infinity in the remaining iterations.

Try it online!

nwellnhof

Posted 2017-02-01T06:35:59.987

Reputation: 10 037

0

Batch, 91 bytes

@set/ai=%3+1,n=%1*3%%%2,f=%1*3/%2%%2,f^|=j=!((%2-i)*n)
@if %f%==0 %0 %n% %2 %i%
@echo %j%

Tests the first y-1 base 3 digits of x/y. i is the count of digits tested. n is the next value of x. j is true if n reaches zero (because the expansion terminates) or we have tested y-1 digits without finding a 1. f is true if j is true or if the next digit is a 1, at which point we stop looping and output j.

Neil

Posted 2017-02-01T06:35:59.987

Reputation: 95 035