Genera scala di numeri interi usando il minor numero di caratteri univoci (in C ++)


13

Sono nuovo nello sport del code golf. Sto cercando di generare una scala di numeri interi usando il minor numero di caratteri univoci in C ++.

Diciamo che ci viene dato un numero intero 4.

Genereremo la seguente scala:

1
1 2
1 2 3
1 2 3 4

In breve, il mio programma leggerà un numero intero positivo da stdin e stamperà questa scala sull'output. Sto cercando di farlo con il minor numero possibile di personaggi unici .

Il mio programma è il seguente:

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

Ecco il correttore che ho usato per controllare il numero di caratteri univoci nel mio programma:

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

Preferibilmente, desidero utilizzare meno di 25 caratteri univoci per completare questo programma (esclusi i caratteri di nuova riga ma inclusi gli spazi bianchi). Attualmente, il mio programma utilizza 27. Non sono sicuro di come ottimizzarlo ulteriormente.

Qualcuno potrebbe consigliarmi su come ottimizzarlo ulteriormente (in termini di numero di caratteri univoci utilizzati)? Si noti che è possibile utilizzare solo C ++.


5
È certamente nuovo chiedere suggerimenti riguardo a qualsiasi altro criterio di punteggio oltre al code-golf , ma è un argomento in discussione, poiché le pagine dei suggerimenti dicono che lo rendono una risposta migliore a una sfida di programmazione che è in argomento .
Adám,

8
@LuisMendo Non credo che sia vero in questo caso, poiché molte lingue banalizzano completamente questo schema di punteggio. Se questo utente desidera aiutare a imparare il "golf unico", ha davvero senso solo in un sottoinsieme di lingue, quindi penso che questo sia molto meglio come suggerimento che come sfida generica. Detto questo, il problema di base potrebbe essere probabilmente una sfida se qualcuno vuole pubblicarlo.
FryAmTheEggman

3
Penso che tu possa usare i digraph <% e%> invece di parentesi graffe, e penso di averne perso alcuni.
mio pronome è monicareinstate

2
Ne ho sicuramente persi alcuni. # è% :, quindi puoi sbarazzarti di tre caratteri e introdurne uno ({=> <%,} =>%>, # =>% :) e arrivare a 25. Se combini questo con la risposta qui sotto, I penso di poter ottenere 24.
mio pronome è monicareinstate

2
@LanceHAOH Le trigrafi sono estremamente comuni nelle domande [subdole], e anche i digrafi si presentano quando leggono delle trigrafi.
mio pronome è monicareinstate

Risposte:


12

Credo di essere riuscito a rimuovere il carattere = dal tuo codice, anche se ora è significativamente più 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;
    }
}

Non è carino, ma abusando dell'overflow di numeri interi possiamo tornare a 0 senza usare =

Inoltre abbiamo dovuto cambiare un po 'le guardie. Sfortunatamente a causa dell'inclusione non sono riuscito a liberarmi di tutti i nuovi personaggi della linea (anche se è vicino), quindi potrebbe essere la prossima strada da investigare.

Modifica: per ora esaurisci il tempo, ma se includi e usi strstream e varie altre librerie, penso che potresti essere in grado di rimuovere anche il "carattere", usando di nuovo numeri interi per arrivare al carattere corretto per lo spazio e passandolo nel strstream


2
Potresti #include<std>ed eliminare tutti :i messaggi. Non è una buona pratica di programmazione, ma questo è il punto.
Darrel Hoffman,

3
@DarrelHoffman Non riesco a farlo funzionare, non devi fare using namespace std;che userebbe una p aggiuntiva per: così un netto 0
Dati scaduti

Hmm. Forse, il mio C ++ è un po 'arrugginito. Anche questo aggiunge una g, quindi perdita netta immagino. Se questo fosse il codice oro, potremmo ridurre il conteggio dei byte rinominando ii, iiie iiiiad altri nomi di singole lettere (scegli eventuali altre lettere già utilizzate), ma non è questo il problema, quindi credo di no. Mi chiedo se ci sarebbe qualche guadagno da usare getce putcinvece di cin/ cout, dovrei provarlo.
Darrel Hoffman,

