Générer une échelle d'entiers en utilisant le moins de caractères uniques (en C ++)

13

Je suis nouveau dans le sport du golf de code. J'essaie de générer une échelle d'entiers en utilisant le moins de caractères uniques en C ++.

Disons que l'on nous donne un entier 4.

Nous générerons l'échelle suivante:

1
1 2
1 2 3
1 2 3 4

En bref, mon programme lira un entier positif depuis stdin et imprimera cette échelle dans la sortie. J'essaie de le faire avec le moins de caractères uniques possible.

Mon programme est le suivant:

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

Voici le vérificateur que j'ai utilisé pour vérifier le nombre de caractères uniques dans mon programme:

#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 préférence, je souhaite utiliser moins de 25 caractères uniques pour terminer ce programme (à l'exclusion des caractères de nouvelle ligne, mais y compris les espaces). Actuellement, mon programme utilise 27. Je ne sais pas comment l'optimiser davantage.

Quelqu'un pourrait-il me conseiller sur la façon de l'optimiser davantage (en termes de nombre de caractères uniques utilisés)? Veuillez noter que seul C ++ peut être utilisé.

LanceHAOH
la source
5
Il est certainement nouveau de demander des conseils concernant tout autre critère de notation que le code-golf , mais afaict, c'est sur le sujet, car les pages de conseils disent qu'il s'agit d'une meilleure réponse à un défi de programmation qui est sur le sujet .
Adám
8
@LuisMendo Je ne pense pas vraiment que ce soit vrai dans ce cas, car de nombreuses langues banalisent complètement ce système de notation. Si cet utilisateur veut de l'aide pour apprendre à "jouer au golf unique", cela n'a de sens que dans un sous-ensemble de langues, donc je pense que c'est beaucoup mieux comme astuce que comme défi générique. Cela dit, le problème de base pourrait probablement être un défi si quelqu'un veut le publier.
FryAmTheEggman
3
Je pense que vous pouvez utiliser des digraphes <% et%> à la place des accolades, et je pense que j'en ai manqué.
mon pronom est monicareinstate
2
J'en ai certainement manqué. # est% :, vous pouvez donc vous débarrasser de trois caractères et en introduire un ({=> <%,} =>%>, # =>% :) et passer à 25. Si vous combinez cela avec la réponse ci-dessous, je pense que vous pouvez obtenir 24.
mon pronom est monicareinstate
2
@LanceHAOH Les trigraphs sont extrêmement courants dans les questions [sournoises], et les digraphes apparaissent également lors de la lecture des trigraphes.
mon pronom est monicareinstate

Réponses:

12

Je crois que j'ai réussi à supprimer le caractère = de votre code, bien qu'il soit maintenant beaucoup plus lent

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

Ce n'est pas joli, mais en abusant du débordement d'entier, nous pouvons revenir à 0 sans utiliser =

Nous avons également dû changer un peu les gardes. Malheureusement, à cause de l'inclusion, je ne pouvais pas me débarrasser de tous les nouveaux personnages de ligne (bien que ce soit proche), ce qui pourrait être la prochaine piste d'enquête.

Edit: manque de temps pour l'instant, mais si vous incluez et utilisez strstream et diverses autres bibliothèques, je pense que vous pourrez peut-être aussi supprimer le caractère ", en utilisant à nouveau des entiers pour arriver au caractère correct pour l'espace et en le passant dans le strstream

