最小限の一意の文字を使用して整数のラダーを生成します(C ++)


13

私はコードゴルフのスポーツは初めてです。C ++で一意の文字の最小数を使用して整数のはしごを生成しようとしています。

整数4が与えられたとしましょう。

次のラダーを生成します。

1
1 2
1 2 3
1 2 3 4

要するに、私のプログラムはstdinから正の整数を読み取り、このラダーを出力に出力します。できる限り少ない数の一意の文字でこれを実行しようとしています。

私のプログラムは次のとおりです。

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

プログラム内の一意の文字数を確認するために使用したチェッカーは次のとおりです。

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

できれば、このプログラムを完了するために25文字未満の固有の文字を使用したい(改行文字を除き、空白文字を含む)。現在、私のプログラムは27を使用しています。さらに最適化する方法がわかりません。

誰かがそれをさらに最適化する方法についてアドバイスしてもらえますか(使用されている一意の文字の数に関して)?C ++のみを使用できることに注意してください。


5
code-golf以外の採点基準に関するヒントを求めるのは確かに斬新ですが、ヒントページはトピックに関するプログラミングの課題に対するより良い答えになると述べているため、それはトピックです。
アダム

8
@LuisMendo多くの言語がこのスコアリングスキームを完全に単純化しているので、この場合は本当だとは思いません。このユーザーが「ユニークなゴルフ」を学ぶのを手伝いたいなら、言語のサブセットでしか意味をなさないので、一般的なチャレンジよりもヒントとしてはるかに良いと思います。誰かがそれを投稿したい場合、基本的な問題はおそらく挑戦になる可能性があるということです。
FryAmTheEggman

3
中括弧の代わりにダイグラフ<%と%>を使用できると思いますが、いくつか見逃したと思います。
私の代名詞は

2
私は間違いなくいくつかを見逃した。#は%:ですので、3文字を取り除き、1文字({=> <%、} =>%>、#=>%:)を導入し、25を取得できます。これを以下の回答と組み合わせると、あなたは24を得ることができると思う
私の代名詞がmonicareinstateで

2
@LanceHAOHトライグラフは[下手な]質問で非常に一般的であり、トライグラフについて読むとダイグラフも表示されます。
私の代名詞は19:19の

回答:


12

コードから=文字を削除できたと思いますが、今では大幅に遅くなっています

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

きれいではありませんが、整数オーバーフローを悪用することで=を使用せずに0に戻すことができます

また、警備員を少し変更する必要がありました。残念なことに、インクルードのために、すべての改行文字を(閉じることはできますが)取り除くことができなかったため、調査の次の手段になる可能性があります。

編集:今は時間を使い果たしますが、strstreamや他のさまざまなライブラリを含めて使用すると、「」文字も削除でき、再び整数を使用してスペースの正しい文字に到達し、 strstream


2
#include<std>すべての:s を削除できます。優れたコーディング慣行ではありませんが、それは重要です。
ダレルホフマン

3
@DarrelHoffman私は仕事に、あなたがする必要がないことを得ることができないusing namespace std;ネット0そう:用の追加のpを使用することになる
期限切れのデータ

うーん。たぶん、私のC ++は少し錆びています。また、それはを追加するgので、私は推測する純損失。これはコードの金だったら、私たちは、名前を変更することでバイト数を減らすことができiiiiiおよびiiii他の単一文字の名前に(他の既に使用された文字を選ぶ)、それは私はないと思いますので、この課題は、約あるかではありません。そこに使用する任意の利益になるだろう場合、私は思ったんだけどgetcputcの代わりにcin/ cout、それを試してみなければならないでしょう。
ダレルホフマン

1
私の悪い。チェッカーをもう一度読み直しただけです。改行文字は無視されるようです。したがって、改行を削除することについて実際に気にする必要はありません。しかし、コメントの@someoneによる戦略と解決策を組み合わせて、なんとか24文字にすることができました。intの代わりにshortを使用して、プログラムをさらに高速化しました。そこで、追加の「h」文字を取得しました。しかし、これにより追加のコストなしでcharデータ型を使用できます。そこで、文字コードを使用して「文字」も
削除しました。–ランスハオー

@LanceHAOH:を含むすべての符号付き型について、符号付き整数オーバーフローはC ++では未定義の動作であることに注意してくださいsigned char。最適化を有効にしてコンパイルすると、符号gcc -fwrapv付きオーバーフローを2の補数ラップアラウンドとして明確に定義するために使用しない限り、このコードは最新のコンパイラーで破損する可能性があります。clangもサポート-fwrapvしています。(unsignedを含む整数型unsigned charは、ISO C ++で明確に定義された動作(ラップアラウンド)を持っています)。これは、ABIに依存するかどうかcharであるsigned charか、unsigned charそう、char[OK]をすることができます。
ピーター

