En az sayıda benzersiz karakter kullanarak tamsayı merdiveni oluşturun (C ++ ile)


13

Kod golf sporunda yeniyim. C ++ benzersiz karakterler en az sayıda kullanarak tamsayılar merdiveni oluşturmaya çalışıyorum.

Diyelim ki bize bir tam sayı 4 verildi.

Aşağıdaki merdiveni oluşturacağız:

1
1 2
1 2 3
1 2 3 4

Kısacası, programım stdin'den pozitif bir tam sayı okuyacak ve bu merdiveni çıktıya yazdıracaktır. Bunu mümkün olan en az sayıda benzersiz karakter ile yapmaya çalışıyorum .

Programım aşağıdaki gibidir:

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

Programımdaki benzersiz karakter sayısını kontrol etmek için kullandığım denetleyici:

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

Tercihen bu programı tamamlamak için 25'ten az benzersiz karakter kullanmak istiyorum (yeni satır karakterleri hariç ancak boşluk dahil). Şu anda programım 27 kullanıyor. Nasıl daha fazla optimize edeceğinden emin değilim.

Birisi bana nasıl daha fazla optimizasyon yapılacağı konusunda tavsiyede bulunabilir mi (kullanılan benzersiz karakter sayısı açısından)? Sadece C ++ 'ın kullanılabileceğini lütfen unutmayın.


5
Kod golf dışında başka puanlama kriterleri ile ilgili ipuçları istemek kesinlikle yenidir , ancak afaict, konuyla ilgilidir, çünkü ipuçları sayfaları , konuyla ilgili bir programlama sorununa daha iyi bir cevap vermeyi söylüyor .
Adám

8
@LuisMendo Pek çok dil bu puanlama şemasını tamamen önemsizleştirdiğinden, bu durumda bunun doğru olduğunu düşünmüyorum. Bu kullanıcı "benzersiz golf" öğrenmek için yardım istiyorsa, sadece gerçekten bir dil alt kümesinde anlamlıdır, bu yüzden bu bir ipucu olarak genel bir meydan okuma daha çok daha iyi olduğunu düşünüyorum. Bu, birileri göndermek isterse temel sorunun muhtemelen bir zorluk olabileceğini söyledi.
FryAmTheEggman

3
Kıvırcık parantez yerine <% ve%> digraflarını kullanabileceğinizi düşünüyorum ve sanırım bazılarını kaçırdım.
zamirim

2
Kesinlikle bazı özledim. #%:, böylece üç karakterden kurtulabilir ve bir tane tanıtabilirsiniz ({=> <%,} =>%>, # =>% :) ve 25'e ulaşırsanız. Aşağıdaki cevapla birleştirirseniz, ben 24 alabileceğinizi düşünüyorum.
zamirim monicareinstate

2
@LanceHAOH Trigraflar [el değmemiş] sorularda son derece yaygındır ve trigrafları okurken digrafiler de ortaya çıkar.
zamirim

Yanıtlar:


12

Ben şimdi önemli ölçüde yavaş olmasına rağmen, = karakter kodunuzu kaldırmak başardı inanıyorum

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

Güzel değil, ancak tamsayı taşmasını kötüye kullanarak = kullanmadan 0'a geri dönebiliriz.

Ayrıca gardiyanları biraz değiştirmek zorunda kaldık. Ne yazık ki içerme nedeniyle tüm yeni çizgi karakterlerinden (yakın olmasına rağmen) kurtulamadım, bu yüzden araştırma için bir sonraki yol olabilir.

Düzenleme: Şimdilik zaman tükenir, ancak strstream ve çeşitli diğer kütüphaneleri ekleyip kullanırsanız, "karakteri de, boşluk için doğru karaktere gelmek ve içine aktarmak için tamsayıları kullanarak da kaldırabileceğinizi düşünüyorum. strstream


2
Sen #include<std>tüm :s. Harika bir kodlama uygulaması değil, ama konunun yanında.
Darrel Hoffman