Données expirées
la source
2
Vous pourriez #include<std>et éliminer tous les :art. Ce n'est pas une excellente pratique de codage, mais ce n'est pas la question.
Darrel Hoffman
3
@DarrelHoffman Je ne peux pas faire fonctionner cela, vous n'avez pas à faire using namespace std;ce qui utiliserait un p supplémentaire pour le: donc un net 0
Données expirées
Hmm. Peut-être que mon C ++ est un peu rouillé. Cela ajoute également une gperte nette, donc je suppose. S'il s'agissait de code or, nous pourrions réduire le nombre d'octets en renommant ii, iiiet en iiiid'autres noms de lettre unique (choisissez toutes les autres lettres déjà utilisées), mais ce n'est pas le but de ce défi, donc je suppose que non. Je me demande s'il y aurait des gains à utiliser getcet putcau lieu de cin/ cout, je devrais l'essayer.
Darrel Hoffman
1
Ma faute. Je viens de relire le vérificateur. Il semble que le caractère de nouvelle ligne soit ignoré. Il n'est donc pas nécessaire de se soucier de supprimer les nouvelles lignes. Mais combiné avec votre stratégie et la solution de @someone dans les commentaires, j'ai réussi à le porter à 24 caractères. J'ai rendu le programme encore plus rapide en utilisant short au lieu de int. J'ai donc obtenu un caractère «h» supplémentaire. Mais cela me permet d'utiliser le type de données char sans coût supplémentaire. Je me suis donc débarrassé du "caractère également en utilisant le code du caractère.
LanceHAOH
@LanceHAOH: notez que le débordement d'entier signé est un comportement non défini en C ++, pour tous les types signés, y compris signed char. Si vous compilez avec l'optimisation activée, ce code peut rompre avec les compilateurs modernes, à moins que vous n'utilisiez gcc -fwrapvpour rendre le débordement signé bien défini comme bouclage du complément à 2. clang prend également en charge -fwrapv. ( unsignedles types entiers, notamment, unsigned charont un comportement bien défini (bouclage) dans ISO C ++). Cela dépend de l'ABI, que ce charsoit signed charou non unsigned char, cela charpeut donc être ok.
Peter Cordes
10

J'ai finalement obtenu 24 caractères uniques en combinant les réponses de @ExpiredData et @someone. En outre, l'utilisation du type de données court au lieu de int a aidé à accélérer mon programme car il prend plus de temps pour déborder un type de données court.

Mon code est le suivant.

%: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
la source
@KevinCruijssen, il l'utilise dans char iiiii;, la dernière des initialisations de variables.
Rɪᴋᴇʀ
1
@KevinCruijssen C'est vrai. Mais cela me permet de supprimer le caractère "car je peux utiliser le code de caractère pour représenter le caractère espace. Donc la différence nette en caractères uniques utilisés = 0.
LanceHAOH
9

23 caractères uniques à l'aide de Digraphs. (25 sans). Pas d'UB.

Utilisez la syntaxe d'initialisation renforcée C ++ 11 pour lister-initialiser un entier à zéro en int var{};évitant =et 0. (Ou dans votre cas, en évitant global iiii). Cela vous donne une source de zéros autres que les variables globales (qui sont initialisées statiquement à zéro, contrairement aux locaux).

Les compilateurs actuels acceptent cette syntaxe par défaut, sans avoir à activer d'options spéciales.

(L'astuce de bouclage entier est amusante, et ok pour le golf avec l'optimisation désactivée, mais le débordement signé est un comportement indéfini dans ISO C ++. L'activation de l'optimisation transformera ces boucles enveloppantes en boucles infinies, sauf si vous compilez avec gcc / clang -fwrapvpour donner un puits de débordement d'entier signé bien - comportement défini: enveloppement du complément à 2.

Fait amusant: ISO C ++ std::atomic<int>a un complément de 2 bien défini! int32_tdoit être le complément de 2 s'il est défini, mais le comportement de débordement n'est pas défini, il peut donc toujours être un typedef pour intou longsur n'importe quelle machine où l'un de ces types est de 32 bits, sans remplissage et complément de 2.)


Inutile pour ce cas spécifique:

Vous pouvez également initialiser une nouvelle variable en tant que copie d'une variable existante, avec des accolades ou (avec un initialiseur non vide), des parens pour l'initialisation directe .
int a(b)ou int a{b}équivalent àint a = b;

Mais int b();déclare une fonction au lieu d'une variable initialisée à zéro.

En outre, vous pouvez obtenir un zéro avec int()ou char(), c'est -à- dire l' initialisation zéro d'un objet anonyme.


Nous pouvons remplacer vos <=comparaisons par des <comparaisons par une simple transformation logique : effectuez l'incrémentation du compteur de boucles juste après la comparaison, plutôt qu'en bas de la boucle. L'OMI est plus simple que les alternatives proposées par les gens, comme utiliser ++dans la première partie de a for()pour transformer 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;
    }

Nous pourrions jouer au golf, for(int r{}; r++ < n;)mais l'OMI est moins facile à lire pour les humains. Nous n'optimisons pas le nombre total d'octets.


Si nous utilisions déjà h, nous pourrions enregistrer le 'ou "pour un espace.

En supposant un environnement ASCII ou UTF-8, l'espace est un charavec une valeur 32. Nous pouvons créer cela dans une variable assez facilement, puiscout << 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