1
Colpa mia. Ho appena letto di nuovo la pedina. Sembra che il carattere newline sia ignorato. Quindi in realtà non è necessario preoccuparsi di rimuovere le nuove righe. Ma combinato con la tua strategia e la soluzione di @someone nei commenti, sono riuscito a portarlo a 24 caratteri. Ho reso il programma ancora più veloce usando short invece di int. Quindi ho ottenuto un ulteriore carattere "h". Ma questo mi consente di utilizzare il tipo di dati char senza costi aggiuntivi. Quindi mi sono liberato del "personaggio anche usando il codice carattere.
LanceHAOH,

@LanceHAOH: notare che l'overflow di numeri interi con segno è un comportamento indefinito in C ++, per tutti i tipi con segno inclusi signed char. Se si compila con l'ottimizzazione abilitata, questo codice potrebbe non funzionare con i compilatori moderni, a meno che non si utilizzi gcc -fwrapvper rendere l'overflow firmato ben definito come complemento del complemento di 2. supporta -fwrapvanche clang . ( unsignedi tipi interi compresi unsigned charhanno un comportamento ben definito (avvolgente) in ISO C ++). Dipende dall'ABI se lo charè signed charo unsigned char, quindi charpuò essere ok.
Peter Cordes,

10

Alla fine ho ottenuto 24 personaggi unici combinando le risposte di @ExpiredData e @someone. Inoltre, l'utilizzo del tipo di dati breve anziché int ha contribuito ad accelerare il mio programma perché impiega meno tempo a traboccare un tipo di dati breve.

Il mio codice è il seguente.

%: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 in char iiiii;, l'ultima delle inizializzazioni variabili.
R

1
@KevinCruijssen È vero. Ma ciò mi permette di rimuovere il carattere "perché posso usare il codice carattere per rappresentare il carattere spaziale. Quindi la differenza netta in caratteri univoci utilizzata = 0
LanceHAOH,

9

23 personaggi unici usando Digraphs. (25 senza). No UB.

Utilizzare la sintassi dell'inizializzatore rinforzato C ++ 11 per elencare l'inizializzazione di un numero intero su zero int var{};evitando =e 0. (O nel tuo caso, evitando globale iiii). Questo ti dà una fonte di zeri diversa dalle variabili globali (che sono inizializzate staticamente a zero, a differenza dei locali).

I compilatori correnti accettano questa sintassi per impostazione predefinita, senza dover abilitare alcuna opzione speciale.

(Il trucco avvolgente intero è divertente, e va bene per giocare a golf con l'ottimizzazione disabilitata, ma l'overflow firmato è un comportamento indefinito in ISO C ++. L'ottimizzazione dell'ottimizzazione trasformerà quei loop avvolgenti in loop infiniti, a meno che non si compili con gcc / clang -fwrapvper fornire bene l'overflow di numeri interi firmati definito comportamento: avvolgente complemento di 2.

Curiosità: ISO C ++ std::atomic<int>ha un complemento di 2 ben definito! int32_tè necessario essere il complemento di 2 se definito affatto, ma il comportamento di overflow non è definito, quindi può ancora essere un typedef per into longsu qualsiasi macchina in cui uno di questi tipi è 32 bit, nessuna spaziatura e complemento di 2).


Non utile per questo caso specifico:

È inoltre possibile inizializzare una nuova variabile come copia di una esistente, con parentesi graffe o (con un inizializzatore non vuoto), parentesi per l'inizializzazione diretta .
int a(b)o int a{b}sono equivalenti aint a = b;

Ma int b();dichiara una funzione invece di una variabile inizializzata a zero.

Inoltre, è possibile ottenere uno zero con int()o char(), ovvero inizializzazione zero di un oggetto anonimo.


Possiamo sostituire i tuoi <=confronti con i <confronti con una semplice trasformazione logica : esegui l'incremento del contatore del ciclo subito dopo il confronto, anziché nella parte inferiore del ciclo. IMO questo è più semplice delle alternative che le persone hanno proposto, come usare ++nella prima parte di a for()per fare uno 0 in 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;
    }

Potremmo giocare a golf for(int r{}; r++ < n;)ma IMO è meno facile da leggere per gli umani. Non stiamo ottimizzando per il conteggio totale dei byte.