3
@DarrelHoffman Çalışmak için alamıyorum, bunun için using namespace std;ek bir p kullanır: bunu yapmak zorunda değilsiniz : yani bir net 0
Süresi dolmuş Veri

Hmm. Belki, C ++'m biraz paslı. Ayrıca bu bir gnet kaybı ekledi sanırım. Bu kod altın olsaydı, biz yeniden adlandırarak bayt sayısını azaltabilir ii, iiive iiiidiğer tek harf adlarına (başka zaten kullanılan harfler almak), ama bu sanırım değil bu nedenle bu sorun, ne olduğunu değil. Kullanmak için herhangi bir kazanç olup olmadığını getcve / putcyerine denemek zorunda olduğunu merak ediyorum . cincout
Darrel Hoffman

1
Benim hatam. Ben sadece denetleyiciyi tekrar okudum. Yeni satır karakterinin yok sayıldığı anlaşılıyor. Dolayısıyla, yeni satırları kaldırma konusunda endişelenmenize gerek yok. Ama stratejileriniz ve yorumlarda @someone'nin çözümü ile birlikte, 24 karaktere ulaşmayı başardım. İnt yerine short kullanarak programı daha da hızlı hale getirdim. Bu yüzden ek bir 'h' karakteri aldım. Ancak bu, char veri tipini ek ücret ödemeden kullanmama izin veriyor. Bu yüzden karakter kodunu kullanarak "karakterden de kurtuldum."
Donald

