Створюйте сходи цілих чисел, використовуючи найменшу кількість унікальних символів (у 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
Безумовно, нове, щоб попросити підказки стосовно будь-яких інших критеріїв оцінки, ніж код-гольф , але це фактично, але це тематично, оскільки на сторінках підказок написано, щоб зробити це кращою відповіддю на завдання програмування, яке є темою .
Адам

8
@LuisMendo Я насправді не думаю, що це правда в цьому випадку, оскільки багато мов цілком реалізують цю схему балів. Якщо цей користувач хоче допомогти навчитися "унікальному гольфу", це дійсно має сенс у підмножині мов, тому я думаю, що це набагато краще, як підказка, ніж як загальна проблема. Це говорить про те, що основна проблема, ймовірно, може стати проблемою, якщо хтось хоче її опублікувати.
FryAmTheEggman

3
Я думаю, що ви можете використовувати графіки <% і%> замість фігурних дужок, і я думаю, що я пропустив деякі.
мій займенник monicareinstate

2
Я напевно дещо пропустив. # є% :, ви можете позбутися трьох символів і ввести один ({=> <%,} =>%>, # =>% :) і дістати до 25. Якщо ви поєднаєте це з відповіддю нижче, я думаю, ти можеш отримати 24.
моя займенник monicareinstate

2
@LanceHAOH Триграфи є надзвичайно поширеними у [непорушних] запитаннях, і діаграми відображаються також і при читанні про триграфи.
мій займенник monicareinstate

Відповіді:


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>і ліквідувати всі :с. Не велика практика кодування, але це не в чому справа.
Даррель Гофман

3
@DarrelHoffman Я не можу зробити так, щоб це працювало, чи не потрібно робити це, using namespace std;що використовує додатковий p для: так net 0
Термін дії закінчився

Хм. Можливо, мій C ++ - це іржавий тад. Крім того, це додає g, так чистий збиток я думаю. Якби це золото з кодом, ми могли б зменшити кількість байтів шляхом перейменування ii, iiiта iiiiінших назв однієї літери (вибрати будь-які інші вже вживані літери), але це не в чому полягає ця проблема, тому я думаю, що ні. Мені цікаво, чи не було б користі від використання, getcа putcзамість cin/ cout, довелося б спробувати.
Даррель Гофман

1
Моє ліжко. Я просто ще раз перечитав шашку. Здається, що символ нового рядка ігнорується. Тож насправді не потрібно турбуватися про видалення нових рядків. Але в поєднанні з вашою стратегією та рішенням @someone в коментарях мені вдалося дістати її до 24 символів. Я зробив програму ще швидше, використовуючи короткий замість int. Так я отримав додатковий характер 'h'. Але це дозволить мені використовувати тип даних char без додаткових витрат. Тож я позбувся "персонажа" і за допомогою коду символів.
Дональд

@LanceHAOH: зауважте, що переливання підписаного цілого числа є невизначеною поведінкою в C ++, для всіх підписаних типів, включаючи signed char. Якщо ви компілюєте з увімкненою оптимізацією, цей код може розірватися із сучасними компіляторами, якщо тільки ви gcc -fwrapvне зробите підписаний переповнення чітко визначеним як обшивка доповнення 2. опори кланг -fwrapvтеж. ( unsignedцілі типи, включаючи unsigned charчітко визначену поведінку (обертання) в ISO C ++). Це залежить від ABI чи charце signed charабо unsigned char, так charможе бути в порядку.
Пітер Кордес

10

Нарешті я отримав 24 унікальних персонажа, поєднавши відповіді @ExpiredData та @someone. Також використання короткого типу даних замість int допомогло пришвидшити мою програму, оскільки для переповнення короткого типу даних потрібно коротший час.

Мій код такий.

%: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.
Дональд

9

23 унікальні символи за допомогою Digraphs. (25 без). Немає УБ.

Використовуйте синтаксис ініціалізатора, уповноваженого C ++ 11, для списку-ініціалізації цілого числа до нуля, int var{};уникаючи =та 0. (Або у вашому випадку, уникаючи глобальних iiii). Це дає вам джерело нулів, окрім глобальних змінних (які статично ініціалізовані до нуля, на відміну від локальних).

Поточні компілятори приймають цей синтаксис за замовчуванням, не вмикаючи жодних спеціальних параметрів.

(Цілий фокус накрутки - це весело, і нормально для гольфу з вимкненою оптимізацією, але підписане переповнення не визначене поведінка в ISO C ++. Увімкнення оптимізації перетворить ці петлі накрутки в нескінченні петлі, якщо ви не компілюєте з gcc / clang, -fwrapvщоб дати підписаному цілому цілому переповнення добре -визначена поведінка: обробка комплексу 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(), тобто нуль-ініціалізації анонімного об'єкта.


Ми можемо замінити ваші <=порівняння зі <порівняннями простим логічним перетворенням : зробіть приріст циклу лічильника відразу після порівняння, а не внизу циклу. ІМО це простіше, ніж запропоновані людьми альтернативи, як, наприклад, використовувати ++в першій частині а, 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;)ІМО, який людині читати менш просто. Ми не оптимізуємо загальну кількість байтів.


Якщо ми вже використовували 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

І інші значення, очевидно, можуть бути створені з послідовності ++та подвоєння, виходячи з бітів їх бінарного подання. Ефективно зміщення 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;
    }
}

