Generar escalera de enteros utilizando el menor número de caracteres únicos (en C ++)


13

Soy nuevo en el deporte del código golf. Estoy tratando de generar una escalera de enteros utilizando el menor número de caracteres únicos en C ++.

Digamos que se nos da un número entero 4.

Generaremos la siguiente escalera:

1
1 2
1 2 3
1 2 3 4

En resumen, mi programa leerá un entero positivo de stdin e imprimirá esta escalera en la salida. Estoy tratando de hacerlo con la menor cantidad de caracteres únicos posibles.

Mi programa es el siguiente:

#include<iostream>

int i;
int ii;
int iii;
int iiii;

main() {
    std::cin >> i;
    for(ii++; ii <= i; ii++) {
        int iii = iiii;
        for(iii++; iii <= ii; iii++) {
            std::cout << iii << " ";
        }
        std::cout << std::endl;
    }
}

Aquí está el corrector que usé para verificar la cantidad de caracteres únicos en mi programa:

#include <cstdio>
#include <cstring>
using namespace std;
int check[300],diffcnt=0,cnt=0,t;
char c;
double score;
int main(){

    memset(check,0,sizeof(check));
    FILE *in=fopen("ans.cpp","r");
    while(fscanf(in,"%c",&c)!=EOF){
        cnt++;
        if(!check[c]){
            check[c]=1;
            if(c=='\r'||c=='\n') continue;
            diffcnt++;
        }
    }
    if(diffcnt<25) printf("100\n");
    else if(diffcnt<30){
        printf("%.3lf\n",20.0*100.0/cnt+20.0*(29-diffcnt));
    }
    else{
        score=20.0;
        for(int x=95;x<cnt;x++) score*=0.9;
        printf("%.3lf\n",score);
    }
    printf("Unique Characters: %d\n", diffcnt);
    printf("Total Characters: %d\n", cnt);
    return 0;
}

Preferiblemente deseo usar menos de 25 caracteres únicos para completar este programa (excluyendo caracteres de nueva línea pero incluyendo espacios en blanco). Actualmente, mi programa usa 27. No estoy seguro de cómo optimizarlo aún más.

¿Podría alguien aconsejarme sobre cómo optimizarlo aún más (en términos de la cantidad de caracteres únicos utilizados)? Tenga en cuenta que solo se puede usar C ++.


55
Sin duda, es novedoso pedir consejos sobre cualquier otro criterio de puntuación que no sea el código golf , pero en realidad, es sobre el tema, ya que las páginas de consejos dicen que es una mejor respuesta a un desafío de programación que es sobre el tema .
Adám

8
@LuisMendo Realmente no creo que eso sea cierto en este caso, ya que muchos idiomas trivializan por completo este esquema de puntuación. Si este usuario quiere ayuda para aprender a "golf único", realmente tiene sentido en un subconjunto de idiomas, por lo que creo que esto es mucho mejor como un consejo que como un desafío genérico. Dicho esto, el problema base probablemente podría ser un desafío si alguien quiere publicarlo.
FryAmTheEggman

3
Creo que puedes usar dígrafos <% y%> en lugar de llaves, y creo que me perdí algunos.
mi pronombre es monicareinstate

2
Definitivamente me perdí un poco. # es% :, para que pueda deshacerse de tres caracteres e introducir uno ({=> <%,} =>%>, # =>% :) y llegar a 25. Si combina esto con la respuesta a continuación, I creo que puede obtener 24.
mi pronombre es monicareinstate

2
@LanceHAOH Los trígrafos son extremadamente comunes en las preguntas [poco claras], y los dígrafos también aparecen cuando se lee sobre los trigrafos.
mi pronombre es monicareinstate

Respuestas:


12

Creo que logré eliminar el carácter = de su código, aunque ahora es significativamente más lento

#include<iostream>

int i;
int ii;
int iii;
int iiii;

int main() {
    std::cin >> i;
    i++;
    for(ii++; ii < i;) {
    for(;iii>iiii;iii++);
    for(;iii<iiii;iii++);
    ii++;
        for(iii++; iii < ii; iii++) {
            std::cout << iii << " ";
        }
        std::cout << std::endl;
    }
}

No es bonito, pero al abusar del desbordamiento de enteros podemos volver a 0 sin usar =

También tuvimos que cambiar un poco a los guardias. Desafortunadamente, debido a la inclusión, no pude deshacerme de todos los nuevos caracteres de línea (aunque está cerca), por lo que puede ser la próxima vía de investigación.

Editar: Sin tiempo por ahora, pero si incluye y usa strstream y varias otras bibliotecas, creo que también puede eliminar el "carácter", nuevamente usando números enteros para llegar al carácter correcto para el espacio y pasarlo al Strstream


2
Podrías #include<std>y eliminar todos los :s. No es una buena práctica de codificación, pero eso no viene al caso.
Darrel Hoffman