@LanceHAOH: imzalı tamsayı taşmasının C ++ 'da dahil olmak üzere tüm imzalı türler için tanımsız davranış olduğunu unutmayın signed char. Optimizasyon etkinken derlerseniz, gcc -fwrapvimzalı taşmayı 2'nin tamamlayıcı sargısı olarak iyi tanımlanmış olarak kullanmadığınız sürece bu kod modern derleyicilerle kırılabilir . clang da destekler -fwrapv. ( ISO C ++ 'da iyi tanımlanmış davranışa (etrafı saran) unsigneddahil tamsayı türleri unsigned char). Bu ABI bağlıdır olmadığını charise signed charya unsigned charbu kadar, chartamam olabilir.
Peter Cordes

10

Sonunda @ExpiredData ve @someone cevaplarını birleştirerek 24 benzersiz karakter var. Ayrıca, int yerine kısa veri türünün kullanılması, kısa bir veri türünün taşması daha kısa sürdüğü için programımı hızlandırmaya yardımcı oldu.

Kodum aşağıdaki gibidir.

%: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 bunu kullanıyor char iiiii;, değişken başlatmaların sonuncusu.
Rɪᴋᴇʀ

1
@KevinCruijssen Bu doğru. Ama bu "karakteri kaldırmama izin veriyor çünkü boşluk karakterini temsil etmek için karakter kodunu kullanabiliyorum. Bu yüzden benzersiz karakterlerde kullanılan net fark = 0.
Donald

9

Digraph kullanarak 23 benzersiz karakter. (25 olmadan). UB yok.

Kullanım C ++ sözdizimi başlatıcısı çaprazlı 11 için sıfıra bir tamsayıyı listesi-başlatmak int var{};kaçınarak =ve 0. (Ya da sizin durumunuzda, küreselden kaçınmak iiii). Bu size global değişkenlerden başka sıfırlar kaynağı verir (yerel ayarların aksine statik olarak sıfıra sıfırlanır).

Mevcut derleyiciler, herhangi bir özel seçeneği etkinleştirmek zorunda kalmadan varsayılan olarak bu sözdizimini kabul eder.

(Tamsayı sarma hüner eğlenceli ve devre dışı optimizasyonu ile golf için Tamam ama imzalı taşma ++ ISO C tanımsız davranıştır. Optimizasyonunu etkinleştirerek, sonsuz döngüler içine bu sarma döngüler dönecek sürece derleme ile gcc / clang -fwrapviçin vermek de taşma tamsayı imzalanmış tanımlanmış davranış: 2'nin tamamlayıcı sarmalaması.

Eğlenceli gerçek: ISO C ++ std::atomic<int>, iyi tanımlanmış 2'nin tamamlayıcı sargılarına sahiptir! int32_ttanımlanmışsa 2'nin tamamlayıcısı olması gerekir, ancak taşma davranışı tanımlanmamıştır, bu nedenle bu türlerden birinin 32 bit, dolgu yok ve 2'nin tamamlayıcısı olduğu herhangi bir makine için intveya bunun için bir typedef olabilir long.)


Bu özel durum için yararlı değil:

Yeni bir değişkeni, doğrudan başlatma için parantez veya (boş olmayan bir başlatıcı ile) parenler ile mevcut değişkenin bir kopyası olarak da başlatabilirsiniz .
int a(b)veya int a{b}şuna eşdeğerdir:int a = b;

Ancak int b();sıfıra sıfırlanmış bir değişken yerine bir işlev bildirir.

Ayrıca, anonim bir nesnenin sıfırıint() veya char()yani sıfır başlatma alabilirsiniz .


Karşılaştırmalarınızı <=karşılaştırmalar ile <basit bir mantık dönüşümü ile değiştirebiliriz : döngünün altında değil, karşılaştırmadan hemen sonra döngü sayacı artışını yapın. IMO, insanların önerdiği alternatiflerden daha basittir, örneğin a'nın ++ilk bölümünde for()0'a 1 yapmak için kullanmak gibi .

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

Biz bunu golf olabilir for(int r{}; r++ < n;)ama IMO insanlar için okumak daha az kolay. Toplam bayt sayısı için optimizasyon yapmıyoruz.


Eğer zaten kullanıyor holsaydık, 'ya da "bir alanı kurtarabilirdik .

ASCII veya UTF-8 ortamını varsayarsak, uzay char32 değeri olan bir değerdir. Bunu bir değişkende kolayca oluşturabiliriz, sonracout << 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

Ve diğer değerler, ++ikili gösterimlerinin bitlerine dayanarak, bir dizi ve iki katından açıkça oluşturulabilir . Yeni bir değişkene iki katına çıkmadan önce LSB'ye 0 (hiçbir şey) veya 1 (++) etkin bir şekilde kaydırılması.


Bu sürüm veya hyerine kullanır .'"

Mevcut sürümlerden herhangi birinden çok daha hızlıdır (uzun bir döngüye dayanmaz) ve Tanımsız Davranış içermez . Bu ile herhangi bir uyarı olmadan derler g++ -O3 -Wall -Wextra -Wpedanticve birlikteclang++ . -std=c++11İsteğe bağlı. Yasal ve taşınabilir ISO C ++ 11 :)

Ayrıca küresel değişkenlere de bağlı değildir. Ve bir anlamı olan değişken isimlerle daha insan tarafından okunabilir hale getirdim.

Benzersiz bayt sayısı: 25 , çıkardığım yorumlar hariçg++ -E . Ve sayacınız gibi boşluk ve satırsonu hariç. sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic Bu askubuntu'dan her karakterin oluşumlarını saymak için kullandım ve bunuwc kaç tane benzersiz karakterim olduğunu saymak için kullandım.

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

Sadece 2 fkarakter for. Biz kullanabiliriz whilebiz bir kullanım vardı yerine eğer döngüler w.

Döngülerin altına i < r || goto some_label;koşullu bir atlama yazmak için döngüleri yeniden birleştirebiliriz . (Ama oryerine kullanmak ||). Hayır, bu işe yaramıyor. gotoBir olan deyimi gibi ifbir ifadenin bir alt bileşeni Perl can gibi olamaz. Aksi takdirde, (ve )karakterlerini kaldırmak için bunu kullanabilirdik .

Biz satabilseydim fiçin gbirlikte if(stuff) goto label;yerine forve sadece normal bir asm gibi altta bir döngü dalı gerekiyordu bu yüzden her iki döngüler daima en az 1 yinelemeyi çalıştırmak do{}whiledöngü yapısı. Kullanıcının> 0 bir tamsayı girdiğini varsayarsak ...


Digraflar ve Trigraflar

Neyse ki, ISO C ++ 17'den itibaren trigraflar kaldırıldı, bu nedenle en son C ++ revizyonu için benzersiz golf oynamak ??>yerine kullanmak }zorunda değiliz.

Ama sadece özel olarak üç karakterli: ISO C ++ 17 hala gibi digraphs vardır :>için ]ve %>için} . Yani kullanarak pahasına %, biz de önleyebilirsiniz {ve }ve kullanımı %:için #2 daha az sayıda benzersiz karakter net tasarrufu için.

