Imprimir un conjunto de Cantor

19

El reto

Construye un conjunto de Cantor N-Nivelado .

El conjunto ternario de Cantor se crea eliminando repetidamente los tercios medios abiertos de un conjunto de segmentos de línea.

El programa recibe un parámetro N(un número entero) y luego imprime (en la consola o de manera similar) un conjunto Cantor de N niveles. La impresión solo puede contener undescore ( _) y espacios en blanco. El parámetro puede ser positivo o negativo y el signo indica la orientación de construcción del conjunto de Cantor: si N > 0el conjunto de Cantor se construye hacia abajo y si N < 0el conjunto de Cantor se construye hacia arriba. Si N = 0entonces el programa imprime una sola línea ( _).

Por ejemplo:

N = 2

_________
___   ___
_ _   _ _

N = -2

_ _   _ _
___   ___
_________

N = 3

___________________________
_________         _________
___   ___         ___   ___
_ _   _ _         _ _   _ _

N = -3

_ _   _ _         _ _   _ _
___   ___         ___   ___
_________         _________
___________________________

Criterios ganadores

Como es un desafío de código de golf, gana el código más corto.

Editado: Modificar 0 entradas por sugerencia de ugoren.

Averroes
fuente
¿Por qué no imprimir nada cuando N = 0? Esto hace que 0 sea un caso especial y hace que sea más difícil usar la recursividad. El manejo general sería imprimir uno solo _(pero imprimirlo hacia abajo cuando se obtiene -0).
Ugoren
Correcto. Ya he modificado las especificaciones.
Averroes

Respuestas:

10

GolfScript, 49 42 40 caracteres