10

@ExpiredDataと@someoneの回答を組み合わせることで、ようやく24個のユニークなキャラクターが得られました。また、intの代わりにshortデータ型を使用すると、短いデータ型をオーバーフローさせるのに要する時間が短くなるため、プログラムの速度が向上しました。

私のコードは次のとおりです。

%: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 char iiiii;は、変数の初期化の最後で使用します。
Rɪᴋᴇʀ

1
@KevinCruijssenそれは本当です。しかし、文字コードを使用してスペース文字を表すことができるため、「」文字を削除できます。したがって、使用される一意の文字の純差= 0です
。– LanceHAOH

9

Digraphsを使用した23のユニークなキャラクター。(25なし)。UBなし

C ++ 11ブレース初期化構文使用int var{};=、およびを避けて整数をゼロにリスト初期化します0。(または、あなたの場合、globalを避けますiiii)。これにより、グローバル変数(ローカル変数とは異なり、静的にゼロに初期化されます)以外のゼロのソースが提供されます。

現在のコンパイラは、特別なオプションを有効にする必要なく、デフォルトでこの構文を受け入れます。

(整数のラップアラウンドトリックは楽しいですし、最適化してゴルフのためのokが無効になってますが、署名オーバーフローがISO C ++で未定義の動作です。あなたは、GCC /打ち鳴らすでコンパイルしない限り、最適化は、無限ループにそれらのラップアラウンドループを回します有効にする-fwrapvだけでなく、オーバーフロー符号付き整数与えます-defined behaviour:2の補数のラップアラウンド。

楽しい事実:ISO C ++にstd::atomic<int>は、明確に定義された2の補数のラップアラウンドがあります! int32_tすべてで定義されている場合、2の補数であることが要求されるが、それは依然としてのtypedefできるようにオーバーフロー動作は未定義であるintか、longこれらのタイプのいずれかが32ビット、無パディング、及び2の補数である任意のマシン上で)。


この特定の場合には役に立ちません:

新しい変数を、既存の変数のコピーとして、ブレースまたは(空でない初期化子を使用して)直接初期化のための括弧で初期化することもできます
int a(b)またはint a{b}と同等ですint a = b;

しかしint b();、ゼロに初期化された変数の代わりに関数を宣言します。

また、int()またはchar()でゼロを取得できます。つまり、匿名オブジェクトのゼロ初期化です。


単純な論理変換により、<=比較を<比較に置き換えることができます。ループの最後ではなく、比較の直後にループカウンターをインクリメントします。IMOこれは++、aの最初の部分で使用してfor()0を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;
    }

私たちはそれをゴルフに落とすことfor(int r{}; r++ < n;)ができましたが、人間にとって読みにくいIMOです。合計バイト数の最適化は行っていません。


我々はすでに使用していた場合h、我々は救うことができる'か、"スペースのため。

ASCIIまたはUTF-8環境を想定すると、スペースのchar値は32になります。変数で十分に簡単に作成できます。cout << c;

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

そして、他の値は++、それらのバイナリ表現のビットに基づいて、シーケンスの2倍から明らかに作成できます。0(なし)または1(++)をLSBに効果的にシフトしてから、新しい変数に二重化します。


このバージョンではh'またはの代わりにを使用し"ます。

既存のバージョンのいずれよりもはるかに高速で(長いループに依存せず)、Undefined Behaviorがありません。これは、との警告なしでコンパイルg++ -O3 -Wall -Wextra -Wpedanticし、とclang++-std=c++11オプションです。それは合法で移植可能なISO C ++ 11です:)

また、グローバル変数に依存しません。そして、意味のある変数名を使用して、人間が読みやすくしました。

一意のバイト数:25私が削除したコメントを除くg++ -E。カウンターのようなスペースと改行を除外します。sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic このaskubuntuから各キャラクターの出現回数をカウントするために使用、それをパイプしてwc独自のキャラクターの数をカウントしました。

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

fからの2 文字のみforです。の用途whileがある場合は、代わりにループを使用できますw

ループをアセンブリ言語スタイルに書き換えて、ループi < r || goto some_label;の下部などに条件付きジャンプを書き込むことができます。(ただし、のor代わりに使用||)。いいえ、それは機能しません。 gotoはのようなステートメントでifあり、Perlの場合のように式のサブコンポーネントにすることはできません。そうでなければ、それを使用して(および)文字を削除できます。

の代わりにと交換fすることができ、両方のループは常に少なくとも1回の反復を実行するため、通常のasm ループ構造のように、下部に1つのループ分岐のみが必要になります。ユーザーが0より大きい整数を入力すると仮定します...gif(stuff) goto label;fordo{}while


ダイグラフとトライグラフ