Se stessimo già utilizzando h, potremmo salvare 'o "per uno spazio.

Supponendo un ambiente ASCII o UTF-8, lo spazio è a charcon valore 32. Possiamo crearlo in una variabile abbastanza facilmente, quindicout << 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 altri valori possono ovviamente essere creati da una sequenza di ++e raddoppio, in base ai bit della loro rappresentazione binaria. Spostando efficacemente uno 0 (niente) o 1 (++) nell'LSB prima di raddoppiare in una nuova variabile.


Questa versione utilizza hinvece di 'o ".

È molto più veloce di una delle versioni esistenti (non si basa su un ciclo lungo) ed è privo di comportamento indefinito . Si compila senza avvisi con g++ -O3 -Wall -Wextra -Wpedantice conclang++ . -std=c++11è facoltativo. È legale e portatile ISO C ++ 11 :)

Inoltre non si basa su variabili globali. E l'ho reso più leggibile dall'uomo con nomi di variabili che hanno un significato.

Numero di byte univoci: 25 , esclusi i commenti con cui ho eliminatog++ -E . Ed escludendo spazio e newline come il tuo contatore. Ho usato sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic da questo askubuntu per contare le occorrenze di ciascun personaggio, e ho analizzato questowc per contare quanti personaggi unici avevo.

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

Gli unici 2 fcaratteri provengono da for. Potremmo usare i whileloop invece se ne avessimo bisogno w.

Potremmo eventualmente riscrivere i loop in uno stile di linguaggio assembly i < r || goto some_label;per scrivere un salto condizionale nella parte inferiore del loop, o altro. (Ma usando orinvece di ||). No, non funziona. gotoè un'affermazione simile ife non può essere un sottocomponente di un'espressione come in Perl. Altrimenti avremmo potuto usarlo per rimuovere i caratteri (e ).

Potremmo scambiare fper gcon if(stuff) goto label;invece di for, e entrambi i loop sempre eseguito almeno 1 iterazione così avremmo bisogno di un solo ciclo-ramo in basso, come un normale asm do{}whilestruttura ad anello. Supponendo che l'utente inserisca un numero intero> 0 ...


Digraphs e Trigraphs

Fortunatamente, le trigrafi sono state rimosse a partire da ISO C ++ 17, quindi non dobbiamo usarle al ??>posto del }golf unico per la revisione C ++ più recente.

Ma solo trigrafi specifici: ISO C ++ 17 ha ancora digrafi come :>a favore ]e %>a favore} . Quindi al costo di utilizzo %, siamo in grado di evitare sia {e }, e utilizzare %:per #un risparmio netto di 2 meno personaggi unici.

E C ++ ha parole chiave dell'operatore come notper l' !operatore o bitorper l' |operatore. Con xor_eqfor ^=, puoi azzerare una variabile con i xor_eq i, ma ha più caratteri che non stavi utilizzando.

Current g++ignora già le trigrafi di default anche senza -std=gnu++17; devi usarli -trigraphsper abilitarli, o -std=c++11qualcosa per rigorosa conformità a uno standard ISO che li include.

23 byte unici:

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

Provalo online!

La versione finale utilizza una 'virgoletta singola anziché ho "per il separatore di spazi. Non volevo digerire la char c{}roba, quindi l'ho cancellata. La stampa di un carattere è più efficiente della stampa di una stringa, quindi l'ho usata.

Istogramma:

$ 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

Il separatore di spazio (ancora irrisolto)

In una risposta ora cancellata, Johan Du Toit ha proposto di utilizzare un separatore alternativo, in particolare std::ends. È un carattere NUL char(0)e viene stampato come larghezza zero sulla maggior parte dei terminali. Quindi il risultato sarebbe simile 1234, no 1 2 3 4. O peggio, separati da immondizia su tutto ciò che non è crollato silenziosamente '\0'.

Se è possibile utilizzare un separatore arbitrario, quando 0è facile creare la cifra cout << some_zeroed_var. Ma nessuno vuole 10203040, è anche peggio di nessun separatore.