3
@DarrelHoffman No puedo hacer que eso funcione, ¿no tienes que hacer using namespace std;lo que usaría una p adicional para: entonces un 0 neto
Datos vencidos

Hmm Tal vez, mi C ++ está un poco oxidado. Además, eso agrega un g, así que la pérdida neta, supongo. Si este fuera el código dorado, podríamos reducir el recuento de bytes cambiando el nombre ii, iiiy iiiia otros nombres de letras individuales (elija cualquier otra letra ya utilizada), pero de eso no se trata este desafío, así que supongo que no. Me pregunto si habría algún beneficio al usarlo getcy, en putclugar de cin/ cout, tendría que intentarlo.
Darrel Hoffman

1
Culpa mía. Acabo de leer el corrector nuevamente. Parece que el carácter de nueva línea se ignora. Por lo tanto, en realidad no hay necesidad de preocuparse por eliminar nuevas líneas. Pero combinado con su estrategia y la solución de @someone en los comentarios, logré llegar a 24 caracteres. Hice el programa aún más rápido usando short en lugar de int. Entonces obtuve un carácter 'h' adicional. Pero esto me permitió usar el tipo de datos char sin costo adicional. Así que también me deshice del "personaje usando el código de personaje."
LanceHAOH

@LanceHAOH: tenga en cuenta que el desbordamiento de enteros con signo es un comportamiento indefinido en C ++, incluidos todos los tipos con signo signed char. Si compila con la optimización habilitada, este código puede romperse con los compiladores modernos, a menos que lo use gcc -fwrapvpara hacer que el desbordamiento firmado esté bien definido como un complemento del complemento 2. el sonido metálico -fwrapvtambién. (los unsignedtipos enteros incluidos unsigned chartienen un comportamiento bien definido (envolvente) en ISO C ++). Depende de la ABI si chares signed charo unsigned char, por lo que charpuede estar bien.
Peter Cordes

10

Finalmente obtuve 24 caracteres únicos al combinar las respuestas de @ExpiredData y @someone. Además, usar el tipo de datos cortos en lugar de int ayudó a acelerar mi programa porque toma más tiempo desbordar un tipo de datos corto.

Mi código es el siguiente.

%:include<iostream>

short i;
short ii;
short iii;
short iiii;
char iiiii;

main() <%
    std::cin >> i;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    i++;
    for(ii++; ii < i; ii++) <%
        for(;iii;iii++);
        for(iii++; iii < ii; iii++)
            std::cout << iii << iiiii;
        std::cout << iii << std::endl;
    %>
%>

@KevinCruijssen lo usa char iiiii;, la última de las inicializaciones variables.
Rɪᴋᴇʀ

1
@KevinCruijssen Eso es cierto. Pero eso me permite eliminar el carácter "porque puedo usar el código de caracteres para representar el carácter de espacio. Entonces, la diferencia neta en caracteres únicos utilizados = 0.
LanceHAOH

9

23 personajes únicos que usan dígrafos. (25 sin). No UB

Utilice la sintaxis de inicializador con soporte de C ++ 11 para inicializar en una lista un entero a cero int var{};evitando =y 0. (O en su caso, evitando global iiii). Esto le proporciona una fuente de ceros distintos de las variables globales (que se inicializan estáticamente a cero, a diferencia de los locales).

Los compiladores actuales aceptan esta sintaxis de manera predeterminada, sin tener que habilitar ninguna opción especial.

(El truco de wraparound entero es divertido, y está bien para jugar al golf con la optimización deshabilitada, pero el desbordamiento firmado es un comportamiento indefinido en ISO C ++. Habilitar la optimización convertirá esos bucles envolventes en bucles infinitos, a menos que compile con gcc / clang -fwrapvpara proporcionar un desbordamiento entero firmado -definido comportamiento: complemento de 2 envolvente.

Dato curioso: ¡ISO C ++ std::atomic<int>tiene un complemento de 2 bien definido! int32_tse requiere que sea el complemento de 2 si está definido, pero el comportamiento de desbordamiento no está definido, por lo que aún puede ser un typedef para into longen cualquier máquina donde uno de esos tipos sea de 32 bits, sin relleno, y el complemento de 2).


No es útil para este caso específico:

También puede inicializar una nueva variable como una copia de una existente, con llaves o (con un inicializador no vacío), parens para la inicialización directa .
int a(b)o int a{b}son equivalentes aint a = b;

Pero int b();declara una función en lugar de una variable inicializada a cero.

Además, puede obtener un cero con int()o char(), es decir , inicialización cero de un objeto anónimo.


