Gere escada de números inteiros usando o menor número de caracteres exclusivos (em C ++)

13

Eu sou novo no esporte do código de golfe. Eu estou tentando gerar uma escada de números inteiros usando o menor número de caracteres exclusivos em C ++.

Digamos que recebemos um número inteiro 4.

Vamos gerar a seguinte escada:

1
1 2
1 2 3
1 2 3 4

Em resumo, meu programa lerá um número inteiro positivo de stdin e imprimirá essa escada na saída. Estou tentando fazer isso com o menor número possível de caracteres únicos .

Meu programa é o seguinte:

#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;
    }
}

Aqui está o verificador que eu usei para verificar o número de caracteres únicos no meu 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;
}

De preferência, desejo usar menos de 25 caracteres únicos para concluir este programa (excluindo caracteres de nova linha, mas incluindo espaço em branco). Atualmente, meu programa usa 27. Não sei como otimizá-lo ainda mais.

Alguém poderia me aconselhar sobre como otimizá-lo ainda mais (em termos do número de caracteres únicos usados)? Observe que apenas C ++ pode ser usado.

LanceHAOH
fonte
5
Certamente, é uma novidade pedir dicas sobre qualquer outro critério de pontuação que não o code-golf , mas o assunto é tópico, uma vez que as páginas de dicas sugerem uma resposta melhor a um desafio de programação relacionado ao tópico .
Adám 13/05/19
8
@LuisMendo Eu realmente não acho que isso seja verdade neste caso, já que muitos idiomas banalizam completamente esse esquema de pontuação. Se esse usuário quer ajudar a aprender a "jogar golfe exclusivo", isso realmente só faz sentido em um subconjunto de idiomas, então acho que isso é muito melhor como uma dica do que como um desafio genérico. Dito isto, o problema básico provavelmente pode ser um desafio se alguém quiser publicá-lo.
FryAmTheEggman
3
Acho que você pode usar dígrafos <% e%> em vez de chaves, e acho que perdi alguns.
meu pronome é monicareinstate 14/05/19
2
Eu definitivamente senti falta de alguns. # é% :, então você pode se livrar de três caracteres e introduzir um ({=> <%,} =>%>, # =>% :) e chegar a 25. Se você combinar isso com a resposta abaixo, eu acho que você pode obter 24.
meu pronome é monicareinstate
2
Os trígrafos do @LanceHAOH são extremamente comuns em perguntas [dissimuladas], e os dígrafos também aparecem ao ler sobre trigramas.
meu pronome é monicareinstate

Respostas:

12

Acredito que consegui remover o caractere = do seu código, embora agora seja significativamente mais 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;
    }
}

Não é bonito, mas ao abusar do excesso de números inteiros, podemos voltar a 0 sem usar =

Também tivemos que mudar um pouco os guardas. Infelizmente, por causa da inclusão, não consegui me livrar de todos os novos caracteres de linha (embora estejam próximos), para que possa ser o próximo caminho para investigação.

Edit: Sem tempo, por enquanto, mas se você incluir e usar strstream e várias outras bibliotecas, acho que também poderá remover o caractere ", novamente usando números inteiros para chegar ao caractere correto por espaço e passá-lo para o corrente