Ve C ++ gibi operatör anahtar kelimelerin yer aldığı notiçin !operatör veya bitoriçin |operatör. İle xor_eqiçin ^=, size bir değişkeni sıfır olabilir i xor_eq i, ancak kullanmakta değildi birden fazla karakter bulunmaktadır.

Akım g++zaten üçgeni varsayılan olarak yok sayar -std=gnu++17; Kullanmak zorunda -trigraphsveya bunları etkinleştirmek için -std=c++11bunları içermez ISO standardına uyumdan için falan.

23 benzersiz bayt:

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

Çevrimiçi deneyin!

Son sürümde boşluk ayırıcı 'yerine hveya "ayırıcı için tek tırnak kullanılır . Bir char c{}şeyleri çizmek istemedim, bu yüzden sildim. Bir karakter yazdırmak, bir dize yazdırmaktan daha verimlidir, bu yüzden bunu kullandım.

histogram:

$ 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

Boşluk ayırıcı (hala çözülmemiş)

Şimdi silinmiş bir cevapta, Johan Du Toit özellikle alternatif bir ayırıcı kullanmayı önerdi std::ends. Bu bir NUL karakteri char(0)ve çoğu terminalde sıfır genişliğinde yazdırıyor. Sonuç olarak çıktı gibi 1234görünmeyecekti 1 2 3 4. Veya daha da kötüsü, sessizce çökmeyen bir şey üzerinde çöple ayrıldı '\0'.

Rasgele bir ayırıcı kullanabiliyorsanız, rakamın 0oluşturulması kolay olduğunda cout << some_zeroed_var. Ama kimse istemiyor 10203040, bu ayırıcıdan daha kötü.

Kullanmadan veya dize değişmez bir std::stringholding oluşturmak için" " bir yol düşünmeye çalışıyordum char. Belki ona bir şey ekliyorlar? Belki de yapıcılardan biri ile 1 uzunluğunda bir tane oluşturduktan sonra [], ilk baytı bir değere ayarlamak için bir digraf 32ile?

Johan ayrıca geçerli doldurma karakterini döndüren std::iosfill () üye işlevini önerdi . Bir akış için varsayılan değer şudur: std::basic_ios::init()ve ' '.

std::cout << i << std::cout.fill();cümledeki << ' ';ancak kullanımları .yerine' .

İle -biz bir işaretçi alabilir coutve kullanım ->fill()üye işlevi çağırmak için:
std::cout << (bitand std::cout)->fill(). Ya da değil, biz de kullanmıyorduk, bbu yüzden &sözlüksel eşdeğeri yerine de kullanmış olabiliriz bitand.

Üye işlevini .veya olmadan çağırma->

Bir sınıfın içine koyun ve tanımlayın 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(); }
}

Sonra ss s{}döngüden önce ve döngü std::cout << i << s;içinde. Büyük, bu derler ve düzgün çalışır, ama biz kullanımına vardı pve hiçin operator char(), biz kaçınılması en az 1. net kaybı için büye işlevlerini yapmak publickullanarak structyerine class. (Ve protectedherhangi bir yardımcı olması durumunda mirası geçersiz kılabiliriz ).