Podemos reemplazar sus <=comparaciones con <comparaciones por una simple transformación lógica : haga el incremento del contador de bucle justo después de la comparación, en lugar de en la parte inferior del bucle. En mi opinión, esto es más simple que las alternativas que la gente ha propuesto, como usar ++en la primera parte de a for()para hacer un 0 en un 1.

    // comments aren't intended as part of the final golfed version
    int n;
    std::cin >> n;      // end condition

    for(int r{}; r < n;) {      // r = rows from 0 .. n-1
        ++r;
        for(int i{}; i < r;) {
            ++i;
            std::cout << i << ' ';
        }
        std::cout << std::endl;
    }

Podríamos reducir eso a golf, for(int r{}; r++ < n;)pero la OMI es menos fácil de leer para los humanos. No estamos optimizando para el recuento total de bytes.


Si ya estuviéramos usando h, podríamos guardar el 'o "para un espacio.

Suponiendo un entorno ASCII o UTF-8, el espacio tiene un charvalor 32. Podemos crearlo en una variable con bastante facilidad, luegocout << c;

    char c{};
    c++; c++;            // c=2
    char cc(c+c+c+c);    // cc=8
    char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8

Y, obviamente, se pueden crear otros valores a partir de una secuencia ++y duplicación, en función de los bits de su representación binaria. Cambiando efectivamente un 0 (nada) o 1 (++) en el LSB antes de duplicar en una nueva variable.


Esta versión usa en hlugar de 'o ".

Es mucho más rápido que cualquiera de las versiones existentes (sin depender de un bucle largo), y está libre de Comportamiento indefinido . Se compila sin advertencias con g++ -O3 -Wall -Wextra -Wpedanticy conclang++ . -std=c++11es opcional. Es legal y portátil ISO C ++ 11 :)

Tampoco se basa en variables globales. Y lo hice más legible para los humanos con nombres de variables que tienen un significado.

Recuento de bytes únicos: 25 , excluyendo los comentarios que eliminég++ -E . Y excluyendo el espacio y la nueva línea como su mostrador. Utilicé sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic este askubuntu para contar las ocurrencias de cada personaje, y lo canalicé wcpara contar cuántos caracteres únicos tenía.

#include<iostream>

int main() {
    char c{};
    c++; c++;            // c=2
    char cc(c+c+c+c);    // cc=8
    char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8

    int n;
    std::cin >> n;      // end condition

    for(int r{}; r < n;) {      // r = rows counting from 0
        ++r;
        for(int i{}; i < r;) {
            ++i;
            std::cout << i << s;
        }
        std::cout << std::endl;
    }
}

Los únicos 2 fcaracteres son de for. Podríamos usar whilebucles en su lugar si tuviéramos un uso w.

Posiblemente podríamos reescribir los bucles en un estilo de lenguaje ensamblador i < r || goto some_label;para escribir un salto condicional en la parte inferior del bucle, o lo que sea. (Pero usando en orlugar de ||). No, eso no funciona. gotoes una declaración como ify no puede ser un subcomponente de una expresión como puede en Perl. De lo contrario, podríamos haberlo usado para eliminar los caracteres (y ).

Podríamos operar fpara gcon if(stuff) goto label;en lugar de for, y ambos bucles siempre se ejecutan al menos 1 iteración por lo que sólo necesitaríamos un bucle-rama en la parte inferior, al igual que una normal de asm do{}whileestructura de bucle. Suponiendo que el usuario ingresa un número entero> 0 ...


Dígrafos y Trígrafos

Afortunadamente, los trigrafos se han eliminado a partir de ISO C ++ 17, por lo que no tenemos que usarlos en ??>lugar de }si estamos jugando al golf exclusivo para la revisión más reciente de C ++.

Pero solo trigrafos específicamente: ISO C ++ 17 todavía tiene digrafos como :>por ]y %>para} . Así que en el costo de usar %, podemos evitar tanto {y }, y el uso %:de #un ahorro neto de 2 menos caracteres únicos.

Y C ++ tiene palabras clave de operador como notpara el !operador o bitorpara el |operador. Con xor_eqfor ^=, puede poner a cero una variable con i xor_eq i, pero tiene varios caracteres que no estaba usando.

Current g++ya ignora los trigraphs por defecto incluso sin -std=gnu++17; tiene que usar -trigraphspara habilitarlos, o -std=c++11algo por el estricto cumplimiento de un estándar ISO que los incluye.

23 bytes únicos:

%:include<iostream>

int main() <%
    int n;
    std::cin >> n;

    for(int r<% %>; r < n;) <%
        ++r;
        for(int i<%%>; i < r;) <%
            ++i;
            std::cout << i << ' ';
        %>
        std::cout << std::endl;
    %>
%>

Pruébalo en línea!

La versión final utiliza una 'comilla simple en lugar de ho "para el separador de espacio. No quería dibujar las char c{}cosas, así que lo eliminé. Imprimir un char es más eficiente que imprimir una cadena, así que lo usé.

Histograma:

$ sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic  | tee /dev/tty | wc -l
     15         // newline
     95         // space
     11 %
      2 '
      3 (
      3 )
      4 +
      9 :
     10 ;
     14 <
      8 >
      2 a
      4 c
      6 d
      3 e
      2 f
     12 i
      2 l
      2 m
     11 n
      5 o
      7 r
      5 s
     11 t
      3 u
25   // total lines, including space and newline

El separador de espacios (aún sin resolver)

En una respuesta ahora eliminada, Johan Du Toit propuso usar un separador alternativo, específicamente std::ends. Ese es un carácter NUL char(0), y se imprime como ancho cero en la mayoría de los terminales. Entonces la salida se vería así 1234, no 1 2 3 4. O peor, separados por basura en cualquier cosa que no colapsó en silencio '\0'.

Si puede usar un separador arbitrario, cuando el dígito 0es fácil de crear cout << some_zeroed_var. Pero nadie quiere 10203040, eso es peor que ningún separador.

Estaba tratando de pensar en una forma de crear una std::stringretención" " sin usar charo un literal de cadena. ¿Quizás agregarle algo? ¿Quizás con un dígrafo para []establecer el primer byte en un valor de 32, después de crear uno con longitud 1 a través de uno de los constructores?

Johan también sugirió la std::iosfunción miembro fill () que devuelve el carácter de relleno actual. El valor predeterminado para una secuencia lo establece std::basic_ios::init()y es ' '.

std::cout << i << std::cout.fill();reemplaza << ' ';pero usa en .lugar de' .

Con -, podemos tomar un puntero a couty el uso ->fill()de llamar a la función miembro:
std::cout << (bitand std::cout)->fill(). O no, no estábamos usando bya sea por lo que bien podría haber utilizado &en lugar de su equivalente léxico, bitand.

Llamar a una función miembro sin .o->

Ponlo dentro de una clase y define operator char() { fill(); }

// not digraphed
struct ss : std::ostream {  // default = private inheritance
//      ss() { init(); }  // ostream's constructor calls this for us
        operator char() { return fill(); }
}

Luego ss s{}antes del bucle, y std::cout << i << s;dentro del bucle. Genial, se compila y funciona correctamente, pero tuvimos que usar py hpara operator char(), para una pérdida neta de 1. Al menos evitamos bhacer funciones miembro publicusando en structlugar de class. (Y podríamos anular la herencia protecteden caso de que alguna vez ayude).


@JohanduToit: buena idea con cout.fill()fromstd::ios , pero no estábamos usando anteriormente ¿ . Tal vez podamos llamarlo de alguna manera tomando un puntero y usando ->fill()una función miembro? ¿Algo devuelve un puntero couto cualquier otra secuencia?
Peter Cordes

Vaya, << (bitand std::cout)->fill()compila, pero usa -. (A pesar del nombre del token, bitandes solo un equivalente léxico &, no específicamente el operador bit a bit. También funciona como la dirección del operador). Hmm, ¿hay alguna plantilla o material lambda que pueda obtener un puntero a una función miembro? que podemos ()sin usar .o ->?
Peter Cordes

1
La única otra cosa que encontré es que std::ios::leftse define como 32, en gcc, pero realmente no pude encontrar una manera de aprovechar eso. Creo que voy a dejar ir este y hacer un trabajo real :-)
Johan du Toit

@JohanduToit: Crear un int32 no es un problema, mi respuesta ya muestra cómo hacerlo ++comenzando desde un int c{};cero. Pero sí, no voy por el agujero del conejo de mirar lambdas, plantillas o std::function. O la std::stringidea Pero no estamos acostumbrados ga que en realidad no podamos declarar un std::stringsin perder; mi idea de usar en gotolugar de forno funcionó. decltype(something)podría darnos un chartipo, pero nos cuesta a y.
Peter Cordes

1
Puede usar auto en lugar de char para el operador: struct ss : std::ostream { operator auto () { return fill(); } };pero no ayuda mucho.
Johan du Toit

7

C ++ (gcc) x86_64 solo Linux, 9295 8900 8712 6812 5590 bytes, 18 caracteres únicos