~.abs.3\?'_'*\{.3%..,' '*\++}*](0>2*(%n*

Gracias a hammar por 42-> 40.

Mi mejor intento hasta ahora en un enfoque más teórico de los números es lamentablemente mucho más largo:

~.abs:^3\?,{3^)?+3base(;1+1?.'_'*^@-)' '*+}%zip\0>2*(%n*

o

~.abs 3\?:^,{6^*+3base.1+1?.('_'*@,@-' '*+}%zip\0>2*(%n*

y sospecho que la longitud de basey ziphará que sea imposible ponerse al día.

Peter Taylor
fuente
~.abs.@/\.3\?'_'*\{.3%..,' '*\++}*](%n*es de 39 caracteres, pero se bloquea en la entrada 0. :-(
Ilmari Karonen
@IlmariKaronen, sí, la división por cero fue un dolor para la implementación de C que escribí también, porque significaba que no puedes hacer nada n/abs(n)para obtenerla signum(n).
Peter Taylor
6

Python, 116 113 104 103 caracteres

n=input()
d=n>0 or-1
for i in range(n*d+1)[::d]:
 s='_'*3**i
 while i<n*d:s+=len(s)*' '+s;i+=1
 print s

Algoritmo más antiguo con 113 caracteres

r=input()
u='_'
l=[u]
for _ in abs(r)*u:o=len(l[0]);l=[s+o*' '+s for s in l]+[u*o*3]
print'\n'.join(l[::r>0 or-1])
Steven Rumbalski
fuente
5

Rubí (97)

Basado en la versión python de Steven Rumbalski:

n,r=$*[0].to_i,[?_]
n.abs.times{z=r[0].size;r=r.map{|s|s+' '*z+s}+[?_*z*3]}
puts n<0?r:r.reverse

Intentos anteriores, ambos de la misma longitud (112)

Construir líneas a partir de piezas:

c=->x,n{n<1??_*x :(z=c[s=x/3,n-1])+' '*s+z}
r=(0..m=(n=$*[0].to_i).abs).map{|i|c[3**m,i]}
puts n<0?r.reverse: r

Comience con una línea, haga agujeros en ella:

r=[?_*3**a=(n=$*[0].to_i).abs]
a.times{|c|r<<r[-1].gsub((x=?_*o=3**(a-c-1))*3,x+' '*o+x)}
puts n<0?r.reverse: r
jsvnm
fuente
3

Perl, 93 caracteres

@x=($t=$x=_ x 3**($a=abs($n=<>)),map$x.=$"x($x=~s/(.)../$1/g).$x,1..$a);say for$n<0?sort@x:@x

Pensé que trataría de ver qué tan bien la solución GolfScript de Peter Taylor se portaría a Perl. Las características notables incluyen el uso de en sortlugar de reverseguardar tres caracteres, usando el hecho de que un espacio se clasifica antes _.

Ilmari Karonen
fuente
2

Lisp común, 217 210 caracteres

(defun m(x)(flet((c(n v)(if(= n 0)`((,v))(cons(substitute v nil(make-list(expt 3 n)))(mapcar #'append(c(1- n)v)(c(1- n)" ")(c(1- n)v))))))(format t "~{~{~a~}~%~}"(let((r(c(abs x)'_)))(if(< x 1)(reverse r)r)))))

Expandido:

(defun m(x)
  (flet((c(n v)
    (if(= n 0)
       `((,v))
       (cons(substitute v nil(make-list(expt 3 n)))
            (mapcar #'append
                    (c(1- n)v)
                    (c(1- n)" ")
                    (c(1- n)v))))))
   (format t "~{~{~a~}~%~}"(let((r(c(abs x)'_)))(if(< x 1)(reverse r)r)))))

Me imagino que si el código Lisp logra superar cualquier conteo inicial para otro idioma (C, 219), estoy bien :)

Paul Richter
fuente
2

C ( 163 161 caracteres)

i,l,N;f(n,m,s){if(n){s=--n<l?m:s;f(n,m,s);f(n,s,s);f(n,m,s);}else
putchar(m);}main(n,v)int**v;{for(i=N=abs(n=atoi(1[v]));i+1;i--)l=n<N?N-i:i,f(N,95,32),f(0,10);}

Toma prestados un par de trucos de la respuesta de ugoren , pero la lógica central es bastante diferente. No pude seguir su ciclo for, por lo que es posible hibridar y guardar algunos más.

Peter Taylor
fuente
2

C, 219 193 179 143 136 131 caracteres

Seguí otra de las ideas de Petyer Taylor, más una mejora mía, ahorré 6 más.
Integró algunos consejos de @PeterTaylor, más copió su función principal, con ligeros cambios, que salvan a un personaje (¿es justo copiarlo? Dado que ninguno de nosotros ganará este, supongo que no está tan mal).
Pensé en una mejora significativa en cómo funciona mi recursión, y después de ver la respuesta de Peter Taylor, la implementé para recuperar el liderazgo. Al leer su respuesta nuevamente, vi que hice casi exactamente lo que él hizo. Así que esto parece la hibridación que sugirió.
También simplificó el bucle main, manteniendo la misma longitud.
Y tomó el truco de Peter para imprimir una nueva línea, en lugar de puts(""): guarda un personaje.

Eliminado intde la declaración de variables: una advertencia, pero guarda 4 caracteres.
El nuevo algoritmo no calcula 3 ^ x por adelantado, pero usa un solo bucle para imprimir 3 ^ x caracteres.
Puede guardar uno más definiendo int*v, pero 64 bits no funcionará.
El recuento de caracteres excluye los espacios en blanco (que se pueden eliminar).

o,i,n;
p(c) {
    n-- ?
        p(c),p(o>n?c:32),p(c)
    :
        putchar(c);
    n++;
}
main(c,v)int**v; {
    for(n=abs(c=atoi(v[1]));i<=n;i++)o=c+n?n-i:i,p(95),puts("");
}

Algoritmo anterior, 219 caracteres:

p(l,o,i,m,c,j) {
    for(;i<(m=l);i++)
        for(j=0,c=95;m/o||!putchar(c);j++)
            i/m%3-1||(c=32),m/=3;
    puts("");
}
main(c,v,n,i,l,o)int**v;{
    (n=atoi(v[1]))<0?n=-n:(c=0);
    for(i=n,l=1;i;i--)l*=3;
    o=c?1:l;
    for (;i<=n;i++)p(l,o,0),c?o*=3:(o/=3);
}
Ugoren
fuente
@PeterTaylor, no puedo eliminar el iparámetro, porque usar el global interferiría main. l--interferirá o>=ly tendré que reemplazarlo por >(entonces, ¿por qué lo escribo como si fuera algo malo?) También podría copiarte main, que es más simple y más corto que el mío.
ugoren
@PeterTaylor, tenías razón i: extrañé el hecho de que realmente ya no lo uso (pensé que querías decir que no lo paso).
ugoren
Por cierto, no me importa que tomes mi función principal. Mi regla general es que copiar la solución de otra persona para cambiar un solo personaje es demasiado agresivo, copiar la solución de otra persona para reescribir la mitad es perfectamente justo, y hay un área gris en algún punto intermedio. Quizás deberíamos tratar de acordar algunos estándares de la comunidad sobre meta.
Peter Taylor
@PeterTaylor, creo que llegamos a un punto muerto. Mi pparece bastante óptimo ahora, y tu mainfue mejor (no estoy seguro de que sea óptimo, pero no puedo mejorarlo más). Entonces, a excepción de una nueva e ingeniosa estructura de programa, la única forma de hacerlo era si cualquiera de nosotros copiaba el código del otro.
ugoren
Por cierto, ¿cómo estás contando tus personajes? Porque hago tu última versión 138 caracteres, no 136.
Peter Taylor
2

J, 44 39 38 37 bytes

' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|

Utiliza la iteración para construir el siguiente conjunto comenzando con 1 (que representa _) inicialmente.

Uso

   f =: ' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|
   f 0
_
   f 1
___
_ _
   f _1
_ _
___
   f 2
_________
___   ___
_ _   _ _
   f _2
_ _   _ _
___   ___
_________
   f 3
___________________________
_________         _________
___   ___         ___   ___
_ _   _ _         _ _   _ _
   f _3
_ _   _ _         _ _   _ _
___   ___         ___   ___
_________         _________
___________________________

Explicación

' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|  Input: integer n
                                    |  Absolute value of n
                (,:1)                  The array [1]
                     1&(          )~   Repeat abs(n) times starting with x = [1]
                                 ]       Identity function, gets x
                            0&*          Multiply x by 0
                               ,.        Join the rows together
                         ]               Identity function, gets x
                          ,.             Join the rows together
                     1  ,                Prepend a row of 1's and return
      0&>                              Test if n is negative, 1 if true else 0
         _&(   )                       If n is negative
             |.                          Reverse the previous result
            ]                            Return that
                                       Else pass the previous result unmodified
' _'                                   The string ' _'
    {~                                 Select from the string using the result
                                       as indices and return
millas
fuente
¡Agradable! No lo he intentado personalmente, pero me encanta usar la agenda, ¿ @.tal vez, combinada con $:, podría ser útil aquí? Por ejemplo, algo como (zero case)`(positive case)`(negative case)@.*, o tal vez ":@_:`(positive case)`(|."1@$:)@.*.
Conor O'Brien
No he intentado una solución recursiva, pero podría intentarlo.
millas
2

R , 141 139 137 bytes

m=abs(n<-scan());write("if"(n<m,rev,c)(c(" ","_")[Reduce(`%x%`,rep(list(matrix(c(1,1,1,1,0,1),3)),m),t(1))[,1+2^m-2^(m:0)]+1]),1,3^m,,"")

Pruébalo en línea!

-15 bytes gracias también por el uso de Giuseppe '('como función de identidad; writeen lugar de catimprimir la salida; uso inteligente de %x%.

-2 bytes gracias a Kirill L. utilizando en clugar de '('como la función de identidad.

JayCe
fuente
¿Podría un producto Kronecker funcionar aquí? %x%? Puede haber algunos problemas con tomar filas alternas quizás ...
Giuseppe
@Giuseppe Lo intenté, aprovechando tu respuesta "Crear una" H "a partir de una" H "más pequeña ... Lo intentaré de nuevo.
JayCe
Ah, entonces fuiste tú quien votó eso. ¡esa es la única razón por la que pensé krontambién! Me imagino que esto debería poder reducirse a 125 bytes si podemos encontrar el enfoque correcto.
Giuseppe
puede usarlo `(`como la función de identidad para poder usarlo writedirectamente en lugar de catun forbucle. 141 bytes
Giuseppe
@Giuseppe No tenía idea de que (podría usarse de esta manera, o que if podría usarse para seleccionar entre dos funciones. Y comenzaré a usar escribir ... ahorra mucho "\ n".
JayCe
1

Python, 177 164 caracteres

N=input()
n=abs(N)
c=lambda x:0if x<1 else x%3==1or c(x/3)
r=["".join([["_"," "][c(x/3**i)]for x in range(3**n)])for i in range(n+1)]
print"\n".join(r[::N>0 or-1])
Apuesta inicial
fuente
Como está utilizando Python 2, no necesita emitir los resultados de inputas int. Sus últimas dos líneas podrían acortarse aprint"\n".join(r[::N>0 or-1])
Steven Rumbalski,
@ Steven hice cambios. Gracias.
Ante
1

Perl, 113 caracteres

$i=abs($I=<>);@w=$_='_'x3**$i;while($i--){$x=3**$i;s/(__){$x}/'_'x$x.' 'x$x/eg;push@w,$_}say for$I>0?reverse@w:@w

Expandido:

$i=abs($I=<>);
@w=$_='_'x3**$i;
while($i--){
    $x=3**$i;
    s/(__){$x}/'_'x$x.' 'x$x/eg;
    push@w,$_
}
say for$I>0?reverse@w:@w
Toto
fuente
1

JavaScript 121 bytes

Función recursiva interna, luego cuide la salida hacia atrás si es necesario

n=>(f=(n,t=n&&f(n-1),r=t[0])=>n?[r+r+r,...t.map(x=>x+t[n]+x)]:['_',' '],f=f(n<0?-n:n),f.pop(),n<0?f.reverse():f).join`\n`

Menos golf

n=>{
  var f = n => { // recursive function
    var t = n && f(n-1), r = t[0]
    return n 
      ? [r+r+r, ...t.map(x => x+t[n]+x)]
      : ['_',' ']
  };
  f = f(n < 0 ? -n : n);
  f.pop(); // last row is all blanks
  if (n<0) f.reverse();
  return f.join`\n`
}

Prueba

var F=
n=>(f=(n,t=n&&f(n-1),r=t[0])=>n?[r+r+r,...t.map(x=>x+t[n]+x)]:['_',' '],f=f(n<0?-n:n),f.pop(),n<0?f.reverse():f).join`\n`

function go()
{
  var n=+I.value
  O.textContent = F(n)
}

go()
<input id=I type=number value=3 oninput='go()'>
<pre id=O></pre>

edc65
fuente
1

Lote, 265 262 242 236 235 bytes

@echo off
set/pn=
set c=%n%,-1,0
if %n% lss 0 set c=0,1,%n:-=%
for /l %%i in (%c%)do call:l %%i
exit/b
:l
set s=_
for /l %%j in (1,1,%n:-=%)do call:m %1 %%j
echo %s%
:m
set t=%s%
if %1 lss +%2 set t=%s:_= %
set s=%s%%t%%s%

Editar: Guardado 12 19 bytes gracias a @ l4m2. Se guardaron 8 bytes al eliminar la %a%variable innecesaria .

Neil
fuente
Esto por 247 bytes.
Conor O'Brien
@ ConorO'Brien Tenga en cuenta que sería 261 si contara todas las CR así como las LF (lo que estoy seguro de que no tiene que hacer, pero soy flojo de esa manera).
Neil
¿Entonces no estás eliminando los CR de tu código? ¿Aunque no es requerido por los archivos .BAT y eliminado por SE de todos modos? : P
Conor O'Brien
@ ConorO'Brien Es una penalización que acepto por usar el Bloc de notas para escribir archivos por lotes.
Neil
¿Puedes hacer algo así set c=%n%,-1,0 [LF] if %n% lss 0 set c=0,1,%a% [LF] for /l %%i in (%c%)do call:l %%i?
l4m2
0

Python 2 , 102 bytes

lambda n:'\n'.join(eval("[i+' '*len(i)+i for i in"*abs(n)+"'_'"+"]+['___'*len(i)]"*abs(n))[::n<1or-1])

Pruébalo en línea!

Erik el Outgolfer
fuente
0

Prólogo (SWI) , 265 232 213 bytes

S-E-R:-between(S,E,R).
[]/R/R.
[H|T]/B/R:-T/[H,32,H|B]/R.
N+R:-(N>0->O is N-1,O+S,S/[]/R;R=`_`).
N*[H|T]:-1-N-_,writef("%n",[H]);N*T.
_*[]:-nl.
-N:-(0-N-J,K is N-J;N-0-I,J is -I,K is I-N),L is 3^K,J+R,L*R,1=0;1=1.

Pruébalo en línea!

Solo ASCII
fuente
0

Potencia Shell , 111 bytes

filter f{if($s=[math]::Sign($_)){($x=$_-$s|f|%{$_+' '*($l=$_|% Le*)+$_})|?{$s-1};'_'*3*$l;$x|?{$s+1}}else{'_'}}

Pruébalo en línea!

Menos golfizado:

filter f{
    if($sign=[math]::Sign($_)){
        $x=$_-$sign|f|%{
            $_+' '*($length=$_|% Length)+$_
        }
        $x|?{$sign-1}  # output $x if $_ is negative
        '_'*3*$length
        $x|?{$sign+1}  # output $x if $_ is positive
    }
    else{
        '_'
    }
}
mazzy
fuente