@JohanduToit: ile iyi bir fikir cout.fill()gelenstd::ios , ama biz daha önce kullanmayan edildi . Belki bir işaretçi alıp kullanarak her nasılsa diyebilirsiniz ->fill()üye işleve? Bir şey bir işaretçiye coutveya başka bir akışa dönüyor mu ?
Peter Cordes

Hata! << (bitand std::cout)->fill()Derler, ama kullanır -. (Jeton adına rağmen, özellikle bitsel ve operatöre değil bitand, sadece sözcüksel bir eşdeğerdir &. Ayrıca operatörün adresi olarak da çalışır.) Hmm, üye işlevine bir işaretçi alabilen bazı şablon veya lambda öğeleri var mı? veya ()kullanmadan yapabiliriz ? .->
Peter Cordes

1
Bulduğum diğer tek şey, std::ios::leftgcc'de 32 olarak tanımlanmış olması, ancak bundan yararlanmak için bir yol bulamadım. Sanırım bunun gitmesine ve bazı gerçek işlerin yapılmasına izin vereceğim :-)
Johan du Toit

@JohanduToit: int32 oluşturmak sorun değil, cevabım bunu sıfırdan ++başlayarak nasıl yapacağınızı gösteriyor int c{};. Ama evet, lambdalara, şablonlara veya tavşanlara bakmanın tavşan deliğinden aşağı inmiyorum std::function. Ya da std::stringfikir. Ama biz kullanmıyoruz gaslında std::stringkaybetmeden ilan edemeyiz ; benim gotoyerine kullanmak için fikrimi fordışarı atmadı. decltype(something)bize bir chartür verebilir , ama bize maliyeti y.
Peter Cordes

1
Opeator için char yerine auto kullanabilirsiniz: struct ss : std::ostream { operator auto () { return fill(); } };ancak bu pek yardımcı olmuyor.
Johan du Toit

7

Yalnızca C ++ (gcc) x86_64 Linux, 9295 8900 8712 6812 5590 bayt, 18 benzersiz karakter

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

Çevrimiçi deneyin!

Bu, bu PPCG yanıtından gelen fikirlere dayanmaktadır . Bir makine dili programı, her biri toplamı olarak gösterilen 32 bitlik bir dizi olarak ifade edilir 1+11+111.... O encode daha verimli olabilir çıkıyor xolarak yöyle ki y%(1<<32)==x. Kodlanmış makine dil programı aşağıdaki gibidir

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

... aşağıdaki C kodunu temel alır.

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

Düzenle: Artık stdinyerine gelen girişi kabul ediyor argv[1]. Yalnızca ASCII ve @PeterCordes'a önerileri için teşekkürler!

Edit4: Biraz önemli ölçüde geliştirilmiş kodlama.


-wbayrak lütfen: P (ayrıca yeniden adlandırabilir iiiçin a)
ASCII sadece