int m[]={111111111111111+1111111111111+1111111111111+1111111111111+1111111111111+1111111111111+1111111111111+111111111+111111111+1111111+111111+11111+11111+11+11+11+11+11+1+1+1,11111111111+11111111111+11111111111+1111111111+111111111+111111111+111111111+111111+1111+1111+111+111+111+111+11+11+11+11+11+11+11+11+11+1+1+1,111111111111111+111111111111111+111111111111+111111111111+1111111111+1111111+1111111+11111+11111+11111+1111+111+111+11+11+11+11+11+11+11+11+11+1+1+1+1+1+1+1+1+1+1,111111111111111+111111111111111+1111111111111+1111111111111+11111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+11111+1111+111+111+11+1+1+1,1111111111111+1111111111111+11111111111+11111111111+1111111111+1111111111+1111111111+111111+11111+11111+11111+11111+1111+1111+1111+1111+111+111+111+11+11+11+11+11+11,11111111111111+1111111111111+11111111111+11111111111+11111111111+1111111111+111111111+11111111+11111111+11111111+11111111+1111+1111+1111+1111+1111+1111+1111+1111+1111+111+1+1+1+1,111111111111111+1111111111111+1111111111111+1111111111111+1111111111111+11111111111+11111111111+1111111+11111+11111+1111+1111+11+11+11+11+11+11+11+1+1+1+1,111111111111+11111111111+1111111111+1111111111+1111111111+1111111111+1111111111+1111111111+11111111+11111+11111+11111+11111+11111+11111+1+1,111111111111111+11111111111111+11111111111+11111111111+1111111111+1111111+1111111+11111+111+111+111+111+111+111+111+111+11+11+1+1+1+1+1+1,11111111111+1111111111+111111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+11+1+1+1+1+1+1+1,111111111111+11111111111+11111111111+11111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+111111+111111+11111+11111+111+111+111+111+111+111+111+1+1+1+1+1+1+1,11==1,1111111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+11+11+11+1+1+1,1111111111111+111111111111+11111111111+1111111111+111111111+111111111+11111111+111111+111111+111111+11111+1111+111+111+1+1,111111111111+111111111111+11111111111+11111111111+11111111111+11111111111+111111111+111111111+11111111+111111+1111+1111+111+111+111,111111111111+11111111111+1111111111+1111111111+111111111+1111111+111+111+1+1+1+1,111111111111111+11111111111111+1111111111111+1111111111111+111111111111+1111111111+1111111111+1111111111+1111111+111111+111111+111111+11111+11111+11111+1111+1111+111+11+11+1+1+1+1,111111111111111+1111111111111+1111111111111+11111111111+1111111111+11111111+11111111+1111+1111+1111+111+111+111+111+11+11,111111111+111111111+11111111+11111111+11111111+1111111+1111111+111111+11111+1111+1111+1111+1111+111+111+11+11+11+11+11+1+1+1+1+1+1+1+1,11111111111111+111111111111+111111111111+11111111111+111111111+111111+111111+111111+1111+1111+1111+1+1+1+1+1+1+1+1,11111111111+11111111111+11111111111+11111111111+1111111111+1111111111+11111111+1111111+1111111+1111111+1111111+111111+11111+11+11+11+1+1+1+1+1+1+1+1,111111111111111+111111111111111+111111111111+1111111111+1111111111+11111111+11111111+1111111+1111111+111111+111111+11111+11111+111+11+11+1+1+1+1+1+1+1+1+1+1,11111111111111+11111111111111+111111111111+11111111111+11111111111+1111111+1111111+1111111+1111111+1111111+1111111+11+11+11+11+11+11+11+11+1,11111111111111+11111111111111+11111111111+1111111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+11111+11111+1111+1111+1111+111+111+111+111+111+111+11,111111111111111+1111111111111+111111111111+111111111111+111111111111+11111111111+1111111111+1111111111+111111111+111111+111111+111111+111111+1111+11+1+1,111111111111111+11111111111111+111111111111+111111111111+1111111111+1111111111+111111111+11111111+1111+1111+1111+111+111+111+111+111+11+11+11+11+11+11+11+11+1+1+1+1,11111111111111+11111111111111+11111111111111+11111111111+11111111111+1111111111+11111111+1111111+11111+11111+11111+1111+111+111+111+11+11+11+11+1+1+1+1+1+1,111111111111111+11111111111111+1111111111+111111111+111111111+111111111+11111111+1111111+111111+11111+1111+1111+1111+111+111+111+111+111+111+11+11+11+11+11+11+11+11+11+1+1+1+1,111111111111111+1111111111111+1111111111111+1111111111111+1111111111+111111111+111111111+111111111+11111111+1111111+11111+1111+1111+1111+111+111+111+11,1111111111111+1111111111+11111111+11111111+11111111+11111+1111+111+111+11+11+11+11+11+11+11+11+11+1+1+1+1+1+1+1+1+1+1,11111111111111+1111111111+1111111111+111111111+11111111+1111111+1111111+1111111+111111+11111+11111+11111+11111+11111+1111+1111+1111+111+111+11+11+11+11+11+11+11+1+1+1+1+1+1+1,11111111111111+1111111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+11111+1111+1111+111+111+111+111+111+111+1+1+1+1+1+1,111111111111111+1111111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+111111+11111+11111+11111+1111+111+111+111+11+11+11+11+11,1111111111+111111111+1111111+1111111+111111+111111+11111+11111+11111+11111+11111+11111+1111+1111+1111+11+11+11+11+11+11+11+11+11+1+1+1,111111111111111+111111111111+111111111111+111111111111+11111111111+1111111111+1111111111+1111111111+11111111+11111+1111+1111+111+111+111+111+111+111+111+111+1,1111111111+111111111+111111111+11111111+1111111+1111111+1111111+111111+11111+11111+11111+11111+11111+111+111+111+11+11+11+1,11111111111111+11111111111111+1111111111+1111111111+1111111111+1111111111+11111111+11111111+11111111+11111111+1111111+1111111+111+111+111+111+11+11+11+11+11+11+11+1+1,111111111111+11111111111+1111111111+111111111+111111111+111111+111111+111111+111111+11111+11111+11+11+11+11+11+1,111111111+11111+11111+111+11+1+1+1+1+1+1+1+1+1};main(){((int(*)())m)();}