Dados expirados
fonte
2
Você poderia #include<std>e eliminar todos os :s. Não é uma boa prática de codificação, mas isso não vem ao caso.
Darrel Hoffman
3
@DarrelHoffman eu não posso chegar a esse trabalho, você não tem que fazer using namespace std;o que usar um p adicional para o: assim uma rede 0
Expirado dados
Hmm. Talvez meu C ++ esteja um pouco enferrujado. Também isso adiciona uma gperda tão líquida, eu acho. Se fosse um código de ouro, poderíamos reduzir a contagem de bytes renomeando ii, iiie iiiipara outros nomes de letras únicas (escolha outras letras já usadas), mas não é esse o objetivo deste desafio, então acho que não. Eu estou querendo saber se haveria algum ganho para usar getce, em putcvez de cin/ cout, teria que tentar.
Darrel Hoffman
1
Foi mal. Acabei de ler o verificador novamente. Parece que o caractere de nova linha é ignorado. Portanto, não há realmente necessidade de se preocupar em remover novas linhas. Mas combinado com sua estratégia e a solução de @someone nos comentários, consegui obter 24 caracteres. Tornei o programa ainda mais rápido usando short em vez de int. Então, eu tenho um personagem 'h' adicional. Mas isso permite que eu use o tipo de dados char sem custo adicional. Então, eu me livrei do "caráter bem por meio de código de caracteres.
LanceHAOH
@LanceHAOH: observe que o excesso de número inteiro assinado é um comportamento indefinido em C ++, para todos os tipos de assinaturas, inclusive signed char. Se você compilar com a otimização ativada, esse código poderá ser rompido com os compiladores modernos, a menos que você use gcc -fwrapvpara tornar o estouro assinado bem definido como o complemento do 2. clang suporta -fwrapvtambém. ( unsignedtipos inteiros, incluindo unsigned charum comportamento bem definido (wrap-around) no ISO C ++). Depende da ABI se charé signed charou unsigned char, então charpode ser ok.
Peter Cordes
10

Finalmente consegui 24 caracteres únicos, combinando as respostas de @ExpiredData e @someone. Além disso, o uso do tipo de dados curto em vez de int ajudou a acelerar o meu programa, pois leva um tempo menor para estourar um tipo de dados curto.

Meu código é o seguinte.

%: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;
    %>
%>
LanceHAOH
fonte
@KevinCruijssen ele usa char iiiii;, a última das inicializações de variáveis.
Rɪᴋᴇʀ
1
@KevinCruijssen Isso é verdade. Mas isso permite que eu remova o caractere "porque eu posso usar o código de caractere para representar o caractere de espaço. Portanto, a diferença líquida em caracteres únicos usados ​​= 0.
LanceHAOH
9

23 caracteres únicos usando Digraphs. (25 sem). Sem UB.

Use a sintaxe do inicializador em C ++ 11 para inicializar em lista um número inteiro para zero, int var{};evitando =e 0. (Ou no seu caso, evitando global iiii). Isso fornece uma fonte de zeros diferentes de variáveis ​​globais (que são inicializadas estaticamente para zero, diferentemente dos locais).

Os compiladores atuais aceitam essa sintaxe por padrão, sem precisar habilitar nenhuma opção especial.

(O truque envolvente de número inteiro é divertido e aceitável para jogar golfe com a otimização desativada, mas o estouro assinado é um comportamento indefinido no ISO C ++. A ativação da otimização transformará esses loops em envolventes em loops infinitos, a menos que você compile com gcc / clang -fwrapvpara fornecer um estouro inteiro assinado comportamento definido: complemento do 2 envolvente.

Curiosidade: o ISO C ++ std::atomic<int>possui um complemento bem definido do 2! int32_té necessário que seja o complemento de 2, se definido, mas o comportamento de estouro é indefinido, portanto ainda pode ser um typedef para intou longem qualquer máquina em que um desses tipos seja de 32 bits, sem preenchimento e o complemento de 2).


Não é útil para este caso específico:

Você também pode inicializar uma nova variável como uma cópia de uma existente, com chaves ou (com um inicializador não vazio), parens para inicialização direta .
int a(b)ou int a{b}são equivalentes aint a = b;

Mas int b();declara uma função em vez de uma variável inicializada como zero.

Além disso, você pode obter um zero com int()ou char(), ou seja, com a inicialização zero de um objeto anônimo.


Podemos substituir suas <=comparações por <comparadas por uma transformação lógica simples : faça o incremento do contador de loop logo após a comparação, em vez de na parte inferior do loop. Na IMO, isso é mais simples do que as alternativas que as pessoas propuseram, como usar ++na primeira parte de a for()para transformar 0 em 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;
    }

Poderíamos jogar isso até a for(int r{}; r++ < n;)IMO, mas é menos fácil para os humanos lerem. Não estamos otimizando a contagem total de bytes.