Et d'autres valeurs peuvent évidemment être créées à partir d'une séquence ++et d'un doublement, en fonction des bits de leur représentation binaire. Décaler efficacement un 0 (rien) ou 1 (++) dans le LSB avant de doubler dans une nouvelle variable.


Cette version utilise à la hplace de 'ou ".

Il est beaucoup plus rapide que l'une des versions existantes (ne s'appuyant pas sur une longue boucle) et est exempt de comportement indéfini . Il se compile sans avertissement avec g++ -O3 -Wall -Wextra -Wpedanticet avecclang++ . -std=c++11est facultatif. Il est légal et portable ISO C ++ 11 :)

Il ne dépend pas non plus des variables globales. Et je l'ai rendu plus lisible par l'homme avec des noms de variables qui ont un sens.

Nombre d'octets uniques: 25 , à l' exclusion des commentaires avec lesquels j'ai supprimég++ -E . Et à l'exclusion de l'espace et de la nouvelle ligne comme votre comptoir. J'ai utilisé à sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic partir de cet askubuntu pour compter les occurrences de chaque personnage, et wcje l'ai canalisé pour compter le nombre de caractères uniques que j'avais.

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

Les 2 seuls fcaractères sont de for. Nous pourrions utiliser des whileboucles à la place si nous en avions une utilisation w.

Nous pourrions éventuellement réécrire les boucles dans un style en langage assembleur i < r || goto some_label;pour écrire un saut conditionnel au bas de la boucle, ou autre chose. (Mais en utilisant orau lieu de ||). Non, ça ne marche pas. gotoest une instruction comme ifet ne peut pas être un sous-composant d'une expression comme il le peut en Perl. Sinon, nous aurions pu l'utiliser pour supprimer les caractères (et ).

Nous pourrions échanger fpour gavec if(stuff) goto label;au lieu de for, et les deux boucles exécutent toujours au moins 1 itération, nous n'aurions donc besoin que d'une seule branche de boucle en bas, comme une do{}whilestructure de boucle asm normale . En supposant que l'utilisateur entre un entier> 0 ...


Digraphes et trigraphes

Heureusement, les trigraphes ont été supprimés à partir d'ISO C ++ 17 , nous n'avons donc pas à utiliser à la ??>place de }si nous sommes uniques pour la révision C ++ la plus récente.

Mais uniquement les trigraphes: ISO C ++ 17 a toujours des digraphes comme :>pour ]et %>pour} . Ainsi , au coût d'utilisation %, nous pouvons éviter à la fois {et }, et utiliser %:pour #une économie nette de 2 moins de caractères uniques.

Et C ++ a des mots-clés d'opérateur comme notpour l' !opérateur, ou bitorpour l' |opérateur. Avec xor_eqfor ^=, vous pouvez mettre à zéro une variable avec i xor_eq i, mais elle contient plusieurs caractères que vous n'utilisiez pas.

Le courant g++ignore déjà les trigraphes par défaut même sans -std=gnu++17; vous devez utiliser -trigraphspour les activer, ou -std=c++11quelque chose pour une stricte conformité à une norme ISO qui les inclut.

23 octets uniques:

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

Essayez-le en ligne!

La version finale utilise un 'guillemet simple au lieu de hou "pour le séparateur d'espace. Je ne voulais pas digraphier le char c{}truc alors je l'ai supprimé. L'impression d'un caractère est plus efficace que l'impression d'une chaîne, donc je l'ai utilisé.

Histogramme:

$ 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

Le séparateur d'espace (toujours non résolu)

Dans une réponse maintenant supprimée, Johan Du Toit a proposé d'utiliser un séparateur alternatif, en particulier std::ends. C'est un caractère NUL char(0), et s'imprime en largeur nulle sur la plupart des terminaux. Ainsi, la sortie ressemblerait 1234, non 1 2 3 4. Ou pire, séparés par des ordures sur tout ce qui ne s'est pas effondré en silence '\0'.

Si vous pouvez utiliser un séparateur arbitraire, lorsque le chiffre 0est facile à créer avec cout << some_zeroed_var. Mais personne ne veut 10203040, c'est encore pire que pas de séparateur.

J'essayais de penser à un moyen de créer un std::stringholding a" " sans utiliser charou un string littéral. Peut-être y ajouter quelque chose? Peut-être avec un digraphe pour []définir le premier octet sur une valeur de 32, après en avoir créé un de longueur 1 via l'un des constructeurs?

