Generieren Sie eine Leiter mit ganzen Zahlen mit der geringsten Anzahl eindeutiger Zeichen (in C ++).


13

Ich bin neu im Code-Golfsport. Ich versuche, eine Rangliste von Ganzzahlen mit der geringsten Anzahl von eindeutigen Zeichen in C ++ zu generieren.

Nehmen wir an, wir bekommen eine ganze Zahl 4.

Wir werden die folgende Leiter generieren:

1
1 2
1 2 3
1 2 3 4

Kurz gesagt, mein Programm liest eine positive Ganzzahl von stdin und druckt diese Leiter zur Ausgabe. Ich versuche dies mit der geringstmöglichen Anzahl von eindeutigen Zeichen zu tun .

Mein Programm ist wie folgt:

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

Hier ist der Checker, mit dem ich die Anzahl der eindeutigen Zeichen in meinem Programm überprüft habe:

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

Vorzugsweise möchte ich weniger als 25 eindeutige Zeichen verwenden, um dieses Programm zu vervollständigen (ausgenommen Zeilenumbrüche, aber einschließlich Leerzeichen). Derzeit verwendet mein Programm 27. Ich bin nicht sicher, wie ich es weiter optimieren soll.

Könnte mich bitte jemand beraten, wie ich es weiter optimieren kann (in Bezug auf die Anzahl der verwendeten eindeutigen Zeichen)? Bitte beachten Sie, dass nur C ++ verwendet werden kann.


5
Es ist sicherlich Roman fragen Tipps in Bezug auf alle anderen Bewertungskriterien als Code-Golf , aber AFAIK ist es beim Thema, da die Spitzen Seiten sagen es eine bessere Antwort auf eine Programmierung Herausforderung zu machen , die beim Thema ist .
Adám,

8
@LuisMendo Ich glaube nicht, dass dies in diesem Fall der Fall ist, da viele Sprachen dieses Bewertungsschema vollständig trivialisieren. Wenn dieser Benutzer beim Erlernen von "einzigartigem Golf" helfen möchte, ist dies nur in einer Teilmenge von Sprachen wirklich sinnvoll. Ich denke, dies ist als Tipp viel besser als als allgemeine Herausforderung. Das heißt, das Grundproblem könnte wahrscheinlich eine Herausforderung sein, wenn jemand es posten möchte.
FryAmTheEggman

3
Ich denke, Sie können Digraphen <% und%> anstelle von geschweiften Klammern verwenden, und ich glaube, ich habe einige verpasst.
Mein Pronomen ist monicareinstate

2
Ich habe definitiv einiges verpasst. # ist% :, also kannst du drei Zeichen loswerden und eins einführen ({=> <%,} =>%>, # =>% :) und auf 25 kommen. Wenn du dies mit der Antwort unten kombinierst, ich Ich denke, Sie können 24 bekommen.
Mein Pronomen ist monicareinstate

2
@LanceHAOH Trigraphen kommen sehr häufig in [hinterhältigen] Fragen vor, und auch beim Lesen von Trigraphen werden Digraphen angezeigt.
Mein Pronomen ist monicareinstate

Antworten:


12

Ich glaube, ich habe es geschafft, das Zeichen = aus Ihrem Code zu entfernen, obwohl es jetzt erheblich langsamer ist

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

Es ist nicht schön, aber durch den Missbrauch eines Integer-Überlaufs können wir ohne Verwendung von = auf 0 zurückkehren

Auch mussten wir die Wachen ein wenig wechseln. Leider konnte ich wegen des Includes nicht alle neuen Zeilenzeichen entfernen (obwohl es in der Nähe ist), so dass dies möglicherweise die nächste Möglichkeit zur Untersuchung ist.

Bearbeiten: Die Zeit wird knapp, aber wenn Sie strstream und verschiedene andere Bibliotheken einbinden und verwenden, können Sie möglicherweise auch das Zeichen "wieder entfernen, indem Sie Ganzzahlen verwenden, um das richtige Zeichen für den Raum zu finden und es in das zu übergeben strstream


2
Sie könnten #include<std>und alle :s beseitigen . Keine großartige Codierungspraxis, aber das ist nebensächlich.
Darrel Hoffman