Se já estivéssemos usando h, poderíamos salvar o espaço 'ou "um espaço.

Assumindo um ambiente ASCII ou UTF-8, o espaço é um charcom valor 32. Podemos criar isso em uma variável com bastante facilidade e, em seguida,cout << 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

E outros valores podem obviamente ser criados a partir de uma sequência ++e duplicação, com base nos bits de sua representação binária. Mudando efetivamente um 0 (nada) ou 1 (++) para o LSB antes de dobrar para uma nova variável.


Esta versão usa em hvez de 'ou ".

É muito mais rápido que qualquer uma das versões existentes (sem depender de um loop longo) e está livre de comportamento indefinido . Ele compila sem advertências com g++ -O3 -Wall -Wextra -Wpedantice comclang++ . -std=c++11é opcional. É ISO C ++ 11 legal e portátil :)

Também não depende de variáveis ​​globais. E tornei mais legível para humanos com nomes de variáveis ​​que têm um significado.

Contagem de bytes únicos: 25 , excluindo os comentários com os quais eu removig++ -E . E excluindo espaço e nova linha como seu contador. Eu usei sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic neste askubuntu para contar ocorrências de cada caractere e canalizei isso wcpara contar quantos caracteres únicos eu tinha.

#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;
    }
}

Os únicos 2 fcaracteres são de for. Em whilevez disso, poderíamos usar loops se tivéssemos um uso w.

Poderíamos reescrever os loops em um estilo de linguagem assembly i < r || goto some_label;para escrever um salto condicional na parte inferior do loop, ou o que seja. (Mas usando em orvez de ||). Não, isso não funciona. gotoé uma declaração como ife não pode ser um subcomponente de uma expressão como no Perl. Caso contrário, poderíamos usá-lo para remover os caracteres (e ).

Poderíamos negociar fpara gcom if(stuff) goto label;em vez de for, e ambos os loops sempre executar pelo menos 1 iteração de modo que só iria precisar de um loop-filial na parte inferior, como uma normal, asm do{}whileestrutura de loop. Supondo que o usuário insira um número inteiro> 0 ...


Digraphs e Trigraphs

Felizmente, os trigrafos foram removidos a partir da ISO C ++ 17, portanto, não precisamos usá-lo em ??>vez de }jogar golfe exclusivo para a revisão mais recente do C ++.

Mas apenas trigramas especificamente: a ISO C ++ 17 ainda possui dígrafos como :>para ]e %>para} . Portanto, com o custo de uso %, podemos evitar ambos {e }, e usá- %:los #para uma economia líquida de menos 2 caracteres únicos.

E o C ++ possui palavras-chave notdo !operador, como para o operador ou bitorpara o |operador. Com xor_eqfor ^=, você pode zerar uma variável com i xor_eq i, mas ela possui vários caracteres que não estava usando.

A corrente g++já ignora trigramas por padrão, mesmo sem -std=gnu++17; você precisa usá -trigraphs-los para habilitá-los, ou -std=c++11algo para uma estrita conformidade com um padrão ISO que os inclui.

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;
    %>
%>

Experimente online!

A versão final usa 'aspas simples em vez de hou "para o separador de espaço. Eu não queria digrafar as char c{}coisas, então as apaguei. Imprimir um char é mais eficiente do que imprimir uma string, então usei isso.

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

O separador de espaço (ainda não resolvido)

Em uma resposta agora excluída, Johan Du Toit propôs o uso de um separador alternativo, especificamente std::ends. Esse é um caractere NUL char(0), e é impresso como largura zero na maioria dos terminais. Portanto, a saída seria semelhante 1234, não 1 2 3 4. Ou pior, separados por lixo em qualquer coisa que não entrou em colapso silenciosamente '\0'.

Se você pode usar um separador arbitrário, quando 0é fácil criar o dígito cout << some_zeroed_var. Mas ninguém quer 10203040, isso é ainda pior do que nenhum separador.

Eu estava tentando pensar em uma maneira de criar um std::stringholding a" " sem usar charou uma string literal. Talvez acrescentando algo a isso? Talvez com um dígrafo para []definir o primeiro byte como um valor de 32, depois de criar um com comprimento 1 por meio de um dos construtores?