Buna ihtiyacın var gcc -zexecstack, değil mi? Çünkü int m[]değil const. (Ve .rodatayine de çalıştırılamayan bir sayfaya konulan son araç zincirleri, bu nedenle const int m[], örneğin gcc8.2.1 20181127 ve ld(GNU Binutils) 2.31.1 ile Arch Linux sistemimde bile çalışmaz . ama TIO bağlantınızda.
Peter Cordes

BTW, OP'nin benzersiz sayım puanlama algoritması alan ve satırsonu saymaz, bu yüzden her şeyi okumak için korkunç yapmak zorunda değilsiniz, sadece dizi: P
Peter Cordes

Makine kodu baytlarını başka bir push-anında yerine / 1ile kopyalayarak kaydedebilirsiniz . Veya daha basit bir şekilde, 64 bit olmayan değerler için, yani işaretçi olmayan, 2 bayt . Ayrıca, Linux giriş kayıtlarını yok etmez, yalnızca dönüş değeri ve talimatın nasıl çalıştığının bir parçası olarak kaydedilmiş RIP ve RFLAGS'li RCX + R11 ile . Eğer bırakabilir Yani ve ayarlı çağrıları arasında ve farklı regs kullanın. Ayrıca, RBX çağrı korumalıdır, bu yüzden clobber main's RBX'e gerçekten tasarruf etmez. CRT başlatma kodu umursamadığı için çalışır. push %raxpop %rdimov %eax, %edisyscallraxsyscallrdirdx1
Peter Cordes

6

21 benzersiz karakter + 1 kaldırılamaz yeni satır

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

İlk satırsonu hariç boşluklara gerek yoktur. G ++ 7.3.0'da derlenmiştir.

Kullanılan karakterler: %:include<ostram>()f-.

Diğer cevaplardaki gelişmeler:

  1. forDöngüleri ifve özyineleme değiştirerek noktalı virgül kaldırıldı .
  2. Uzay karakterini std::addressof(std::cout)->fill()aldım, aka std::cout.fill().

std :: AddressOf, güzel!
Johan du Toit

2

21 Beyaz alanlar hariç 20 benzersiz karakter

Tüm beyaz alanlar yeni satırlara dönüştürülebilir.

%: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 ile çıkar. Kullanılan karakterler: %:include<ostram>;-h.

64 bit Linux'ta bu derleyici sürümünde çalışır:

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

Parametre ile:

-std=c++17

O zaman bile, her zaman işe yarayacağından emin değilim. Ayrıca birçok başka şeye de bağlı olabilir. ciave ciuarasında 4'e bölünmesiyle bellek konumlarıdır ia iuve i. ( intbu sürümde 32 bittir.) Sayıları gerçek ofsetle eşleşecek şekilde değiştirmeniz gerekebilir. Adreslerin tümü bir yapıda bulunuyorsa çok daha öngörülebilir olurdu. Ne yazık ki autobir yapıda statik olmayan yapılara izin verilmez.

e(2 32 -1) × 2 32 bayt boyutunda bir eleman tipinde 0 elemanlık bir dizidir . Karşılık gelen imleç tipi eazaltılırsa, imlecin daha yüksek yarısı (2 32 -1) azaltılır , bu da bir arttırmaya eşdeğerdir. Bu, eşitlik işaretini kullanmadan azalan sayacı sıfırlayabilir.

Daha güvenilir çalışması gereken, ancak bir karakter daha kullanan daha makul bir sürüm =:

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

Bu bile g ++ 'nın en son sürümünde çalışmıyor çünkü mainartık rastgele bir tipte tanımlamaya izin vermiyor gibi görünüyor .

Bu iki program parantez kullanmaz. Ama sonra noktalı virgüllerden kaçınılamaz gibi görünüyor.


1

22 Boşluklar hariç benzersiz karakterler. Sayıları Windows'ta doğru görüntülenen NUL karakteriyle ayırır.

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

Çevrimiçi deneyin

histogram:

[%] 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 :: uçları char(0)bir boşluk ( char(32)ASCII / UTF-8'de) bir NUL karakteridir ( ). en.cppreference.com/w/cpp/io/manip/ends . Sadece emin olmak için Linux masaüstümde denedim ve çıktı 1234değil gibi görünüyor 1 2 3 4. TIO çıkışınızda da aynı şekilde görünüyor!
Peter Cordes

@PeterCordes, OP sayıların nasıl ayrılması gerektiğini belirtmez ;-)
Johan du Toit

Eğer gerçekten onlar üzerinde bir karakteri boşa olurdu sizce "için " "kullandıkları eğer iiiiile ayırmak '0'için 10203040? Programın ikili çıktısında hala bir ayırıcı olduğunu tahmin edebilirsiniz, ancak bu değişikliği belirtmek ve İngilizce olarak tanımlamak cevabınız için önemlidir, çünkü bu bir damla değiştirme değildir! Bunu açıklamak ve haklı göstermek için cevabınızı genişletirseniz downvote'umu kaldırmaktan memnuniyet duyarım.
Peter Cordes

1
@PeterCordes, Alınan nokta.
Johan du Toit
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.