Tạo thang số nguyên bằng cách sử dụng số lượng ký tự duy nhất ít nhất (trong C ++)


13

Tôi mới tham gia môn thể thao golf mã. Tôi đang cố gắng tạo ra một thang số nguyên bằng cách sử dụng số lượng ký tự duy nhất ít nhất trong C ++.

Giả sử chúng ta được cấp một số nguyên 4.

Chúng tôi sẽ tạo các bậc thang sau:

1
1 2
1 2 3
1 2 3 4

Nói tóm lại, chương trình của tôi sẽ đọc một số nguyên dương từ stdin và in thang này đến đầu ra. Tôi đang cố gắng làm như vậy với số lượng ký tự ít nhất có thể.

Chương trình của tôi như sau:

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

Đây là trình kiểm tra mà tôi đã sử dụng để kiểm tra số lượng ký tự duy nhất trong chương trình của mình:

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

Tốt nhất là tôi muốn sử dụng ít hơn 25 ký tự duy nhất để hoàn thành chương trình này (không bao gồm các ký tự dòng mới nhưng bao gồm cả khoảng trắng). Hiện tại, chương trình của tôi sử dụng 27. Tôi không chắc làm thế nào để tối ưu hóa nó hơn nữa.

Ai đó có thể vui lòng tư vấn cho tôi về cách tối ưu hóa hơn nữa (về số lượng ký tự duy nhất được sử dụng) không? Xin lưu ý rằng chỉ C ++ có thể được sử dụng.


5
Chắc chắn sẽ rất mới lạ khi hỏi những lời khuyên liên quan đến bất kỳ tiêu chí chấm điểm nào khác ngoài môn đánh gôn , nhưng thật ra, đó là về chủ đề, vì các trang mẹo nói để làm cho nó trở thành một câu trả lời tốt hơn cho một thách thức lập trình theo chủ đề .
Adám

8
@LuisMendo Tôi thực sự không nghĩ đó là sự thật trong trường hợp này, vì nhiều ngôn ngữ hoàn toàn tầm thường hóa sơ đồ chấm điểm này. Nếu người dùng này muốn giúp học "golf độc đáo" thì nó chỉ thực sự có ý nghĩa trong một tập hợp ngôn ngữ, vì vậy tôi nghĩ rằng đây là một mẹo tốt hơn nhiều so với một thách thức chung. Điều đó nói rằng vấn đề cơ bản có thể là một thách thức nếu ai đó muốn đăng nó.
FryAmTheEggman

3
Tôi nghĩ rằng bạn có thể sử dụng sơ đồ <% và%> thay vì dấu ngoặc nhọn và tôi nghĩ rằng tôi đã bỏ lỡ một số.
đại từ của tôi là monicareinstate

2
Tôi chắc chắn đã bỏ lỡ một số. # là%:, vì vậy bạn có thể thoát khỏi ba ký tự và giới thiệu một ký tự ({=> <%,} =>%>, # =>% :) và đến 25. Nếu bạn kết hợp điều này với câu trả lời bên dưới, tôi nghĩ rằng bạn có thể nhận được 24.
đại từ của tôi là monicareinstate

2
@LanceHAOH Tricles là cực kỳ phổ biến trong các câu hỏi [ngầm], và các bản tóm tắt cũng xuất hiện khi đọc về các bức thư.
đại từ của tôi là monicareinstate

Câu trả lời:


12

Tôi tin rằng tôi đã quản lý để xóa ký tự = khỏi mã của bạn, mặc dù hiện tại nó chậm hơn đáng kể

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

Nó không đẹp, nhưng bằng cách lạm dụng tràn số nguyên, chúng ta có thể quay về 0 mà không cần sử dụng =

Ngoài ra chúng tôi đã phải thay đổi các vệ sĩ một chút. Thật không may vì bao gồm tôi không thể loại bỏ tất cả các ký tự dòng mới (mặc dù nó gần) nên đó có thể là con đường tiếp theo để điều tra.

Chỉnh sửa: Hết thời gian, nhưng nếu bạn bao gồm và sử dụng chuỗi và nhiều thư viện khác, tôi nghĩ bạn cũng có thể xóa "ký tự, một lần nữa bằng cách sử dụng số nguyên để đến đúng ký tự cho không gian và chuyển nó vào ký tự dòng chảy