3
@ DarrelHoffman Ich kann das nicht zum Laufen bringen, musst du nicht tun, using namespace std;was ein zusätzliches p für das: also net 0 verwenden würde
Abgelaufene Daten

Hmm. Vielleicht ist mein C ++ ein bisschen rostig. Auch das fügt einen g, also netto Verlust, denke ich. Wenn dieser Code Gold waren, konnten wir die Bytezahl durch die Umbenennung reduzieren ii, iiiund iiiizu anderen einzelnen Buchstaben - Namen (Pick alle anderen bereits verwendeten Buchstaben), aber das ist nicht das, was diese Herausforderung zu, so dass ich nicht erraten. Ich frage mich, ob es irgendwelche Vorteile bei der Verwendung gibt getcund ob ich es putcanstelle von cin/ coutversuchen müsste.
Darrel Hoffman

1
Mein Fehler. Ich habe gerade den Checker noch einmal durchgelesen. Scheint, dass Newline-Zeichen ignoriert wird. Das Entfernen von Zeilenumbrüchen erübrigt sich also. Aber in Kombination mit Ihrer Strategie und der Lösung von @someone in den Kommentaren habe ich es geschafft, 24 Zeichen zu bekommen. Ich habe das Programm noch schneller gemacht, indem ich short anstelle von int verwendet habe. Also habe ich ein zusätzliches 'h' Zeichen bekommen. Aber damit kann ich den char-Datentyp ohne zusätzliche Kosten verwenden. Also habe ich das Zeichen auch mit dem Zeichencode entfernt.
LanceHAOH

@LanceHAOH: Beachten Sie, dass der Überlauf von vorzeichenbehafteten Ganzzahlen in C ++ für alle vorzeichenbehafteten Typen, einschließlich, undefiniertes Verhalten ist signed char. Wenn Sie mit aktivierter Optimierung kompilieren, funktioniert dieser Code möglicherweise nicht mehr mit modernen Compilern, es sei denn, Sie gcc -fwrapvdefinieren einen signierten Überlauf als Komplementumlauf von 2. Clang unterstützt -fwrapvauch. ( unsignedInteger-Typen einschließlich unsigned charhaben in ISO C ++ ein genau definiertes Verhalten (Wrap-Around)). Es hängt von dem ABI , ob charist signed charoder unsigned char, so charkann in Ordnung sein.
Peter Cordes

10

Ich habe endlich 24 einzigartige Charaktere erhalten, indem ich die Antworten von @ExpiredData und @someone kombiniert habe. Außerdem hat die Verwendung des Datentyps short anstelle von int dazu beigetragen, mein Programm zu beschleunigen, da das Überlaufen eines kurzen Datentyps kürzer ist.

Mein Code ist wie folgt.

%: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 verwendet er in char iiiii;der letzten Variableninitialisierung.
16.

1
@ KevinCruijssen Das ist wahr. Aber das erlaubt mir, das Zeichen "zu entfernen, weil ich Zeichencode verwenden kann, um das Leerzeichen darzustellen. Also ist der
Nettodifferenz der

9

23 einzigartige Charaktere mit Digraphen. (25 ohne). Nein UB.

Verwenden Sie die geschweifte C ++ 11 -Initialisierungssyntax, um eine Ganzzahl unter int var{};Vermeidung von =und auf Null zu setzen 0. (Oder in Ihrem Fall global vermeiden iiii). Dies gibt Ihnen eine Quelle für andere Nullen als globale Variablen (die im Gegensatz zu lokalen Variablen statisch auf Null initialisiert sind).

Aktuelle Compiler akzeptieren diese Syntax standardmäßig, ohne dass spezielle Optionen aktiviert werden müssen.