Johan também sugeriu a std::iosfunção de membro fill () que retorna o caractere de preenchimento atual. O padrão para um fluxo é definido por std::basic_ios::init()e é ' '.

std::cout << i << std::cout.fill();substitui << ' ';mas usa em .vez de' .

Com -, podemos tomar um ponteiro para coute uso ->fill()para chamar a função de membro:
std::cout << (bitand std::cout)->fill(). Ou não, nós também não estávamos usando b, então poderíamos ter usado em &vez de seu equivalente lexical bitand.

Chamar uma função de membro sem .ou->

Coloque-o dentro de uma classe e defina 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(); }
}

Depois, ss s{}antes do loop, e std::cout << i << s;dentro do loop. Ótimo, ele compila e funciona corretamente, mas tivemos que usar pe hpara operator char(), com uma perda líquida de 1. Pelo menos, evitamos bcriar funções de membro publicusando em structvez de class. (E podemos substituir a herança protectedcaso isso ajude).

Peter Cordes
fonte
@JohanduToit: boa ideia com cout.fill()fromstd::ios , mas não estávamos usando anteriormente. . Talvez possamos chamá-lo de alguma forma pegando um ponteiro e usando ->fill()uma função de membro? Alguma coisa retorna um ponteiro para coutou para qualquer outro fluxo?
Peter Cordes
Opa, << (bitand std::cout)->fill()compila, mas usa -. (Apesar do nome do token, bitandé apenas um equivalente lexical &, e não especificamente o operador bit a bit. Também funciona como o endereço do operador.) Hmm, há algum modelo ou material lambda que pode direcionar uma função de membro que podemos ()sem usar .ou ->?
Peter Cordes
1
A única outra coisa que encontrei é std::ios::leftdefinida como 32, no gcc, mas não consegui descobrir uma maneira de tirar proveito disso. Eu acho que eu vou deixar isso ir e obter algum trabalho real feito :-)
Johan du Toit
@JohanduToit: Criar um int32 não é um problema, minha resposta já mostra como fazer isso ++partindo do int c{};zero. Mas sim, eu não vou descer pela toca do coelho olhando para lambdas, modelos ou std::function. Ou a std::stringideia. Mas não estamos acostumados ga, na verdade, não podemos declarar um std::stringsem perder; minha ideia de usar em gotovez de fornão deu certo. decltype(something)poderia nos dar um chartipo, mas nos custa a y.
Peter Cordes
1
Você pode usar auto em vez de char para o oponente: struct ss : std::ostream { operator auto () { return fill(); } };mas isso não ajuda muito.
Johan du Toit
7

C ++ (gcc) x86_64 somente Linux, 9295 8900 8712 6812 5590 bytes, 18 caracteres exclusivos

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)();}

Experimente online!

Isso é baseado nas idéias desta resposta do PPCG . Um programa de linguagem de máquina é expresso como uma matriz de 32 bits de ints, cada um dos quais é representado como uma soma de 1+11+111.... Acontece que pode ser mais eficiente codificar xcomo ytal y%(1<<32)==x. O programa de linguagem de máquina codificado é o seguinte

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 baseia no seguinte 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: agora aceita entrada de em stdinvez de argv[1]. Obrigado a @ ASCII-only e @PeterCordes por suas sugestões!

Edit4: Codificação ligeiramente melhorada significativamente.