2
Bạn có thể #include<std>và loại bỏ tất cả các :s. Không thực hành mã hóa tuyệt vời, nhưng đó là bên cạnh điểm.
Darrel Hoffman

3
@DarrelHoffman Tôi không thể làm việc đó được, bạn không phải làm điều using namespace std;đó sẽ sử dụng một p bổ sung cho: vì vậy, mạng 0
Dữ liệu hết hạn vào

Hừm. Có lẽ, C ++ của tôi là một thứ gỉ. Ngoài ra, điều đó thêm một g, vì vậy tôi mất ròng. Nếu đây là vàng mã, chúng ta có thể làm giảm số lượng byte bằng cách đổi tên ii, iiiiiiiđể tên chữ cái khác (chọn bất kỳ thư đã được sử dụng khác), nhưng đó không phải là những gì thách thức này là về, vì vậy tôi đoán không. Tôi tự hỏi nếu có bất kỳ lợi ích nào khi sử dụng getcputcthay vì cin/ cout, sẽ phải thử nó.
Darrel Hoffman

1
Lỗi của tôi. Tôi chỉ đọc qua kiểm tra một lần nữa. Có vẻ như nhân vật dòng mới bị bỏ qua. Vì vậy, thực sự không cần phải bận tâm về việc loại bỏ các dòng mới. Nhưng kết hợp với chiến lược của bạn và giải pháp của @someone trong các bình luận, tôi đã xoay sở để đưa nó đến 24 ký tự. Tôi đã làm cho chương trình thậm chí nhanh hơn bằng cách sử dụng ngắn thay vì int. Vì vậy, tôi đã có thêm một nhân vật 'h'. Nhưng điều này cho phép tôi sử dụng kiểu dữ liệu char mà không phải trả thêm chi phí. Vì vậy, tôi cũng đã thoát khỏi "ký tự bằng cách sử dụng mã ký tự.
LanceHAOH

@LanceHAOH: lưu ý rằng tràn số nguyên đã ký là hành vi không xác định trong C ++, cho tất cả các loại đã ký bao gồm signed char. Nếu bạn biên dịch với tối ưu hóa được bật, mã này có thể bị hỏng với các trình biên dịch hiện đại, trừ khi bạn sử dụng gcc -fwrapvđể làm cho tràn tràn được ký được xác định rõ là bao bọc bổ sung của 2. hỗ trợ clang -fwrapv, quá. ( unsignedcác loại số nguyên bao gồm unsigned charcó hành vi được xác định rõ (bao quanh) trong ISO C ++). Nó phụ thuộc vào ABI cho dù charsigned charhay unsigned char, vì vậy charcó thể ok.
Peter Cordes

10

Cuối cùng tôi đã có 24 ký tự duy nhất bằng cách kết hợp các câu trả lời của @ExpiredData và @someone. Ngoài ra, sử dụng kiểu dữ liệu ngắn thay vì int đã giúp tăng tốc chương trình của tôi vì phải mất một thời gian ngắn hơn để vượt qua loại dữ liệu ngắn.

Mã của tôi là như sau.

%: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 anh ấy sử dụng nó char iiiii;, lần khởi tạo cuối cùng của biến.
Rɪᴋᴇʀ

1
@KevinCruijssen Đó là sự thật. Nhưng điều đó cho phép tôi loại bỏ "ký tự vì tôi có thể sử dụng mã ký tự để thể hiện ký tự khoảng
trắng

9

23 ký tự độc đáo sử dụng Digraphs. (25 không có). Không có UB.

Sử dụng cú pháp khởi tạo giằng C ++ 11 để liệt kê khởi tạo một số nguyên về 0 mà int var{};tránh =0. (Hoặc trong trường hợp của bạn, tránh toàn cầu iiii). Điều này cung cấp cho bạn một nguồn số không khác với các biến toàn cục (được khởi tạo tĩnh thành 0, không giống như các địa phương).

Trình biên dịch hiện tại chấp nhận cú pháp này theo mặc định, mà không phải bật bất kỳ tùy chọn đặc biệt nào.