Johan a également suggéré la std::iosfonction membre fill () qui renvoie le caractère de remplissage actuel. La valeur par défaut pour un flux est définie par std::basic_ios::init()et est ' '.

std::cout << i << std::cout.fill();remplace << ' ';mais utilise à la .place de' .

Avec -, nous pouvons prendre un pointeur vers coutet à utiliser ->fill()pour appeler la fonction de membre:
std::cout << (bitand std::cout)->fill(). Ou pas, nous n'utilisions pas bnon plus, donc nous pourrions aussi bien avons utilisé au &lieu de son équivalent lexical, bitand.

Appel d'une fonction membre sans .ou->

Mettez-le dans une classe et définissez 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(); }
}

Puis ss s{}avant la boucle, et std::cout << i << s;à l'intérieur de la boucle. Génial, il compile et fonctionne correctement, mais nous avons dû utiliser pet hpour operator char(), pour une perte nette de 1. Au moins, nous avons évité bde créer des fonctions membres publicen utilisant structau lieu de class. (Et nous pourrions remplacer l'héritage protectedau cas où cela aiderait).

Peter Cordes
la source
@JohanduToit: bonne idée avec cout.fill()fromstd::ios , mais nous n'utilisions pas auparavant . Peut-être pouvons-nous l'appeler d'une manière ou d'une autre en prenant un pointeur et en utilisant ->fill()une fonction membre? Est-ce que quelque chose renvoie un pointeur vers coutou tout autre flux?
Peter Cordes
Oups, << (bitand std::cout)->fill()compile, mais utilise -. (Malgré le nom du jeton, bitandest juste un équivalent lexical &, pas spécifiquement l'opérateur au niveau du bit et. Il fonctionne également comme l'opérateur de l'adresse.) Hmm, existe-t-il des modèles ou des éléments lambda qui peuvent obtenir un pointeur vers une fonction membre que nous pouvons ()sans utiliser .ou ->?
Peter Cordes
1
La seule autre chose que j'ai trouvée est celle std::ios::leftdéfinie comme 32, dans gcc, mais je ne pouvais pas vraiment trouver un moyen d'en tirer parti. Je pense que je vais laisser tomber celui-ci et faire un travail réel :-)
Johan du Toit
@JohanduToit: Créer un int32 n'est pas un problème, ma réponse montre déjà comment faire cela en ++partant d'un int c{};zéro. Mais ouais, je ne vais pas dans le trou du lapin pour chercher des lambdas, des modèles ou std::function. Ou l' std::stringidée. Mais nous ne l'utilisons gpas, nous ne pouvons pas déclarer un std::stringsans perdre; mon idée d'utiliser gotoau lieu de forn'a pas fonctionné. decltype(something)pourrait nous donner un chartype, mais nous coûte un y.
Peter Cordes
1
Vous pouvez utiliser auto au lieu de char pour l'opeator: struct ss : std::ostream { operator auto () { return fill(); } };mais cela n'aide pas beaucoup.
Johan du Toit
7

C ++ (gcc) x86_64 Linux uniquement, 9295 8900 8712 6812 5590 octets, 18 caractères uniques

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

Essayez-le en ligne!

Ceci est basé sur les idées de cette réponse PPCG . Un programme en langage machine est exprimé sous la forme d'un tableau de 32 bits, chacun étant représenté par une somme de 1+11+111.... Il s'avère qu'il peut être plus efficace de coder xcomme ytel y%(1<<32)==x. Le programme en langage machine codé est le suivant

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

... qui est basé sur le code C suivant.

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

Modifier: accepte désormais les entrées de stdinau lieu de argv[1]. Merci à @ ASCII et @PeterCordes pour leurs suggestions!

Edit4: encodage légèrement amélioré.