Stavo cercando di pensare a un modo per creare una std::stringholding a" " senza usare charo una stringa letterale. Forse aggiungere qualcosa ad esso? Forse con un digraph per []impostare il primo byte su un valore di 32, dopo averne creato uno con lunghezza 1 tramite uno dei costruttori?

Johan ha anche suggerito la std::iosfunzione membro fill () che restituisce il carattere di riempimento corrente. L'impostazione predefinita per uno stream è impostata da std::basic_ios::init(), ed è ' '.

std::cout << i << std::cout.fill();sostituisce << ' ';ma utilizza .invece di' .

Con -, possiamo prendere un puntatore a coute l'uso ->fill()di chiamare la funzione membro:
std::cout << (bitand std::cout)->fill(). O no, non eravamo utilizzando bsia così potremmo anche usato &al posto del suo equivalente lessicale, bitand.

Chiamare una funzione membro senza .o->

Inseriscilo in una classe e definiscilo 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(); }
}

Quindi ss s{}prima del loop e std::cout << i << s;all'interno del loop. Fantastico, si compila e funziona correttamente, ma abbiamo dovuto usare pe hper operator char(), per una perdita netta di 1. Almeno abbiamo evitato bdi fare funzioni membro publicusando structinvece di class. (E potremmo scavalcare l'eredità con protectednel caso in cui ciò possa mai aiutare).


@JohanduToit: buona idea con cout.fill()fromstd::ios , ma in precedenza non utilizzavamo . Forse potremmo chiamarlo in qualche modo prendendo un puntatore e usando ->fill()una funzione membro? Qualcosa restituisce un puntatore couto qualsiasi altro flusso?
Peter Cordes,

Oops, << (bitand std::cout)->fill()compila, ma usa -. (Nonostante il nome del token, bitandè solo un equivalente lessicale di &, non specificamente l'operatore bit a bit e. Funziona anche come operatore dell'indirizzo.) Hmm, c'è qualche template o materiale lambda che può ottenere un puntatore a una funzione membro che possiamo ()senza usare .o ->?
Peter Cordes,

1
L'unica altra cosa che ho scoperto è che std::ios::leftè definito come 32, in gcc, ma non sono riuscito davvero a trovare un modo per sfruttarlo. Penso che lascerò andare questo e farò un po 'di lavoro effettivo :-)
Johan du Toit

@JohanduToit: la creazione di un int32 non è un problema, la mia risposta mostra già come farlo ++partendo da int c{};zero. Ma sì, non vado nella tana del coniglio a guardare lambda, modelli o std::function. O l' std::stringidea. Ma non ci stiamo abituando, non gpossiamo effettivamente dichiarare un std::stringsenza perdere; la mia idea per l'utilizzo gotoinvece di fornon è stata interrotta. decltype(something)potrebbe darci un chartipo, ma ci costa a y.
Peter Cordes,

1
Puoi usare auto invece di char per l'operatore: struct ss : std::ostream { operator auto () { return fill(); } };ma non aiuta molto.
Johan du Toit

7

C ++ (gcc) x86_64 solo Linux, 9295 8900 8712 6812 5590 byte, 18 caratteri univoci

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

Provalo online!

Questo si basa sulle idee di questa risposta PPCG . Un programma di linguaggio macchina è espresso come un array di 32 bit ints, ognuno dei quali è rappresentato come una somma di 1+11+111.... Risulta che può essere più efficiente per codificare xcome ytale y%(1<<32)==x. Il programma di linguaggio macchina codificato è il seguente

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

... che si basa sul seguente codice 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 );
}

Modifica: ora accetta input da stdinanziché argv[1]. Grazie a @ ASCII-only e @PeterCordes per i loro suggerimenti!

Edit4: codifica leggermente migliorata.


-wpls bandiera: P (anche è possibile rinominare iia a)
ASCII solo

Ne hai bisogno gcc -zexecstack, vero? Perché int m[]non lo è const. (E le toolchain recenti inseriscono .rodatacomunque una pagina non eseguibile, quindi const int m[]non funzionano nemmeno sul mio sistema Arch Linux con gcc8.2.1 20181127 e ld(GNU Binutils) 2.31.1.) Comunque, hai dimenticato di menzionarlo nella tua risposta, ma è nel tuo link TIO.
Peter Cordes,