(Der Integer-Wraparound-Trick macht Spaß und ist für Golfer mit deaktivierter Optimierung in Ordnung, aber signierter Überlauf ist undefiniertes Verhalten in ISO C ++. Durch Aktivieren der Optimierung werden diese Wraparound-Schleifen in Endlosschleifen umgewandelt, es sei denn, Sie kompilieren mit gcc / clang -fwrapv, um einen signierten Integer-Überlauf zu erzielen -definiertes Verhalten: Komplementumlauf von 2.

Unterhaltsame Tatsache: ISO C ++ std::atomic<int>hat einen gut definierten 2er-Komplement-Wrap-Around! int32_tEs muss das Zweierkomplement sein, wenn es überhaupt definiert ist, aber das Überlaufverhalten ist undefiniert, sodass es weiterhin ein typedef für intoder longauf jedem Computer sein kann, auf dem einer dieser Typen 32 Bit, kein Auffüllen und das Zweierkomplement ist.)


Für diesen speziellen Fall nicht nützlich:

Sie können eine neue Variable auch mit geschweiften Klammern oder (mit einem nicht leeren Initialisierer) mit Parens für die direkte Initialisierung als Kopie einer vorhandenen Variablen initialisieren .
int a(b)oder int a{b}sind äquivalent zuint a = b;

Aber int b();deklariert eine Funktion statt einer Variable auf Null initialisiert.

Sie können auch eine Null mit int()oder erhalten char(), dh eine Null-Initialisierung eines anonymen Objekts.


Wir können Ihre <=Vergleiche durch <Vergleiche durch eine einfache logische Transformation ersetzen : Führen Sie das Inkrement des Schleifenzählers direkt nach dem Vergleich durch, anstatt am Ende der Schleife. IMO ist dies einfacher als die Alternativen, die die Leute vorgeschlagen haben, wie ++im ersten Teil von a for(), um aus einer 0 eine 1 zu machen.

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

Wir könnten Golf spielen, for(int r{}; r++ < n;)aber IMO ist das für Menschen weniger einfach zu lesen. Wir optimieren nicht für die Gesamtanzahl der Bytes.


Wenn wir bereits verwenden h, könnten wir das 'oder "für ein Leerzeichen speichern .

Angenommen, eine ASCII- oder UTF-8-Umgebung hat das Leerzeichen den charWert 32. Das können wir dann leicht in einer Variablen erstellencout << 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

Und andere Werte können offensichtlich aus einer Folge von ++und Verdopplung erzeugt werden, basierend auf den Bits ihrer Binärdarstellung. Verschieben Sie effektiv eine 0 (nichts) oder 1 (++) in das LSB, bevor Sie es in eine neue Variable verdoppeln.


Diese Version verwendet hanstelle von 'oder ".

Es ist viel schneller als jede der vorhandenen Versionen (ohne auf eine lange Schleife angewiesen zu sein) und es ist frei von undefiniertem Verhalten . Es kompiliert ohne Warnungen mit g++ -O3 -Wall -Wextra -Wpedanticund mitclang++ . -std=c++11es ist optional. Es ist legal und portabel ISO C ++ 11 :)

Es ist auch nicht auf globale Variablen angewiesen. Und ich habe es mit Variablennamen, die eine Bedeutung haben, lesbarer gemacht.

Anzahl der eindeutigen Bytes: 25 , ohne die Kommentare, die ich entfernt habeg++ -E . Und ohne Leerzeichen und Zeilenvorschub wie bei Ihrem Counter. Ich habe sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic dieses Askubuntu verwendet, um die Vorkommen der einzelnen Charaktere zu zählen, und habe es weitergeleitet, um wczu zählen, wie viele eindeutige Charaktere ich hatte.

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

Die einzigen 2 fZeichen sind von for. Wir könnten whilestattdessen Schleifen verwenden, wenn wir eine Verwendung für hatten w.

Wir könnten die Schleifen möglicherweise in einen Assembler-Stil umschreiben i < r || goto some_label;, um einen bedingten Sprung am Ende der Schleife zu schreiben, oder was auch immer. (Aber mit orstatt ||). Nein, das geht nicht. gotoist eine Anweisung wie ifin Perl und kann keine Unterkomponente eines Ausdrucks sein. Andernfalls hätten wir es verwenden können, um die Zeichen (und zu entfernen ).

Wir konnten den Handel ffür gmit if(stuff) goto label;statt for, und beide Schleifen immer mindestens 1 Iteration laufen , so dass wir nur an der Unterseite eine Schleife-Zweig benötigen würde, wie eine normale asm do{}whileSchleifenstruktur. Angenommen, der Benutzer gibt eine Ganzzahl> 0 ein ...