Тільки 2 fсимволи - від for. Ми могли б whileзамість цього використовувати петлі, якби для них використовувались w.

Ми, можливо, зможемо переписати петлі у стилі мови складання, i < r || goto some_label;щоб написати умовний стрибок у нижній частині циклу, чи будь-що інше. (Але використовуючи orзамість ||). Ні, це не працює. gotoє твердженням, як ifі не може бути підкомпонентним виразом, як він може в Perl. В іншому випадку ми могли б використовувати його для видалення символів (і ).

Ми могли б торгувати fна gз if(stuff) goto label;а for, і обидві петлі завжди виконуються по крайней мере 1 ітерації , тому ми повинні були б тільки одну петлю-гілки в нижній частині, як нормальний ASM do{}whileструктури петлі. Якщо припустити, що користувач вводить ціле число> 0 ...


Диграфи та триграфи

На щастя, триграфи були вилучені за ISO C ++ 17, тому нам не доведеться використовувати ??>замість того, }якщо ми унікальні для гольфу для останньої редакції C ++.

Але тільки тріграфи саме: ISO C ++ 17 по і раніше має диграфів як :>для ]і %>для} . Тож ціною використання %ми можемо уникнути і {і }, і використовувати %:для #чистої економії на 2 менших унікальних символи.

І C ++ має ключові слова оператора, як notдля !оператора, або bitorдля |оператора. З допомогою xor_eqfor ^=, ви можете нульову змінну i xor_eq i, але вона містить кілька символів, які ви не використовували.

Поточна g++вже ігнорує триграфи за замовчуванням навіть без -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

Розділювач простору (досі невирішений)

У видаленій відповіді Йохан Дю Тойт запропонував конкретно використовувати альтернативний роздільник std::ends. Це символ NUL char(0), і друкується як нульова ширина на більшості терміналів. Таким чином, вихід буде виглядати, як 1234ні 1 2 3 4. Або ще гірше, розділений сміттям на будь-що, що мовчки не руйнувалося '\0'.

Якщо ви можете використовувати довільний роздільник, коли цифру 0легко створити за допомогою cout << some_zeroed_var. Але ніхто не хоче 10203040, це ще гірше, ніж жоден роздільник.

Я намагався придумати спосіб створення std::stringхолдингу" " без використання charабо рядкового літералу. Можливо, до чогось додати? Може, за допомогою диграфа для []встановлення першого байта значення 32, після створення одного з довжиною 1 через один з конструкторів?

Йохан також запропонував std::iosфункцію члена (), яка повертає поточний символ заповнення. За замовчуванням для потоку встановлюється 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;всередині петлі. Відмінно, він збирає і працює нормально, але ми повинні були використовувати pі hдля operator char(), для чистої втрати 1. Принаймні , ми уникали , bщоб функцій - членів public, використовуючи structзамість class. (І ми могли б перекрити спадщину, protectedякщо це коли-небудь допоможе).


@JohanduToit: хороша ідея з cout.fill()відstd::ios , але ми раніше не використовували . Можливо, ми можемо це якось назвати, взявши покажчик і використовуючи ->fill()функцію члена? Чи повертає щось покажчик на coutбудь-який інший потік?
Пітер Кордес

На жаль, << (bitand std::cout)->fill()компілює, але використовує -. (Незважаючи на назву лексеми, bitandце лише лексичний еквівалент &, а не конкретно побітовий і оператор. Він також працює як адреса оператора.) Хм, чи є деякі матеріали шаблону чи лямбда, які можуть отримати вказівник на функцію члена що ми можемо ()без використання .або ->?
Пітер Кордес

1
Єдине інше, що я виявив - це те, що std::ios::leftвизначено як 32, в gcc, але я не міг реально розібратися, як скористатися цим. Я думаю, що я відпущу цього і
Йохан дю