Pruébalo en línea!

Esto se basa en ideas de esta respuesta PPCG . Un programa de lenguaje de máquina se expresa como una matriz de entradas de 32 bits, cada una de las cuales se representa como una suma de 1+11+111.... Resulta que puede ser más eficiente codificar xcomo ytal que y%(1<<32)==x. El programa de lenguaje de máquina codificado es el siguiente

0x0000000000000000:  55                         push    rbp
0x0000000000000001:  31 ED                      xor     ebp, ebp
0x0000000000000003:  53                         push    rbx
0x0000000000000004:  48 83 EC 18                sub     rsp, 0x18
0x0000000000000008:  48 8D 74 24 0C             lea     rsi, [rsp + 0xc]
0x000000000000000d:  31 C0                      xor     eax, eax
0x000000000000000f:  31 FF                      xor     edi, edi
0x0000000000000011:  6A 01                      push    1
0x0000000000000013:  5A                         pop     rdx
0x0000000000000014:  0F 05                      syscall 
0x0000000000000016:  89 C3                      mov     ebx, eax
0x0000000000000018:  85 C0                      test    eax, eax
0x000000000000001a:  74 0C                      je      0x28
0x000000000000001c:  6B ED 0A                   imul    ebp, ebp, 0xa
0x000000000000001f:  03 6C 24 0C                add     ebp, dword ptr [rsp + 0xc]
0x0000000000000023:  83 ED 30                   sub     ebp, 0x30
0x0000000000000026:  EB E0                      jmp     8
0x0000000000000028:  C7 44 24 0C 00 00 00 00    mov     dword ptr [rsp + 0xc], 0
0x0000000000000030:  FF C3                      inc     ebx
0x0000000000000032:  8B 44 24 0C                mov     eax, dword ptr [rsp + 0xc]
0x0000000000000036:  8D 78 01                   lea     edi, [rax + 1]
0x0000000000000039:  89 7C 24 0C                mov     dword ptr [rsp + 0xc], edi
0x000000000000003d:  E8 27 00 00 00             call    0x69
0x0000000000000042:  6A 20                      push    0x20
0x0000000000000044:  48 89 E6                   mov     rsi, rsp
0x0000000000000047:  52                         push    rdx
0x0000000000000048:  58                         pop     rax
0x0000000000000049:  50                         push    rax
0x000000000000004a:  5F                         pop     rdi
0x000000000000004b:  0F 05                      syscall 
0x000000000000004d:  5E                         pop     rsi
0x000000000000004e:  39 5C 24 0C                cmp     dword ptr [rsp + 0xc], ebx
0x0000000000000052:  7C DE                      jl      0x32
0x0000000000000054:  6A 0A                      push    0xa
0x0000000000000056:  48 89 E6                   mov     rsi, rsp
0x0000000000000059:  52                         push    rdx
0x000000000000005a:  58                         pop     rax
0x000000000000005b:  0F 05                      syscall 
0x000000000000005d:  5E                         pop     rsi
0x000000000000005e:  39 DD                      cmp     ebp, ebx
0x0000000000000060:  7F C6                      jg      0x28
0x0000000000000062:  48 83 C4 18                add     rsp, 0x18
0x0000000000000066:  5B                         pop     rbx
0x0000000000000067:  5D                         pop     rbp
0x0000000000000068:  C3                         ret     
0x0000000000000069:  85 FF                      test    edi, edi
0x000000000000006b:  74 2C                      je      0x99
0x000000000000006d:  89 F8                      mov     eax, edi
0x000000000000006f:  6A 0A                      push    0xa
0x0000000000000071:  59                         pop     rcx
0x0000000000000072:  48 83 EC 18                sub     rsp, 0x18
0x0000000000000076:  99                         cdq     
0x0000000000000077:  F7 F9                      idiv    ecx
0x0000000000000079:  89 C7                      mov     edi, eax
0x000000000000007b:  8D 42 30                   lea     eax, [rdx + 0x30]
0x000000000000007e:  89 44 24 0C                mov     dword ptr [rsp + 0xc], eax
0x0000000000000082:  E8 E2 FF FF FF             call    0x69
0x0000000000000087:  48 8D 74 24 0C             lea     rsi, [rsp + 0xc]
0x000000000000008c:  6A 01                      push    1
0x000000000000008e:  58                         pop     rax
0x000000000000008f:  50                         push    rax
0x0000000000000090:  5F                         pop     rdi
0x0000000000000091:  50                         push    rax
0x0000000000000092:  5A                         pop     rdx
0x0000000000000093:  0F 05                      syscall 
0x0000000000000095:  48 83 C4 18                add     rsp, 0x18
0x0000000000000099:  C3                         ret