plafond
la source
-wdrapeau pls: P (vous pouvez également renommer iià a)
ASCII seulement
Vous en avez besoin gcc -zexecstack, non? Parce que int m[]non const. (Et les chaînes d'outils récentes placées .rodatadans une page non exécutable de toute façon, donc même const int m[]ne fonctionne pas, par exemple, sur mon système Arch Linux avec gcc8.2.1 20181127 et ld(GNU Binutils) 2.31.1.) Quoi qu'il en soit, vous avez oublié de le mentionner dans votre réponse, mais c'est dans votre lien TIO.
Peter Cordes
BTW, l'algorithme de notation à comptage unique de l'OP ne compte pas l'espace et la nouvelle ligne, vous n'avez donc pas à rendre le tout terrible à lire, juste le tableau: P
Peter Cordes
Vous pouvez enregistrer des octets de code machine en copiant le 1avec push %rax/ pop %rdiau lieu d'un autre push-immediate. Ou plus simplement, pour les valeurs qui ne sont pas 64 bits, c'est-à-dire non pointeurs, 2 octets mov %eax, %edi. En outre, Linux syscallne détruit pas ses registres d'entrée, uniquement raxavec la valeur de retour et RCX + R11 avec RIP et RFLAGS enregistrés dans le cadre du fonctionnement de l' syscallinstruction. Ainsi, vous pouvez quitter rdiet rdxdéfinir plusieurs 1appels, et utiliser différentes regs. De plus, RBX est préservé des appels, il n'est donc pas vraiment enregistré dans le RBX de clobber main. Il se trouve que cela fonctionne parce que le code de démarrage CRT s'en fiche.
Peter Cordes
6

21 caractères uniques + 1 nouvelle ligne 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))<%%>
%>

Les espaces blancs ne sont pas requis, sauf pour la première nouvelle ligne. Compilé dans g ++ 7.3.0.

Les caractères utilisés: %:include<ostram>()f-.

Améliorations apportées à d'autres réponses:

  1. Suppression des points-virgules en modifiant les forboucles ifet la récursivité.
  2. Vous avez le personnage de l'espace std::addressof(std::cout)->fill(), alias std::cout.fill().
jimmy23013
la source
std :: addressof, sympa!
Johan du Toit
2

21 20 personnages uniques hors espaces blancs

Tous les espaces blancs peuvent être modifiés en nouvelles lignes.

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

Quitte avec segfault. Les caractères utilisés: %:include<ostram>;-h.

Il fonctionne dans cette version de compilateur spécifique sur un Linux 64 bits:

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

Avec le paramètre:

-std=c++17

Même alors, je ne suis pas sûr que cela fonctionnerait toujours. Cela peut aussi dépendre de beaucoup d'autres choses. ciaet ciusont les décalages de mémoire divisés par 4 entre ia iuet i. ( int32 bits dans cette version.) Il se peut que vous deviez modifier les nombres pour qu'ils correspondent au décalage réel. Les adresses seraient beaucoup plus prévisibles si elles sont toutes contenues dans une structure. Malheureusement non statique auton'est pas autorisé dans une structure.

eest un tableau à 0 éléments d'un type d'élément avec une taille de (2 32 -1) × 2 32 octets. Si le type de pointeur correspondant de eest décrémenté, la moitié supérieure du pointeur serait décrémentée de (2 32 -1), ce qui équivaut à une incrémentation de un. Cela pourrait réinitialiser le compteur décrémenté sans utiliser le signe d'égalité.

Une version plus raisonnable qui devrait fonctionner de manière plus fiable, mais utilise un caractère de plus =:

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

Même cela ne fonctionne pas dans la dernière version de g ++ car il ne semble plus permettre de définir maindans un type arbitraire.

Ces deux programmes n'utilisent pas de parenthèses. Mais les points-virgules ne semblent pas évitables.

jimmy23013
la source
1

22 Personnages uniques hors espaces blancs. Sépare les nombres par un caractère NUL qui s'affiche correctement sous 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;
    %>
%>

Essayez-le en ligne

Histogramme:

[%] 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
la source
std :: ends est un caractère NUL ( char(0)), pas un espace ( char(32)en ASCII / UTF-8). en.cppreference.com/w/cpp/io/manip/ends . Je l'ai essayé sur mon bureau Linux juste pour m'assurer, et la sortie ressemble 1234, non 1 2 3 4. Il en va de même sur votre sortie TIO!
Peter Cordes
@PeterCordes, l'OP ne précise pas comment séparer les nombres ;-)
Johan du Toit
Pensez-vous vraiment qu'ils auraient gaspillé un personnage "pour " "s'ils auraient pu iiiise séparer de '0'pour 10203040? Je suppose que vous pouvez démontrer qu'il y a toujours un séparateur dans la sortie binaire du programme, mais souligner ce changement et le décrire en anglais est important pour votre réponse, car ce n'est pas un remplacement direct! Je serais heureux de supprimer mon downvote si vous développez votre réponse pour expliquer et justifier cela.
Peter Cordes
1
@PeterCordes, Point pris.
Johan du Toit