(Thủ thuật tổng hợp số nguyên rất thú vị và ok khi chơi golf với tối ưu hóa bị vô hiệu hóa, nhưng tràn tràn đã ký là hành vi không xác định trong ISO C ++. Kích hoạt tối ưu hóa sẽ biến các vòng lặp đó thành các vòng lặp vô hạn, trừ khi bạn biên dịch với gcc / clang -fwrapvđể cung cấp tràn số nguyên đã ký hành vi được xác định: gói bổ sung của 2.

Sự thật thú vị: ISO C ++ std::atomic<int>có sự bao bọc hoàn hảo của 2! int32_tđược yêu cầu phải là phần bù 2 nếu được xác định hoàn toàn, nhưng hành vi tràn không được xác định để nó vẫn có thể là một typedef cho inthoặc longtrên bất kỳ máy nào có một trong các loại đó là 32 bit, không có phần đệm và phần bù của 2).


Không hữu ích cho trường hợp cụ thể này:

Bạn cũng có thể khởi tạo một biến mới dưới dạng một bản sao của một biến hiện có, với dấu ngoặc nhọn hoặc (với bộ khởi tạo không trống), parens để khởi tạo trực tiếp .
int a(b)hoặc int a{b}tương đương vớiint a = b;

Nhưng int b();khai báo một hàm thay vì một biến được khởi tạo bằng không.

Ngoài ra, bạn có thể nhận được số 0 bằng int()hoặc char(), tức là không khởi tạo đối tượng ẩn danh.


Chúng tôi có thể thay thế <=so sánh của bạn bằng <so sánh bằng một phép chuyển đổi logic đơn giản : thực hiện tăng số đếm vòng lặp ngay sau khi so sánh, thay vì ở dưới cùng của vòng lặp. IMO điều này đơn giản hơn các lựa chọn thay thế mà mọi người đã đề xuất, như sử dụng ++trong phần đầu tiên của a for()để biến 0 thành 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;
    }

Chúng ta có thể đánh gôn xuống for(int r{}; r++ < n;)nhưng IMO không dễ đọc cho con người. Chúng tôi không tối ưu hóa cho tổng số byte.


Nếu chúng tôi đã sử dụng h, chúng tôi có thể tiết kiệm 'hoặc "cho một không gian.

Giả sử môi trường ASCII hoặc UTF-8, không gian là chargiá trị 32. Chúng ta có thể tạo nó trong một biến đủ dễ dàng, sau đó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

Và các giá trị khác rõ ràng có thể được tạo ra từ một chuỗi ++và nhân đôi, dựa trên các bit của biểu diễn nhị phân của chúng. Hiệu quả chuyển 0 (không có gì) hoặc 1 (++) vào LSB trước khi nhân đôi thành một biến mới.


Phiên bản này sử dụng hthay vì 'hoặc ".

Nó nhanh hơn nhiều so với một trong các phiên bản hiện có (không dựa vào một vòng lặp dài) và không có Hành vi không xác định . Nó biên dịch không có cảnh báo với g++ -O3 -Wall -Wextra -Wpedanticvà vớiclang++ . -std=c++11Là tùy chọn. Nó là hợp pháp và di động ISO C ++ 11 :)

Nó cũng không dựa vào các biến toàn cầu. Và tôi đã làm cho nó dễ đọc hơn với tên biến có ý nghĩa.

Số byte duy nhất: 25 , không bao gồm các nhận xét mà tôi đã loại bỏg++ -E . Và không bao gồm không gian và dòng mới như quầy của bạn. Tôi đã sử dụng sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic từ Ubuntu này để đếm số lần xuất hiện của từng nhân vật, và đưa nó vào wcđể đếm xem tôi có bao nhiêu ký tự duy nhất.

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

Chỉ có 2 fnhân vật là từ for. Chúng ta có thể sử dụng whilecác vòng thay thế nếu chúng ta đã sử dụng w.

Chúng ta có thể viết lại các vòng lặp thành một kiểu ngôn ngữ lắp ráp i < r || goto some_label;để viết một bước nhảy có điều kiện ở cuối vòng lặp, hoặc bất cứ điều gì. (Nhưng sử dụng orthay vì ||). Không, điều đó không làm việc. gotolà một câu lệnh like ifvà không thể là thành phần phụ của biểu thức giống như trong Perl. Nếu không, chúng ta có thể đã sử dụng nó để loại bỏ các ký tự ().

Chúng tôi có thể giao dịch fcho gvới if(stuff) goto label;thay for, và cả hai vòng luôn chạy ít nhất 1 lần lặp vì vậy chúng tôi chỉ cần một vòng lặp-chi nhánh ở phía dưới, giống như một asm bình thường do{}whilecấu trúc vòng lặp. Giả sử người dùng nhập số nguyên> 0 ...