Digraphen und Trigraphen

Glücklicherweise wurden Trigraphen ab ISO C ++ 17 entfernt, sodass wir sie nicht mehr verwenden müssen, ??>anstatt }für die letzte C ++ - Revision ausschließlich Golf zu spielen.

Aber nur Trigraphen im Speziellen: ISO C ++ 17 hat immer noch Digraphen wie :>für ]und %>für} . Also auf Kosten der Verwendung %, können wir beide vermeiden {und }, und verwenden Sie %:für #eine Netto-Einsparung von 2 weniger eindeutigen Zeichen.

Und C ++ hat Operator-Schlüsselwörter wie notfür den !Operator oder bitorfür den |Operator. Mit xor_eqfor ^=können Sie eine Variable auf Null setzen i xor_eq i, sie enthält jedoch mehrere Zeichen, die Sie nicht verwendet haben.

Current g++ignoriert Trigraphen bereits standardmäßig, auch ohne -std=gnu++17; Sie müssen verwenden -trigraphs, um sie zu aktivieren, oder -std=c++11etwas, um einer ISO-Norm, die sie enthält, strikt zu entsprechen.

23 eindeutige Bytes:

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

Probieren Sie es online!

In der endgültigen Version wird 'anstelle von hoder "für das Leerzeichen ein einfaches Anführungszeichen verwendet. Ich wollte das char c{}Zeug nicht digraphen, also habe ich es gelöscht. Das Drucken eines Zeichens ist effizienter als das Drucken einer Zeichenfolge, also habe ich das verwendet.

Histogramm:

$ 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

Das Leerzeichen (noch ungelöst)

In einer jetzt gelöschten Antwort schlug Johan Du Toit die Verwendung eines alternativen Trennzeichens vor std::ends. Das ist ein NUL-Zeichen char(0)und wird auf den meisten Terminals als Breite Null gedruckt. So würde die Ausgabe aussieht 1234, nicht 1 2 3 4. Oder schlimmer noch, getrennt durch Müll an irgendetwas, das nicht lautlos zusammenbrach '\0'.

Wenn Sie ein beliebiges Trennzeichen verwenden können, können Sie die Ziffer 0einfach mit erstellen cout << some_zeroed_var. Aber niemand will 10203040, das ist noch schlimmer als kein Trennzeichen.

Ich habe versucht, einen Weg zu finden, um eine std::stringHolding" " ohne Verwendung chareines String-Literal zu erstellen . Vielleicht etwas anhängen? Vielleicht mit einem Digraphen [], um das erste Byte auf einen Wert von zu setzen 32, nachdem über einen der Konstruktoren eines mit der Länge 1 erstellt wurde?

Johan schlug auch die std::iosMitgliedsfunktion fill () vor, die das aktuelle Füllzeichen zurückgibt. Die Standardeinstellung für einen Stream ist std::basic_ios::init()und ' '.

std::cout << i << std::cout.fill();ersetzt << ' ';aber verwendet .statt' .

Mit -, können wir einen Zeiger nehmen coutund die Verwendung ->fill()der Memberfunktion aufzurufen:
std::cout << (bitand std::cout)->fill(). Oder auch nicht, wir wurden mit nicht bentweder so können wir auch genutzt haben &statt dessen lexikalische Äquivalent bitand.

Aufrufen einer Mitgliedsfunktion ohne .oder->

Setzen Sie es in eine Klasse und definieren Sie 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(); }
}

Dann ss s{}vor der Schleife und std::cout << i << s;innerhalb der Schleife. Großartig, es kompiliert und funktioniert einwandfrei , aber wir mussten Gebrauch pund hfür operator char(), für einen Nettoverlust von 1. Mindestens wir vermieden bMitgliederfunktionen zu machen , publicindem Sie structstatt class. (Und wir könnten die Vererbung mit überschreiben, protectedfalls das jemals hilft).


@JohanduToit: Gute Idee mit cout.fill()fromstd::ios , aber wir haben es vorher nicht benutzt . Vielleicht können wir es irgendwie aufrufen, indem wir einen Zeiger nehmen und ->fill()auf eine Mitgliedsfunktion verweisen? Gibt irgendetwas einen Zeiger auf couteinen anderen Stream zurück?
Peter Cordes