... que se basa en el siguiente código C.

void print(int x){
  if( x ) {
    int y=x%10+'0';
    print(x/10);
    write(1,&y,1);
  }
}
void f() {
  int i=0,j=0,k;
  for( ;read(0,&k,1);i=i*10+k-'0' );
  do {
    for( j++,k=0; print( ++k ), write(1," ",1), k<j; );
    write(1,"\n",1);
  } while(j<i );
}

Editar: ahora acepta entradas de en stdinlugar de argv[1]. ¡Gracias a @ ASCII-only y @PeterCordes por sus sugerencias!

Edit4: codificación ligeramente mejorada significativamente.


-wpls (bandera también: P puede cambiar el nombre iia a)
ASCII de sólo

Necesitas gcc -zexecstackesto, ¿verdad? Porque int m[]no lo es const. (Y las cadenas de herramientas recientes ponen .rodataen una página no ejecutable de todos modos, por lo que incluso const int m[]no funciona en, por ejemplo, mi sistema Arch Linux con gcc8.2.1 20181127 y ld(GNU Binutils) 2.31.1.) De todos modos, olvidó mencionar eso en su respuesta, pero está en tu enlace TIO.
Peter Cordes

Por cierto, el algoritmo de puntuación de recuento único del OP no cuenta el espacio y la nueva línea, por lo que no tiene que hacer que todo sea terrible de leer, solo la matriz: P
Peter Cordes

Puede guardar bytes de código de máquina copiando 1con push %rax/ en pop %rdilugar de otro envío inmediato. O más simplemente, para valores que no son de 64 bits, es decir, no punteros, de 2 bytes mov %eax, %edi. Además, Linux syscallno destruye sus registros de entrada, solo raxcon el valor de retorno y RCX + R11 con RIP y RFLAGS guardados como parte de cómo funciona la syscallinstrucción. Por lo tanto, puede salir rdiy rdxestablecer 1llamadas cruzadas, y usar diferentes registros. Además, RBX tiene llamadas preservadas, por lo que en realidad no se guarda en RBX de clobber main. Sucede que funciona porque el código de inicio CRT no le importa.
Peter Cordes

6

21 personajes únicos + 1 nueva línea inamovible

%:include<iostream>
int(n)(int(i))<%
    if(--i)if(n(i))<%%>
    if(i)if(std::cout<<i<<std::addressof(std::cout)->fill())<%%>
%>
int(l)(int(i))<%
    if(n(-(--i)))<%%>
%>
int(m)(int(i))<%
    if(--i)if(m(i))<%%>
    if(i)if(l(-i))<%%>
    if(i)if(std::cout<<std::endl)<%%>
%>
int(c)(int(i))<%
    if(m(-(--i)))<%%>
%>
int(main)(int(i))<%
    if(std::cin>>i)<%%>
    if(c(-i))<%%>
%>

No se requieren espacios en blanco excepto la primera línea nueva. Compilado en g ++ 7.3.0.

Caracteres usados: %:include<ostram>()f- .

Mejoras a otras respuestas:

  1. Se eliminaron los punto y coma cambiando for bucles ify recursividad.
  2. Consiguió el personaje espacial por std::addressof(std::cout)->fill(), alias std::cout.fill().

std :: addressof, agradable!
Johan du Toit

2

21 20 caracteres únicos excluyendo espacios en blanco

Todos los espacios en blanco podrían cambiarse a nuevas líneas.

%:include<iostream>
%:include<list>
int n;
const int co<%%>;
const int ci<%not co%>;
const int cmu<%-ci-ci-ci-ci%>;
const char ctd<%-cmu-cmu-cmu-cmu-cmu-cmu-cmu-cmu%>;
const int cia<%-ctd-ctd-ctd-ctd-ctd-cmu%>;
const int ciu<%cia- -ci- -ci%>;

struct<%struct<%struct<%struct<%struct<%struct<%struct<%
int c<:ctd-ci:>;
%>d<:ctd:>;int c<:ctd-ci:>;%>d<:ctd:>;int c<:ctd-ci:>;
%>d<:ctd:>;int c<:ctd-ci:>;%>d<:ctd:>;int c<:ctd-ci:>;
%>d<:ctd:>;int c<:ctd-ci:>;%>d<:-cmu:>;int c<:-ci-cmu:>;
%>e<:co:><:ctd:><:ctd:><:ctd:><:ctd:><:ctd:><:ctd:>;