Digraphs và Tricles

May mắn thay, các bộ ba đã bị xóa kể từ ISO C ++ 17, vì vậy chúng tôi không phải sử dụng ??>thay vì }nếu chúng tôi chơi golf duy nhất cho phiên bản C ++ gần đây nhất.

Nhưng chỉ có các bộ ba cụ thể: ISO C ++ 17 vẫn có các sơ đồ như :>cho ]%>cho} . Vì vậy, với chi phí sử dụng %, chúng ta có thể tránh cả hai {}, và sử dụng %:để #tiết kiệm ròng ít hơn 2 ký tự duy nhất.

Và C ++ có các từ khóa toán tử như notcho !toán tử hoặc bitorcho |toán tử. Với xor_eqcho ^=, bạn có thể bằng không một biến với i xor_eq i, nhưng nó có nhiều ký tự mà bạn không sử dụng.

Hiện tại g++đã bỏ qua các biểu tượng theo mặc định ngay cả khi không có -std=gnu++17; bạn phải sử dụng -trigraphsđể kích hoạt chúng, -std=c++11hoặc một cái gì đó để tuân thủ nghiêm ngặt tiêu chuẩn ISO bao gồm chúng.

23 byte duy nhất:

%: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ãy thử trực tuyến!

Phiên bản cuối cùng sử dụng một 'trích dẫn duy nhất thay vì hhoặc "cho dấu phân cách không gian. Tôi không muốn trình bày char c{}nội dung nên tôi đã xóa nó. In một char hiệu quả hơn in một chuỗi, vì vậy tôi đã sử dụng nó.

Biểu đồ:

$ 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

Dấu phân cách không gian (vẫn chưa được giải quyết)

Trong một câu trả lời hiện đã bị xóa, cụ thể là Johan Du Toit đã đề xuất sử dụng một dấu phân cách thay thế std::ends. Đó là một ký tự NUL char(0)và in dưới dạng độ rộng bằng không trên hầu hết các thiết bị đầu cuối. Vì vậy, đầu ra sẽ như thế nào 1234, không 1 2 3 4. Hoặc tệ hơn, bị ngăn cách bởi rác trên bất cứ thứ gì không âm thầm sụp đổ '\0'.

Nếu bạn có thể sử dụng một dấu tách tùy ý, khi chữ số 0dễ tạo cout << some_zeroed_var. Nhưng không ai muốn 10203040, điều đó thậm chí còn tệ hơn là không có dải phân cách.

Tôi đã cố gắng nghĩ ra một cách để tạo ra một std::stringtổ chức" " mà không cần sử dụng charhoặc một chuỗi ký tự. Có thể nối thêm một cái gì đó cho nó? Có thể với một sơ đồ []để đặt byte đầu tiên thành giá trị của 32, sau khi tạo một byte có độ dài 1 thông qua một trong các hàm tạo?

Johan cũng đề xuất std::ioshàm thành viên fill () trả về ký tự điền hiện tại. Mặc định cho một luồng được đặt bởi std::basic_ios::init()và là ' '.

std::cout << i << std::cout.fill();thay thế << ' ';nhưng sử dụng .thay vì' .

Với -, chúng ta có thể lấy một con trỏ tới coutvà sử dụng ->fill()để gọi hàm thành viên :
std::cout << (bitand std::cout)->fill(). Hoặc không, chúng tôi không sử dụng bhoặc vì vậy chúng tôi cũng có thể đã sử dụng &thay vì tương đương từ vựng của nó , bitand.

Gọi một chức năng thành viên mà không có .hoặc->

Đặt nó trong một lớp và định nghĩa 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(); }
}

Sau đó ss s{}trước vòng lặp, và std::cout << i << s;bên trong vòng lặp. Tuyệt vời, nó biên dịch và hoạt động đúng, nhưng chúng tôi đã phải sử dụng phcho operator char(), vì mất ròng 1. Ít nhất chúng tôi đã tránh bthực hiện các chức năng thành viên publicbằng cách sử dụng structthay vì class. (Và chúng tôi có thể ghi đè lên phần thừa kế protectedtrong trường hợp có ích).