幸いなことに、ISO C ++ 17の時点で3文字表記が削除されたため、最新のC ++リビジョンでユニークゴルフをしている場合の??>代わりに使用する必要はありません}

しかし、特にトライグラフのみ:ISO C ++ 17には、:>for ]%>forのようなダイグラフがまだあります}。したがって%、を使用するコストで、との両方{を回避 し}、一意の文字を2つ少なくする%:ため#に使用できます。

また、C ++にはnot!演算子または演算子のような演算子キーワードbitorがあり|ます。xor_eqforを使用すると^=、を使用して変数をゼロにすることができi xor_eq iますが、使用していない複数の文字が含まれています。

現在でg++は、デフォルトで3文字表記は無視されます-std=gnu++17-trigraphsそれらを有効にするために使用する必要があります。または-std=c++11、それらを含むISO標準に厳密に準拠するために何かを使用する必要があります。

23個の一意のバイト:

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

オンラインでお試しください!

最終バージョンでは、スペース区切り文字の'代わりに、hまたは"スペース区切り文字に単一引用符が使用されます。char c{}ものをダイグラフにしたくなかったので削除しました。文字の印刷は文字列の印刷よりも効率的であるため、それを使用しました。

ヒストグラム:

$ 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

スペース区切り文字(未解決)

削除された回答で、Johan Du Toitは代替セパレーター、特にを使用することを提案しましたstd::ends。これはNUL文字でありchar(0)、ほとんどの端末でゼロ幅として出力されます。したがって、出力は次のよう1234になります1 2 3 4。さらに悪いことに、静かに崩壊しなかったものはゴミで区切られてい'\0'ます。

任意のセパレータを使用できる場合、数字を0で簡単に作成できますcout << some_zeroed_var。しかし、誰も望ん10203040でいない、それはセパレータなしよりもさらに悪いことです。

私は、文字列リテラルを使用せずに保持作成するstd::string" "方法を考えていましたchar。たぶん何かを追加しますか?たぶん、コンストラクタの1つを使用して長さ1の[]バイトを32作成した後、最初のバイトを値に設定するためのdigraph がありますか?

Johan は、現在の塗りつぶし文字を返すstd::iosfill()メンバー関数も提案しました。ストリームのデフォルトはによって設定されstd::basic_ios::init()、です' '

std::cout << i << std::cout.fill();置き換えます<< ' ';、の.代わりに使用します'

では-、私たちはへのポインタを取ることができcout、使用する->fill()メンバ関数を呼び出すために:
std::cout << (bitand std::cout)->fill()。または、私たちはbどちらも使用していなかったので&、レキシカルな同等物の代わりに使用したかもしれませんbitand

なしメンバ関数を呼び出します.か、->

クラス内に配置して、定義します 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(); }
}

次にss s{}、ループの前、ループのstd::cout << i << s;内側。グレート、それはコンパイルし、正常に動作し、私たちが使用していたphのためにoperator char()少なくとも1の純損失のために、我々は避けbメンバ関数を作るためにpublic使用してstructの代わりにclass。(そして、私たちはprotected、これが助けになる場合に、継承をオーバーライドできます)。


@JohanduToit:cout.fill()fromstd::iosを使用. することをお勧めします、以前は使用していませんでしたが、ポインターを使用->fill()してメンバー関数を使用することで何らかの方法で呼び出すことができますか?何かがcout他のストリームへのポインタを返しますか?
ピーター

エラー、<< (bitand std::cout)->fill()コンパイルしますが、を使用し-ます。(トークン名にも関わらず、具体的にはビット単位のAND演算子でbitandはなく、に字句的に相当し&ます。アドレス演算子としても機能します。)私たちができることを()使用せずに.、または->
ピーター

1
私が見つけた他の唯一のことはstd::ios::left、gccで32と定義されていることですが、それを活用する方法を本当に理解できませんでした。私はこれを手放して実際の仕事を終わらせるつもりだと思う:
ヨハン・デュ・トワ

@JohanduToit:int32を作成することは問題ではありません。私の答え++は、int c{};ゼロから開始する方法を既に示しています。しかし、そうです、私はラムダ、テンプレート、またはを調べるといううさぎの穴を下っていませんstd::function。またはstd::stringアイデア。しかし、私たちはg実際にa std::stringを失うことなく宣言することはできません。goto代わりに使用するという私の考えforはうまくいきませんでした。 型を指定decltype(something)できますcharが、費用がかかりますy
ピーター

1
operatorにはcharの代わりにautoを使用できますstruct ss : std::ostream { operator auto () { return fill(); } };が、あまり役に立ちません。
ヨハンデュトワ

7

C ++(gcc) x86_64 Linuxのみ、9295 8900 8712 6812 5590バイト、18個の一意の文字

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

オンラインでお試しください!