@JohanduToit: Створення int32-х не є проблемою, моя відповідь уже показує, як це зробити, ++починаючи з int c{};нуля. Але так, я не спускаюсь із кролячої нори, щоб заглянути в лямбди, шаблони чи std::function. Або std::stringідея. Але ми не звикли, що gнасправді не можна оголосити std::stringбез втрати; моя ідея використовувати gotoзамість того, forщоб не спала . decltype(something)може дати нам charтип, але коштує нам y.
Пітер Кордес

1
Ви можете використовувати авто замість char для оператора: 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 бітових ints, кожен з яких представлений у вигляді суми 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

... що базується на наведеному нижче коді С.

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 та @PeterCordes за їх пропозиції!

Edit4: Трохи значно покращене кодування.


-wпрапор ласка: P (також можна перейменувати iiв a)
ASCII-тільки

Вам це потрібно gcc -zexecstack, правда? Тому що int m[]ні const. (І останні інструментальні ланцюги все-таки поміщаються .rodataна невиконанну сторінку, тому навіть const int m[]не працює, наприклад, в моїй системі Arch Linux з gcc8.2.1 20181127 та ld(GNU Binutils) 2.31.1.) У будь-якому випадку, ви забули згадати про це у своїй відповіді, але це у вашому TIO-посиланні.
Пітер Кордес

До речі, алгоритм підрахунку унікальних підрахунків ОП не рахує місця та нового рядка, тому вам не доведеться робити всю справу страшною для читання, просто масив: P
Пітер Кордес

Ви можете зберегти байти машинного коду, скопіювавши 1з push %rax/ pop %rdiзамість іншого прямого натискання. Або, простіше кажучи, для значень, які не є 64-бітовими, тобто не-покажчиками, 2-байтними mov %eax, %edi. Крім того, Linux syscallне руйнує свої вхідні регістри, тільки raxзі зворотним значенням і RCX + R11 із збереженими RIP та RFLAGS як частина того, як syscallпрацює інструкція. Таким чином, ви можете залишати rdiта rdxвстановлювати 1різні дзвінки та використовувати різні регістри. Крім того, RBX зберігає виклики, тому не дуже ефективно економити 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))<%%>
%>

Білі простори не потрібні, окрім першого нового рядка. Складено у г ++ 7.3.0.

Використовувані символи: %:include<ostram>()f-.

Вдосконалення інших відповідей:

  1. Видалено крапки з комою шляхом зміни forциклів на ifта рекурсії.
  2. Отримав пробільний персонаж std::addressof(std::cout)->fill(), ака std::cout.fill().

std :: addressof, приємно!
Йохан дю

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 iuі i. ( intу цій версії 32 біт.) Можливо, вам доведеться змінити числа, щоб відповідати фактичному зміщенню. Адреси були б набагато передбачуванішими, якби вони містяться в структурі. На жаль, нестатичні autoв структурі не допускаються.

eявляє собою масив 0 елементів типу елементів розміром (2 32 -1) × 2 32 байт. Якщо відповідний тип вказівника eзменшено, то вища половина вказівника буде зменшена на (2 32 -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в довільному типі.

Ці дві програми не використовують дужки. Але тоді крапки з комою не здаються уникнутими.


1

22 унікальних символів, виключаючи пробіли. Розділяє числа з символом NUL, який правильно відображається в Windows.

%:include<iostream>
int main(int n)<%
    std::cin>>n;
    for(int r<%%>;r++<n;)<%
        for(int i<%%>;i<r;)
            std::cout<<++i<<std::ends;
        std::cout<<std::endl;
    %>
%>

Спробуйте в Інтернеті

Гістограма:

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

std :: end - символ NUL ( char(0)), а не пробіл ( char(32)в ASCII / UTF-8). en.cppreference.com/w/cpp/io/manip/ends . Я спробував це на своєму робочому столі Linux, щоб переконатися, і результат виглядає, як 1234ні 1 2 3 4. Це виглядає так само на вашому виході TIO!
Пітер Кордес

@PeterCordes, ОП не визначає, як слід розділяти номери ;-)
Йохан дю

Ви дійсно думаєте , що вони б витратили характер на "для , " "якщо вони могли б використовувати iiiiдля поділу з '0'для 10203040? Я думаю, ви можете зробити так, що у двійковому виході програми все ще є роздільник, але вказівка ​​на цю зміну та опис її англійською мовою є важливою для вашої відповіді, оскільки це не заміна заміни! Я був би радий зняти свою позицію, якщо ви розгорнете свою відповідь, щоб пояснити та виправдати це.
Пітер Кордес

1
@PeterCordes, точка взята.
Йохан дю
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.