@JohanduToit: ý tưởng hay với cout.fill()từstd::ios , nhưng trước đây chúng tôi chưa sử dụng . Có thể chúng tôi có thể gọi nó bằng cách nào đó bằng cách lấy một con trỏ và sử dụng ->fill()cho một hàm thành viên? Có bất cứ điều gì trả về một con trỏ đến couthoặc bất kỳ luồng nào khác không?
Peter Cordes

Rất tiếc, << (bitand std::cout)->fill()biên dịch, nhưng sử dụng -. (Mặc dù tên mã thông báo, bitandchỉ là một từ vựng tương đương &, không cụ thể là toán tử bitwise và toán tử. Nó cũng hoạt động như địa chỉ của toán tử.) Hmm, có một số công cụ mẫu hoặc lambda có thể đưa con trỏ đến hàm thành viên mà chúng ta có thể ()mà không cần sử dụng .hay ->?
Peter Cordes

1
Điều khác duy nhất mà tôi tìm thấy là std::ios::leftđược định nghĩa là 32, trong gcc, nhưng tôi thực sự không thể tìm ra cách nào để tận dụng lợi thế đó. Tôi nghĩ rằng tôi sẽ để điều này diễn ra và hoàn thành một số công việc thực tế :-)
Johan du Toit

@JohanduToit: Tạo intsố 32 không phải là vấn đề, câu trả lời của tôi đã chỉ ra cách thực hiện điều đó với ++bắt đầu từ int c{};số không. Nhưng vâng, tôi sẽ không xuống hố thỏ khi nhìn vào lambdas, mẫu, hay std::function. Hoặc std::stringý tưởng. Nhưng chúng tôi không sử dụng gđể thực sự không thể tuyên bố std::stringmà không mất; ý tưởng của tôi để sử dụng gotothay vì forkhông pan. decltype(something)có thể cung cấp cho chúng tôi một charloại, nhưng chi phí cho chúng tôi a y.
Peter Cordes

1
Bạn có thể sử dụng tự động thay vì char cho opeator: struct ss : std::ostream { operator auto () { return fill(); } };nhưng nó không giúp được gì nhiều.
Johan du Toit

7

Chỉ C ++ (gcc) x86_64 Linux, 9295 8900 8712 6812 5590 byte, 18 ký tự duy nhất

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

Hãy thử trực tuyến!

Điều này dựa trên ý tưởng từ câu trả lời PPCG này . Một chương trình ngôn ngữ máy được thể hiện dưới dạng một mảng gồm 32 bit ints, mỗi chuỗi được biểu diễn dưới dạng tổng 1+11+111.... Nó chỉ ra rằng nó có thể hiệu quả hơn để mã hóa xnhư yvậy y%(1<<32)==x. Chương trình ngôn ngữ máy được mã hóa như sau

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

... dựa trên mã C sau đây.

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

Chỉnh sửa: Bây giờ chấp nhận đầu vào từ stdinthay vì argv[1]. Cảm ơn chỉ @ ASCII và @PeterCordes vì ​​những gợi ý của họ!

Edit4: Hơi được cải thiện đáng kể mã hóa.


-wcờ xin vui lòng: P (bạn cũng có thể đổi tên iithành a)
ASCII chỉ

Bạn cần gcc -zexecstackcho điều này, phải không? Bởi vì int m[]không phải const. (Và các bộ công cụ gần đây đã đặt .rodatavào một trang không thể thực thi được nên thậm chí const int m[]không hoạt động trên hệ thống Arch Linux của tôi với gcc8.2.1 20181127 và ld(GNU Binutils) 2.31.1.) Dù sao, bạn đã quên đề cập đến câu trả lời của mình, nhưng đó là trong liên kết TIO của bạn.
Peter Cordes

BTW, thuật toán tính điểm duy nhất của OP không tính không gian và dòng mới, vì vậy bạn không cần phải làm cho toàn bộ điều khủng khiếp để đọc, chỉ là mảng: P
Peter Cordes

Bạn có thể lưu byte mã máy bằng cách sao chép 1bằng push %rax/ pop %rdithay vì đẩy ngay lập tức. Hay đơn giản hơn, đối với các giá trị không phải là 64 bit, tức là không phải con trỏ, 2 byte mov %eax, %edi. Ngoài ra, Linux syscallkhông phá hủy các thanh ghi đầu vào của nó, chỉ raxvới giá trị trả về và RCX + R11 với RIP và RFLAGS đã lưu như một phần trong cách syscallhoạt động của lệnh. Vì vậy, bạn có thể rời khỏi rdirdxđặt thành 1các cuộc gọi và sử dụng các reg khác nhau. Ngoài ra, RBX được bảo toàn cuộc gọi, do đó, nó không thực sự tiết kiệm cho RBX của clobber chính. Nó xảy ra để làm việc vì mã bắt đầu CRT không quan tâm.
Peter Cordes