int i<:co:>;
auto ia<%e%>;
auto iu<%e%>;
int l<%std::cin>>n and co%>;

struct s<%
    int c<%std::cout<<i<:ciu:>- --i<:cia:><<ctd and n%>;
%>;
struct o<%
    int c<%--ia and n%>;
%>;
struct t<%
    std::list<s>c<%- --l%>;
    std::list<o>r<%-l%>;
    int m<%std::cout<<std::endl and n%>;
%>;
std::list<t>a<%n%>;
int main;

Salidas con segfault. Los caracteres utilizados: %:include<ostram>;-h.

Funciona en esta versión específica del compilador en un Linux de 64 bits:

g++-5 (Ubuntu 5.5.0-12ubuntu1) 5.5.0 20171010

Con el parámetro:

-std=c++17

Incluso entonces, no estoy seguro de que siempre funcione. También puede depender de muchas otras cosas. ciay ciuson las compensaciones de memoria divididas por 4 entre ia iuy i. ( intes de 32 bits en esta versión). Puede que tenga que cambiar los números para que coincidan con el desplazamiento real. Las direcciones serían mucho más predecibles si están todas contenidas en una estructura. Lamentablemente, no estático autono está permitido en una estructura.

ees una matriz de 0 elementos de un tipo de elemento con un tamaño de (2 32 -1) × 2 32 bytes. Si ese disminuye el tipo de puntero correspondiente , la mitad superior del puntero se disminuiría en (2 32 -1), lo que equivale a incrementar en uno. Esto podría restablecer el contador decrementado sin usar el signo de igualdad.

Una versión más razonable que debería funcionar de manera más confiable, pero utiliza un personaje más =:

%:include<iostream>
%:include<list>
int n;
int ci<%not n%>;
int cmu<%-ci-ci-ci-ci%>;
char ctd<%-cmu-cmu-cmu-cmu-cmu-cmu-cmu-cmu%>;
int i;
int l<%std::cin>>n and n-n%>;

struct s<%
    int c<%std::cout<<- --i<<ctd and n%>;
%>;
struct t<%
    std::list<s>c<%- --l%>;
    int r<%i=n-n%>;
    int m<%std::cout<<std::endl and n%>;
%>;
std::list<t>a<%n%>;
int main;

Incluso esto no funciona en la última versión de g ++ porque ya no parece permitir la definición mainen un tipo arbitrario.

Estos dos programas no usan paréntesis. Pero los puntos y comas no parecen ser evitables.


1

22 caracteres únicos, excluyendo espacios en blanco. Separa los números con un carácter NUL que se muestra correctamente en Windows.

%:include<iostream>
int main(int n)<%
    std::cin>>n;
    for(int r<%%>;r++<n;)<%
        for(int i<%%>;i<r;)
            std::cout<<++i<<std::ends;
        std::cout<<std::endl;
    %>
%>

Pruébalo en línea

Histograma:

[%] 0x25 = 9
[:] 0x3A = 11
[)] 0x29 = 3
[i] 0x69 = 11
[n] 0x6E = 12
[c] 0x63 = 4
[l] 0x6C = 2
[u] 0x75 = 3
[d] 0x64 = 8
[e] 0x65 = 4
[<] 0x3C = 13
[o] 0x6F = 5
[s] 0x73 = 7
[t] 0x74 = 12
[r] 0x72 = 6
[a] 0x61 = 2
[m] 0x6D = 2
[>] 0x3E = 7
[(] 0x28 = 3
[;] 0x3B = 7
[f] 0x66 = 2
[+] 0x2B = 4
Unique Characters: 22
Total Characters: 189

std :: ends es un carácter NUL ( char(0)), no un espacio ( char(32)en ASCII / UTF-8). en.cppreference.com/w/cpp/io/manip/ends . Lo probé en mi escritorio de Linux solo para asegurarme, y la salida se ve así 1234, no 1 2 3 4. ¡Se ve de la misma manera en su salida TIO!
Peter Cordes

@PeterCordes, el OP no especifica cómo se deben separar los números ;-)
Johan du Toit

¿De verdad cree que habrían perdido un personaje de "por " "si podían haber utilizado iiiipara separar con '0'a 10203040? Supongo que puede argumentar que todavía hay un separador en la salida binaria del programa, pero señalar este cambio y describirlo en inglés es importante para su respuesta, ¡porque este no es un reemplazo directo! Estaré encantado de eliminar mi voto negativo si expande su respuesta para explicar y justificar eso.
Peter Cordes

1
@PeterCordes, Punto tomado.
Johan du Toit
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.