Hoppla, << (bitand std::cout)->fill()kompiliert, aber verwendet -. (Ist trotz des Tokennamens bitandnur ein lexikalisches Äquivalent zu& , nicht speziell zum bitweisen Operator und zum Operator. Sie fungiert auch als Operatoradresse.) Hmm, gibt es Template- oder Lambda-Informationen, die einen Zeiger auf eine Member-Funktion abrufen können? das können wir ()ohne .oder ->?
Peter Cordes

1
Das einzige andere, was ich gefunden habe, ist, dass std::ios::leftes in gcc als 32 definiert ist, aber ich konnte nicht wirklich einen Weg finden, dies auszunutzen. Ich glaube, ich lasse diesen los und erledige ein paar aktuelle Arbeiten :-)
Johan du Toit

@JohanduToit: Eine int32 zu erstellen ist kein Problem, meine Antwort zeigt bereits, wie man das mit ++einer int c{};Null anfängt . Aber ja, ich gehe nicht das Kaninchenloch hinunter, um Lambdas, Vorlagen oder ähnliches zu untersuchen std::function. Oder die std::stringIdee. Aber wir sind nicht daran gewöhnt g, dass wir tatsächlich eine deklarieren können, std::stringohne zu verlieren. Meine Idee, gotoanstatt zu verwenden, ist fornicht aufgegangen. decltype(something)könnte uns einen chartyp geben, kostet uns aber einen y.
Peter Cordes

1
Sie können auto anstelle von char für den opeator verwenden, struct ss : std::ostream { operator auto () { return fill(); } };aber es hilft nicht viel.
Johan du Toit

7

Nur C ++ (gcc) x86_64 Linux, 9295 8900 8712 6812 5590 Bytes, 18 eindeutige Zeichen

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

Probieren Sie es online!

Dies basiert auf Ideen aus dieser PPCG-Antwort . Ein Maschinensprachenprogramm wird als Array von 32-Bit-Ints ausgedrückt, von denen jedes als Summe von dargestellt wird 1+11+111.... Es stellt sich heraus, dass es möglicherweise effizienter ist, das xals ysolches zu codieren y%(1<<32)==x. Das codierte Maschinensprachenprogramm ist das folgende

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

... der auf dem folgenden C-Code basiert.

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

Bearbeiten: Akzeptiert jetzt Eingaben von stdinanstelle von argv[1]. Vielen Dank an @ ASCII-only und @PeterCordes für ihre Vorschläge!

Edit4: Etwas deutlich verbesserte Kodierung.


-wpls Flagge: P (auch können Sie umbenennen iizu a)
ASCII-only

Du brauchst gcc -zexecstackdafür, oder? Da int m[]geht es nicht const. (Und die neuesten Toolchains haben .rodatasowieso eine nicht ausführbare Seite hinzugefügt, so dass sie zB const int m[]auf meinem Arch Linux-System mit gcc8.2.1 20181127 und ld(GNU Binutils) 2.31.1 nicht funktionieren.) Wie auch immer, Sie haben vergessen, dies in Ihrer Antwort zu erwähnen. aber es ist in Ihrem TIO-Link.
Peter Cordes

Übrigens, der Bewertungsalgorithmus für die eindeutige Anzahl der OPs zählt weder Leerzeichen noch Zeilenumbrüche, sodass Sie nicht alles fürchterlich machen müssen, nur das Array: P
Peter Cordes

Sie können Maschinencode-Bytes speichern, indem Sie das 1mit push %rax/ pop %rdianstelle eines anderen Push-Direkt kopieren . Oder einfacher gesagt, für Werte, die nicht 64-Bit sind, dh Nicht-Zeiger, 2 Byte mov %eax, %edi. Außerdem zerstört Linux syscallseine Eingaberegister nicht, nur raxmit dem Rückgabewert und RCX + R11 mit gespeichertem RIP und RFLAGS als Teil der Funktionsweise des syscallBefehls. So verlassen Sie können rdiund rdxsetzen 1über Anrufe und verschiedene regs verwenden. RBX bleibt auch bei Anrufen erhalten, so dass es nicht wirklich sicher ist, die RBX des Hauptgeräts zu löschen. Es funktioniert, weil es den CRT-Startcode nicht interessiert.
Peter Cordes