6

21 ký tự duy nhất + 1 dòng mới không thể thay đổi

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

Không cần phải có khoảng trắng ngoại trừ dòng mới đầu tiên. Được biên dịch trong g ++ 7.3.0.

Ký tự được sử dụng : %:include<ostram>()f-.

Cải tiến cho các câu trả lời khác:

  1. Xóa dấu chấm phẩy bằng cách thay đổi forvòng lặp ifvà đệ quy.
  2. Có nhân vật không gian bởi std::addressof(std::cout)->fill(), aka std::cout.fill().

std :: addressof, tốt đẹp!
Johan du Toit

2

21 20 ký tự độc đáo không bao gồm khoảng trắng

Tất cả các khoảng trắng có thể được thay đổi thành dòng mới.

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

Thoát với segfault. Ký tự được sử dụng : %:include<ostram>;-h.

Nó hoạt động trong phiên bản trình biên dịch cụ thể này trên Linux 64 bit:

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

Với tham số:

-std=c++17

Ngay cả sau đó, tôi không chắc nó sẽ luôn hoạt động. Nó cũng có thể phụ thuộc vào rất nhiều thứ khác. ciaciulà độ lệch bộ nhớ chia cho 4 giữa ia iui. ( intlà 32 bit trong phiên bản này.) Bạn có thể phải thay đổi số để khớp với phần bù thực tế. Các địa chỉ sẽ dễ dự đoán hơn nhiều nếu tất cả chúng đều được chứa trong một cấu trúc. Thật không may, không tĩnh autokhông được phép trong một cấu trúc.

elà mảng 0 phần tử của loại phần tử có kích thước (2 32 -1) × 2 32 byte. Nếu loại con trỏ tương ứng ebị giảm, thì nửa con trỏ cao hơn sẽ bị giảm theo (2 32 -1), tương đương với tăng thêm một. Điều này có thể thiết lập lại bộ đếm giảm mà không sử dụng dấu bằng.

Một phiên bản hợp lý hơn sẽ hoạt động đáng tin cậy hơn, nhưng sử dụng thêm một ký tự =:

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

Ngay cả điều này không hoạt động trong phiên bản mới nhất của g ++ vì dường như nó không cho phép xác định maintheo kiểu tùy ý nữa.

Hai chương trình này không sử dụng dấu ngoặc đơn. Nhưng sau đó, dấu chấm phẩy dường như không thể tránh được.


1

22 ký tự độc đáo không bao gồm khoảng trắng. Tách các số bằng ký tự NUL hiển thị chính xác trên 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;
    %>
%>

Dùng thử trực tuyến

Biểu đồ:

[%] 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 :: kết thúc là một ký tự NUL ( char(0)), không phải là khoảng trắng ( char(32)trong ASCII / UTF-8). en.cppreference.com/w/cpp/io/manip/ends . Tôi đã thử nó trên máy tính để bàn Linux của tôi chỉ để đảm bảo, và đầu ra trông như thế 1234, không phải 1 2 3 4. Nó trông giống như trên đầu ra TIO của bạn!
Peter Cordes

@PeterCordes, OP không chỉ định cách tách các số ;-)
Johan du Toit

Bạn có thực sự nghĩ rằng họ có thể đã lãng phí một nhân vật trên "cho " "nếu họ có thể đã sử dụng iiiiđể tách với '0'cho 10203040? Tôi đoán bạn có thể tạo ra một trường hợp vẫn có dấu phân cách trong đầu ra nhị phân của chương trình, nhưng chỉ ra sự thay đổi này và mô tả nó bằng tiếng Anh rất quan trọng cho câu trả lời của bạn, bởi vì đây không phải là sự thay thế thả xuống! Tôi rất vui khi xóa downvote của tôi nếu bạn mở rộng câu trả lời của mình để giải thích và biện minh cho điều đó.
Peter Cordes

1
@PeterCordes, Điểm lấy.
Johan du Toit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.