A proposito, l'algoritmo di calcolo del conteggio univoco dell'OP non conta spazio e newline, quindi non devi rendere tutto terribile da leggere, solo l'array: P
Peter Cordes,

È possibile salvare i byte del codice macchina copiando 1con push %rax/ pop %rdiinvece di un altro push-immediato. O più semplicemente, per valori che non sono 64 bit, ovvero non puntatori, 2 byte mov %eax, %edi. Inoltre, Linux syscallnon distrugge i suoi registri di input, solo raxcon il valore restituito e RCX + R11 con RIP e RFLAGS salvati come parte del funzionamento syscalldell'istruzione. Quindi puoi partire rdie rdximpostare le 1chiamate multiple e utilizzare registri diversi. Inoltre, RBX è preservato dalle chiamate, quindi non è realmente salvabile nell'RBX principale di clobber. Capita di funzionare perché il codice di avvio CRT non importa.
Peter Cordes,

6

21 personaggi unici + 1 newline inamovibile

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

Gli spazi bianchi non sono richiesti ad eccezione della prima riga nuova. Compilato in g ++ 7.3.0.

Caratteri usati: %:include<ostram>()f-.

Miglioramenti ad altre risposte:

  1. Punto e virgola rimossi modificando i forloop in ife la ricorsione.
  2. Ho ottenuto il personaggio spaziale di std::addressof(std::cout)->fill()aka std::cout.fill().

std :: AddressOf, bello!
Johan du Toit,

2

21 20 personaggi unici esclusi gli spazi bianchi

Tutti gli spazi bianchi potrebbero essere cambiati in nuove righe.

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

Esce con segfault. Caratteri usati: %:include<ostram>;-h.

Funziona in questa specifica versione del compilatore su un Linux a 64 bit:

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

Con il parametro:

-std=c++17

Anche allora, non sono sicuro che funzionerebbe sempre. Può anche dipendere da molte altre cose. ciae ciusono gli offset di memoria divisi per 4 tra ia iue i. ( intè 32 bit in questa versione.) Potrebbe essere necessario modificare i numeri in modo che corrispondano all'offset effettivo. Gli indirizzi sarebbero molto più prevedibili se fossero tutti contenuti in una struttura. Sfortunatamente non statico autonon è permesso in una struttura.

eè un array di 0 elementi di un tipo di elemento con una dimensione di (2 32 -1) × 2 32 byte. Se il tipo di puntatore corrispondente di eviene diminuito, la metà superiore del puntatore verrebbe decrementata di (2 32 -1), che equivale a incrementare di uno. Ciò potrebbe ripristinare il contatore decrementato senza utilizzare il segno di uguaglianza.

Una versione più ragionevole che dovrebbe funzionare in modo più affidabile, ma utilizza un altro carattere =:

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

Anche questo non funziona nell'ultima versione di g ++ perché non sembra più consentire la definizione mainin un tipo arbitrario.

Questi due programmi non usano le parentesi. Ma poi i punti e virgola non sembrano essere evitabili.


1

22 personaggi unici esclusi gli spazi bianchi. Separa i numeri con un carattere NUL che viene visualizzato correttamente su 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;
    %>
%>

Provalo online

Istogramma:

[%] 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 :: End è un carattere NUL ( char(0)), non uno spazio ( char(32)in ASCII / UTF-8). en.cppreference.com/w/cpp/io/manip/ends . L'ho provato sul mio desktop Linux solo per essere sicuro, e l'output sembra 1234, no 1 2 3 4. Sembra allo stesso modo sull'uscita TIO!
Peter Cordes,

@PeterCordes, l'OP non specifica come separare i numeri ;-)
Johan du Toit

Pensi davvero che avrebbero sprecato un personaggio "per " "se avessero potuto iiiisepararsi con '0'per 10203040? Suppongo che tu possa sostenere che esiste ancora un separatore nell'output binario del programma, ma sottolineare questo cambiamento e descriverlo in inglese è importante per la tua risposta, perché non si tratta di un rimpiazzo! Sarei felice di rimuovere il mio voto negativo se espandi la tua risposta per spiegarlo e giustificarlo.
Peter Cordes,

1
@PeterCordes, punto preso.
Johan du Toit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.