6

21 eindeutige Zeichen + 1 nicht entfernbarer Zeilenvorschub

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

Leerzeichen sind bis auf die erste Zeile nicht erforderlich. Kompiliert in g ++ 7.3.0.

Verwendete Zeichen: %:include<ostram>()f- .

Verbesserungen bei anderen Antworten:

  1. Semikolons wurden entfernt, indem die forSchleifen in geändert wurdenif und die Rekursion geändert wurden.
  2. Habe den Space-Charakter von std::addressof(std::cout)->fill(), aka std::cout.fill().

std :: addressof, nett!
Johan du Toit

2

21 20 eindeutige Zeichen ohne Leerzeichen

Alle Leerzeichen können in Zeilenumbrüche umgewandelt werden.

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

Beendet mit Segfault. Verwendete Zeichen:%:include<ostram>;-h .

Es funktioniert in dieser speziellen Compiler-Version auf einem 64-Bit-Linux:

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

Mit dem Parameter:

-std=c++17

Selbst dann bin ich mir nicht sicher, ob es immer funktionieren würde. Es kann auch von vielen anderen Dingen abhängen. ciaund ciusind die Speicheroffsets geteilt durch 4 zwischen ia iuund i. ( intIn dieser Version sind es 32 Bit.) Möglicherweise müssen Sie die Zahlen ändern, um sie an den tatsächlichen Versatz anzupassen. Die Adressen wären viel vorhersehbarer, wenn sie alle in einer Struktur enthalten wären. Leider autoist in einer Struktur nicht statisch erlaubt.

eist ein 0-Elemente-Array eines Elementtyps mit einer Größe von (2 32 -1) × 2 32 Bytes. Wenn der entsprechende Zeigertyp von edekrementiert wird, wird die höhere Hälfte des Zeigers um (2 32) dekrementiert -1) , was einer Inkrementierung um eins entspricht. Dies könnte den dekrementierten Zähler ohne Verwendung des Gleichheitszeichens zurücksetzen.

Eine vernünftigere Version, die zuverlässiger funktionieren sollte, aber ein Zeichen mehr verwendet =:

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

Auch dies funktioniert in der neuesten Version von g ++ nicht, da es nicht mehr möglich zu sein scheint, maineinen beliebigen Typ zu definieren.

Diese beiden Programme verwenden keine Klammern. Aber dann scheinen Semikolons nicht vermeidbar zu sein.


1

22 Eindeutige Zeichen ohne Leerzeichen. Trennt die Zahlen durch ein NUL-Zeichen, das unter Windows korrekt angezeigt wird.

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

Probieren Sie es online aus

Histogramm:

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

std :: ends ist ein NUL-Zeichen ( char(0)), kein Leerzeichen ( char(32)in ASCII / UTF-8). de.cppreference.com/w/cpp/io/manip/ends . Ich habe es auf meinem Linux-Desktop versucht, um sicherzugehen, und die Ausgabe sieht 1234nicht so aus 1 2 3 4. Auf Ihrem TIO-Ausgang sieht es genauso aus!
Peter Cordes

@ PeterCordes, das OP legt nicht fest, wie die Nummern getrennt werden sollen ;-)
Johan du Toit

Glaubst du wirklich, sie hätten einen Charakter "für verschwendet, " "wenn sie sich früher iiiimit '0'für hätten trennen können 10203040? Ich vermute, Sie können ein Argument dafür anführen, dass sich noch ein Trennzeichen in der Binärausgabe des Programms befindet, aber es ist wichtig, dass Sie auf diese Änderung hinweisen und sie auf Englisch beschreiben, da dies kein Ersatz ist! Ich würde gerne meine Ablehnung entfernen, wenn Sie Ihre Antwort erweitern, um dies zu erklären und zu rechtfertigen.
Peter Cordes

1
@PeterCordes, Punkt genommen.
Johan du Toit
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.