これはこのPPCG答えからの考えに基づいています。機械語プログラムは、32ビット整数の配列として表され、各配列はの合計として表され1+11+111...ます。それはエンコードに、より効率的であり得ることが判明したxとしてy、その結果y%(1<<32)==x。エンコードされた機械語プログラムは次のとおりです

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

...次の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 );
}

編集:今からの入力を受け付けるstdin代わりにargv[1]。@ ASCII-onlyと@PeterCordesの提案に感謝します!

Edit4:エンコーディングがわずかに大幅に改善されました。


-wフラグPLS:P(また、名前を変更することができますiia
ASCIIのみの

これが必要gcc -zexecstackですよね?でint m[]はないのでconst。(そして.rodata、とにかく最近のツールチェーンは非実行可能ページに置かれているのでconst int m[]、たとえばgcc8.2.1 20181127とld(GNU Binutils)2.31.1の私のArch Linuxシステムでも動作しません。)とにかく、あなたはあなたの答えでそれを言及するのを忘れました、しかし、それはTIOリンクにあります。
ピーター

ところで、OPのユニークカウントスコアリングアルゴリズムは、スペースと改行をカウントしません。したがって、配列全体だけを読むためにすべてをひどくする必要はありません:P
Peter Cordes

別のプッシュイミディエートの代わりに1with push %rax/をコピーすることにより、マシンコードバイトを節約できますpop %rdi。より簡単に言うと、64ビットではない値(つまり、非ポインター、2バイト)の場合mov %eax, %edi。また、Linux syscallは入力レジスタを破棄せずrax、戻り値と、syscall命令の動作の一部としてRIPとRFLAGSを保存したRCX + R11 のみを破棄します。したがって、コールをまたいrdirdx設定し1、異なるregを使用できます。また、RBXはコール保存されるため、clobber mainのRBXに実際には保存されません。CRT開始コードは気にしないので、たまたま動作します。
ピーター

6

21個の一意の文字+ 1個の削除不可能な改行

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

最初の改行を除いて、空白は必要ありません。g ++ 7.3.0でコンパイル。

使用される文字:%:include<ostram>()f-

他の回答の改善:

  1. forループifと再帰を変更してセミコロンを削除しました。
  2. std::addressof(std::cout)->fill()別名でスペース文字を取得しましたstd::cout.fill()

std :: addressof、nice!
ヨハンデュトワ

2

21 空白を除く20の一意の文字

すべての空白は改行に変更できます。

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

segfaultで終了します。使用される文字:%:include<ostram>;-h

64ビットLinux上のこの特定のコンパイラバージョンで動作します。

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

パラメーター付き:

-std=c++17

それでも、それが常に機能するかどうかはわかりません。また、他の多くのことに依存する場合があります。ciaそして、ciuの間に4分割されたメモリオフセットであるia iui。(intこのバージョンでは32ビットです。)実際のオフセットと一致するように数値を変更する必要がある場合があります。アドレスがすべて構造体に含まれている場合、アドレスははるかに予測可能です。残念ながらauto、構造体では非静的は許可されていません。

e(2 32 -1)×2 32バイトのサイズの要素タイプの0要素配列です。の対応するポインタタイプeがデクリメントされると、ポインタの上位半分は(2 32 -1)だけデクリメントされます。これは、1ずつインクリメントするのと同じです。これにより、等号を使用せずにデクリメントされたカウンターをリセットできます。

より確実に動作するはずですが、もう1つの文字を使用するより合理的なバージョン=

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

これでも、最新バージョンのg ++​​では機能しませmainん。これは、任意の型での定義が許可されなくなったためです。

これらの2つのプログラムは括弧を使用しません。ただし、セミコロンは回避できないようです。


1

22空白を除く一意の文字。Windowsで正しく表示されるNUL文字で数字を区切ります。

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

オンラインで試す

ヒストグラム:

[%] 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はNUL文字(char(0))であり、スペースではありません(char(32)ASCII / UTF-8で)。 en.cppreference.com/w/cpp/io/manip/ends。念のためにLinuxデスクトップで試してみましたが、出力はのよう1234になり1 2 3 4ます。TIO出力でも同じように見えます!
ピーター

@ PeterCordes、OPは数字の分離方法を指定しません;
ヨハンデュトワ

彼らがforで分離するのに慣れていたら、彼らは"for キャラクターを無駄にしていたと本当に思いますか?プログラムのバイナリ出力にまだセパレーターがあるというケースを作ることができると思いますが、この変更を指摘して英語で説明することは、ドロップインの代替ではないため、あなたの答えにとって重要です!それを説明し、正当化するためにあなたの答えを広げるならば、私は私の下票を削除したいです。" "iiii'0'10203040
ピーター

1
@PeterCordes、ポイントを取った。
ヨハンデュトワ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.