teto
fonte
-wflag pls: P (também é possível renomear iipara a)
somente ASCII
Você precisa gcc -zexecstackdisso, certo? Porque int m[]não é const. (E cadeias de ferramentas recentes colocadas .rodataem uma página não executável de qualquer maneira, mesmo const int m[]assim não funcionam, por exemplo, no meu sistema Arch Linux com gcc8.2.1 20181127 e ld(GNU Binutils) 2.31.1.) De qualquer forma, você esqueceu de mencionar isso em sua resposta, mas está no seu link do TIO.
Peter Cordes
BTW, algoritmo de pontuação de contagem exclusivo do OP não conta espaço e nova linha, para que você não tem que fazer a coisa toda terrível para ler, apenas o array: P
Peter Cordes
Você pode salvar bytes de código de máquina copiando o 1com push %rax/ em pop %rdivez de outro push-imediato. Ou, mais simplesmente, para valores que não são de 64 bits, ou seja, não ponteiros, 2 bytes mov %eax, %edi. Além disso, o Linux syscallnão destrói seus registros de entrada, apenas raxcom o valor de retorno e RCX + R11 com RIP e RFLAGS salvos como parte de como as syscallinstruções funcionam. Assim, você pode sair rdie rdxdefinir 1entre chamadas e usar diferentes regs. Além disso, o RBX é preservado por chamada, portanto, não é realmente possível salvar o RBX do main. Acontece que funciona porque o código inicial do CRT não se importa.
Peter Cordes
6

21 caracteres únicos + 1 nova linha irremovível

%: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))<%%>
%>

Espaços em branco não são necessários, exceto para a primeira nova linha. Compilado em g ++ 7.3.0.

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

Melhorias em outras respostas:

  1. Removido ponto e vírgula alterando forloops para ife recursão.
  2. Obteve o caractere espacial por std::addressof(std::cout)->fill(), akastd::cout.fill() .
jimmy23013
fonte
std :: addressof, bom!
Johan du Toit
2

21 20 caracteres únicos, excluindo espaços em branco

Todos os espaços em branco podem ser alterados para novas linhas.

%: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;

Sai com segfault. Caracteres usados:%:include<ostram>;-h .

Ele funciona nesta versão específica do compilador em um Linux de 64 bits:

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

Com o parâmetro:

-std=c++17

Mesmo assim, não tenho certeza de que sempre funcionaria. Também pode depender de muitas outras coisas. ciae ciusão os deslocamentos da memória divididos por 4 entre ia iue i. ( inté de 32 bits nesta versão.) Pode ser necessário alterar os números para corresponder ao deslocamento real. Os endereços seriam muito mais previsíveis se todos estiverem contidos em uma estrutura. Infelizmente não estáticoauto não é permitido em uma estrutura.

eé uma matriz de 0 elementos de um tipo de elemento com um tamanho de (2 32 -1) × 2 32 bytes. Se o tipo de ponteiro correspondente efor decrementado, a metade superior do ponteiro será decrementada por (2 32 -1), o que equivale a incrementar um. Isso poderia redefinir o contador decrementado sem usar o sinal de igualdade.

Uma versão mais razoável que deve funcionar de maneira mais confiável, mas usa mais um caractere =:

%: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;

Mesmo isso não funciona na versão mais recente do g ++, porque parece não permitir definir main um tipo arbitrário.

Esses dois programas não usam parênteses. Mas então ponto e vírgula não parece ser evitável.

jimmy23013
fonte
1

22 Caracteres exclusivos, excluindo espaços em branco. Separa os números com um caractere NUL que é exibido corretamente no 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;
    %>
%>

Experimente online

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
Johan du Toit
fonte
std :: ends é um caractere NUL ( char(0)), não um espaço ( char(32)em ASCII / UTF-8). pt.cppreference.com/w/cpp/io/manip/ends . Eu tentei no meu desktop Linux apenas para ter certeza, e a saída parece1234 , não 1 2 3 4. Parece o mesmo na sua saída TIO!
Peter Cordes
@PeterCordes, o OP não especifica como os números devem ser separados ;-)
Johan du Toit
Você realmente acha que eles teriam perdido um personagem em "para " "se eles poderiam ter usado iiiipara separar com '0'a 10203040? Eu acho que você pode argumentar que ainda existe um separador na saída binária do programa, mas apontar essa alteração e descrevê-la em inglês é importante para sua resposta, porque essa não é uma substituição imediata! Ficaria feliz em remover meu voto negativo se você expandir sua resposta para explicar e justificar isso.
Peter Cordes
1
@PeterCordes, ponto utilizado.
Johan du Toit