使用最少数量的唯一字符生成整数阶梯(在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
寻求有关除代码高尔夫以外的任何其他评分标准的提示当然是新颖的,但是afaict确实是主题,因为提示页面说它可以更好地解决主题编程难题
阿达姆

8
@LuisMendo在这种情况下,我真的不认为这是正确的,因为许多语言都完全忽略了这种计分方案。如果该用户希望帮助学习“独特的高尔夫”知识,那么它实际上仅在部分语言中才有意义,因此,我认为这比一般性提示要好得多。那就是说,如果有人要发布基本问题,则可能是一个挑战。
FryAmTheEggman,

3
我认为您可以使用图<%和%>代替花括号,并且我想念了一些。
我的代词是monicareinstate,

2
我肯定想念一些。#是%:,因此您可以删除三个字符并引入一个({=> <%,} =>%>,#=>%:)并得出25。如果将其与下面的答案结合使用,我认为你可以得到24
我的代名词是monicareinstate

2
@LanceHAOH三元组在[不足的问题]中极为常见,在阅读有关三元组的信息时也会显示三元组。
我的代名词是monicareinstate '19

Answers:


12

我相信我设法从您的代码中删除了=字符,尽管现在它的速度大大降低了

#include<iostream>

int i;
int ii;
int iii;
int iiii;

int main() {
    std::cin >> i;
    i++;
    for(ii++; ii < i;) {
    for(;iii>iiii;iii++);
    for(;iii<iiii;iii++);
    ii++;
        for(iii++; iii < ii; iii++) {
            std::cout << iii << " ";
        }
        std::cout << std::endl;
    }
}

它不是很漂亮,但是通过滥用整数溢出,我们无需使用=即可回到0

我们也不得不稍微改变一下警卫。不幸的是,由于包含在内,我无法摆脱所有换行符(尽管它很接近),所以这可能是下一个调查途径。

编辑:暂时没有时间了,但是如果您包括并使用strstream和其他各种库,我想您也可以删除“字符,再次使用整数来获得正确的空格字符并将其传递给strstream


2
您可以#include<std>消除所有:s。并不是很好的编码实践,但这是无关紧要的。
Darrel Hoffman

3
@DarrelHoffman我不能得到那个工作,不要你必须做using namespace std;这将使用一个额外的对的:这样的净0
过期数据

嗯 也许,我的C ++有点生疏。这也增加了一个g,所以我猜是净亏损。如果这是代码黄金,我们可以通过将iiiii和重命名iiii为其他单个字母名称(选择任何其他已使用的字母)来减少字节数,但这不是此挑战的目的,所以我想不是。我想知道是否有使用getcputc不是cin/的好处cout,必须尝试一下。
达雷尔·霍夫曼

1
我的错。我只是再次阅读了检查器。似乎换行符被忽略。因此,实际上没有必要去掉换行符。但结合您的策略和@someone在评论中的解决方案,我设法将其设置为24个字符。我通过使用short而不是int使程序变得更快。所以我又得到了一个“ h”字符。但是,这让我使用了char数据类型而无需支付额外费用。因此,我也通过使用字符代码摆脱了“字符”
。– LanceHAOH

@LanceHAOH:请注意,对于所有带符号类型,包括C,带符号整数溢出在C ++中都是未定义的行为signed char。如果您在启用优化的情况下进行编译,则此代码可能会与现代编译器一起使用,除非您习惯gcc -fwrapv使带符号的溢出定义为2的补码环绕。clang也支持-fwrapv。(unsigned整数类型包括unsigned charISO C ++中具有明确定义的行为(环绕))。取决于ABI charsigned char还是unsigned char,所以char可以。
彼得·科德斯

10

通过结合@ExpiredData和@someone的答案,我最终得到了24个独特的字符。另外,使用短数据类型而不是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。– LanceHAOH

9

使用Digraphs的23个独特字符。(25个)。没有UB。

使用C ++ 11大括号初始化器语法,通过int var{};避免=和将列表整数初始化为零0。(或者在您的情况下,避免使用global iiii)。除了全局变量(不同于局部变量,它们被静态初始化为零)之外,这还为您提供了零的来源。

当前的编译器默认情况下接受此语法,而无需启用任何特殊选项。

(整数环绕技巧很有趣,并且可以在禁用优化的情况下打高尔夫球,但是在ISO C ++中,有符号溢出是未定义的行为。启用优化会将这些环绕循环变成无限循环,除非您使用gcc / clang -fwrapv进行编译以使有符号整数溢出良好定义的行为:2的补回。

有趣的事实:ISO C ++ std::atomic<int>具有定义明确的2的补码环绕! int32_t如果完全定义,则必须为2的补码,但是溢出行为是未定义的,因此对于那些类型之一为32位,无填充和2的补码的机器intlong在任何机器上,它仍然可以是typedef 。)


在这种情况下没有用:

您还可以将新变量初始化为现有变量的副本,并使用花括号或(使用非空的初始值设定项)对原变量进行直接初始化
int a(b)int a{b}等于int a = b;

但是int b();声明一个函数而不是初始化为零的变量。

同样,您可以使用int()或来获得零char(),即对匿名对象进行零初始化


我们可以通过简单的逻辑转换将您的<=比较替换为<比较:在比较之后而不是在循环的底部进行循环计数器的递增。IMO比人们提出的替代方案更简单,例如++在a的第一部分中使用for()将0变成1。

    // comments aren't intended as part of the final golfed version
    int n;
    std::cin >> n;      // end condition

    for(int r{}; r < n;) {      // r = rows from 0 .. n-1
        ++r;
        for(int i{}; i < r;) {
            ++i;
            std::cout << i << ' ';
        }
        std::cout << std::endl;
    }

我们可以将其降低到最低水平,for(int r{}; r++ < n;)但是IMO对于人类来说不那么容易阅读。我们并未针对总字节数进行优化。


如果我们已经在使用h,则可以为'或节省"空间。

假设在ASCII或UTF-8环境中,空格为a 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字符来自forwhile如果我们有使用,则可以改用循环w

我们可以将循环重写为汇编语言样式,i < r || goto some_label;以在循环底部或任何其他位置编写条件跳转。(但使用or代替||)。不,那是行不通的。 goto类似于ifPerl中语句,并且不能像Perl中那样成为表达式的子组件。否则,我们可以使用它删除()字符。

我们可以换fgif(stuff) goto label;替代的for,并且两个回路始终运行至少1次迭代因此我们只需要在底部有一个循环分支,像一个正常的ASM do{}while循环结构。假设用户输入的整数> 0 ...


有向图和有向图

幸运的是,自ISO C ++ 17起三字母组合已被删除,因此对于最新的C ++修订版??>}如果我们使用唯一标识,就不必使用。

但仅特定于三部曲:ISO C ++ 17仍然具有:>for ]%>for 等二字}。因此,在使用的成本%,我们可以同时避免 {},并使用%:#用于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

空格分隔符(仍未解决)

在一个现已删除的答案中,Johan Du Toit建议使用备用分隔符,特别是std::ends。这是一个NUL字符,char(0)并且在大多数终端上以零宽度打印。所以输出看起来像1234不是1 2 3 4。更糟糕的是,在没有默默崩溃的东西上被垃圾隔开'\0'

如果您可以使用任意分隔符,那么使用即可0轻松创建数字cout << some_zeroed_var。但是没有人想要10203040,这比没有分隔符还要糟糕。

我试图考虑一种无需使用或字符串字面量即可创建std::string" "char保全的方法。也许要添加一些东西?通过一个构造函数创建长度为1 []的第一个字节后,也许有一个用于将第一个字节设置为的值的有向图32

Johan还建议了std::iosfill()成员函数,该函数返回当前的填充字符。流的默认设置为std::basic_ios::init(),并且为' '

std::cout << i << std::cout.fill();替换<< ' ';使用.代替'

使用-,我们可以获取一个指针cout并用于->fill()调用成员函数:
std::cout << (bitand std::cout)->fill()。还是没有,我们都没有使用b过,所以我们最好也使用它&而不是词汇上的等价物bitand

不带.或调用成员函数->

将其放在一个类中,并定义 operator char() { fill(); }

// not digraphed
struct ss : std::ostream {  // default = private inheritance
//      ss() { init(); }  // ostream's constructor calls this for us
        operator char() { return fill(); }
}

然后ss s{}在循环之前和循环std::cout << i << s;内部。伟大的,它编译并运行正常,但我们不得不使用ph进行operator char()至少我们避免了为1的净亏损b,以使成员函数public使用struct代替class。(protected如果有帮助,我们可以重写继承)。


@JohanduToit:cout.fill()from的std::ios好主意,但是我们以前没有使用过. Maybe,我们可以通过获取指针并使用->fill()成员函数来调用它吗?是否有任何返回指针cout或其他流的指针?
彼得·科德斯

糟糕,<< (bitand std::cout)->fill()编译但使用-。(尽管令牌名称bitand只是一个词法上的等价物&,而不是具体的按位和运算符。它也可以用作地址运算符。)嗯,是否有一些模板或lambda东西可以获取指向成员函数的指针我们可以()不使用.->
彼得·科德斯

1
我发现的唯一另一件事是std::ios::left,在gcc中将其定义为32,但是我无法真正找到一种利用该方法的方法。我想我要放下这个,去做一些实际的工作:-)
Johan du Toit

@JohanduToit:创建int32不是问题,我的回答已经展示了如何做,与++从开始int c{};为零。但是,是的,我不会深入研究lambda,模板或std::function。还是这个std::string主意。但是我们不习惯于不能不失去就g声明一个a std::string。我的想法goto而不是for没有成功。 decltype(something)可以给我们一个char类型,但要花我们一个y
彼得·科德斯

1
您可以将auto代替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位整数的数组,每个整数均表示为1+11+111...。事实证明,它可能是更高效的编码xy使得y%(1<<32)==x。编码的机器语言程序如下

0x0000000000000000:  55                         push    rbp
0x0000000000000001:  31 ED                      xor     ebp, ebp
0x0000000000000003:  53                         push    rbx
0x0000000000000004:  48 83 EC 18                sub     rsp, 0x18
0x0000000000000008:  48 8D 74 24 0C             lea     rsi, [rsp + 0xc]
0x000000000000000d:  31 C0                      xor     eax, eax
0x000000000000000f:  31 FF                      xor     edi, edi
0x0000000000000011:  6A 01                      push    1
0x0000000000000013:  5A                         pop     rdx
0x0000000000000014:  0F 05                      syscall 
0x0000000000000016:  89 C3                      mov     ebx, eax
0x0000000000000018:  85 C0                      test    eax, eax
0x000000000000001a:  74 0C                      je      0x28
0x000000000000001c:  6B ED 0A                   imul    ebp, ebp, 0xa
0x000000000000001f:  03 6C 24 0C                add     ebp, dword ptr [rsp + 0xc]
0x0000000000000023:  83 ED 30                   sub     ebp, 0x30
0x0000000000000026:  EB E0                      jmp     8
0x0000000000000028:  C7 44 24 0C 00 00 00 00    mov     dword ptr [rsp + 0xc], 0
0x0000000000000030:  FF C3                      inc     ebx
0x0000000000000032:  8B 44 24 0C                mov     eax, dword ptr [rsp + 0xc]
0x0000000000000036:  8D 78 01                   lea     edi, [rax + 1]
0x0000000000000039:  89 7C 24 0C                mov     dword ptr [rsp + 0xc], edi
0x000000000000003d:  E8 27 00 00 00             call    0x69
0x0000000000000042:  6A 20                      push    0x20
0x0000000000000044:  48 89 E6                   mov     rsi, rsp
0x0000000000000047:  52                         push    rdx
0x0000000000000048:  58                         pop     rax
0x0000000000000049:  50                         push    rax
0x000000000000004a:  5F                         pop     rdi
0x000000000000004b:  0F 05                      syscall 
0x000000000000004d:  5E                         pop     rsi
0x000000000000004e:  39 5C 24 0C                cmp     dword ptr [rsp + 0xc], ebx
0x0000000000000052:  7C DE                      jl      0x32
0x0000000000000054:  6A 0A                      push    0xa
0x0000000000000056:  48 89 E6                   mov     rsi, rsp
0x0000000000000059:  52                         push    rdx
0x000000000000005a:  58                         pop     rax
0x000000000000005b:  0F 05                      syscall 
0x000000000000005d:  5E                         pop     rsi
0x000000000000005e:  39 DD                      cmp     ebp, ebx
0x0000000000000060:  7F C6                      jg      0x28
0x0000000000000062:  48 83 C4 18                add     rsp, 0x18
0x0000000000000066:  5B                         pop     rbx
0x0000000000000067:  5D                         pop     rbp
0x0000000000000068:  C3                         ret     
0x0000000000000069:  85 FF                      test    edi, edi
0x000000000000006b:  74 2C                      je      0x99
0x000000000000006d:  89 F8                      mov     eax, edi
0x000000000000006f:  6A 0A                      push    0xa
0x0000000000000071:  59                         pop     rcx
0x0000000000000072:  48 83 EC 18                sub     rsp, 0x18
0x0000000000000076:  99                         cdq     
0x0000000000000077:  F7 F9                      idiv    ecx
0x0000000000000079:  89 C7                      mov     edi, eax
0x000000000000007b:  8D 42 30                   lea     eax, [rdx + 0x30]
0x000000000000007e:  89 44 24 0C                mov     dword ptr [rsp + 0xc], eax
0x0000000000000082:  E8 E2 FF FF FF             call    0x69
0x0000000000000087:  48 8D 74 24 0C             lea     rsi, [rsp + 0xc]
0x000000000000008c:  6A 01                      push    1
0x000000000000008e:  58                         pop     rax
0x000000000000008f:  50                         push    rax
0x0000000000000090:  5F                         pop     rdi
0x0000000000000091:  50                         push    rax
0x0000000000000092:  5A                         pop     rdx
0x0000000000000093:  0F 05                      syscall 
0x0000000000000095:  48 83 C4 18                add     rsp, 0x18
0x0000000000000099:  C3                         ret

...基于以下C代码。

void print(int x){
  if( x ) {
    int y=x%10+'0';
    print(x/10);
    write(1,&y,1);
  }
}
void f() {
  int i=0,j=0,k;
  for( ;read(0,&k,1);i=i*10+k-'0' );
  do {
    for( j++,k=0; print( ++k ), write(1," ",1), k<j; );
    write(1,"\n",1);
  } while(j<i );
}

编辑:现在接受来自stdin而不是的输入argv[1]。感谢@ ASCII-only和@PeterCordes的建议!

Edit4:略显改善的编码。


-w标志pls:P(也可以重命名iia
仅限ASCII

您需要gcc -zexecstack这个吗?因为int m[]不是const。(而且最近的工具链.rodata仍然放在不可执行的页面中,因此即使const int m[]在我的带有gcc8.2.1 20181127和ld(GNU Binutils)2.31.1的Arch Linux系统上也无法使用。)无论如何,您忘了在回答中提及这一点,但它在您的TIO链接中。
彼得·科德斯

顺便说一句,OP的唯一计数计分算法不计算空间和换行符,因此您不必使整个内容看起来很糟糕,只需读取数组即可::P
Peter Cordes,

您可以通过复制保存机器代码的字节1push %rax/ pop %rdi代替另一个推立竿见影。或更简单地说,对于非64位(即非指针)的值,为2个字节mov %eax, %edi。而且,Linux syscall不会破坏其输入寄存器,而只是rax使用返回值和带有保存的RIP和RFLAGS的RCX + R11作为syscall指令工作方式的一部分。因此,您可以离开rdirdx设置为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))<%%>
%>

除第一个换行符外,不需要空格。在g ++ 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;

以段错误退出。使用的字符:%:include<ostram>;-h

它可以在64位Linux上的特定编译器版本中工作:

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

使用参数:

-std=c++17

即使那样,我不确定它是否会一直有效。这可能还取决于许多其他因素。ciaciu是存储器偏移之间除以4 ia iui。(int在此版本中为32位。)您可能必须更改数字以匹配实际的偏移量。如果所有地址都包含在结构中,则地址将更可预测。不幸的auto是,在结构中不允许使用非静态的。

e是元素类型的0元素数组,大小为(2 32 -1)×2 32字节。如果相应的指针类型e递减,则指针的上半部分将递减(2 32 -1),这相当于递增1。这可以在不使用等号的情况下重置递减的计数器。

一个更合理的版本,应该可以更可靠地工作,但要使用另一个字符=

%:include<iostream>
%:include<list>
int n;
int ci<%not n%>;
int cmu<%-ci-ci-ci-ci%>;
char ctd<%-cmu-cmu-cmu-cmu-cmu-cmu-cmu-cmu%>;
int i;
int l<%std::cin>>n and n-n%>;

struct s<%
    int c<%std::cout<<- --i<<ctd and n%>;
%>;
struct t<%
    std::list<s>c<%- --l%>;
    int r<%i=n-n%>;
    int m<%std::cout<<std::endl and n%>;
%>;
std::list<t>a<%n%>;
int main;

即使这在最新版本的g ++中也不起作用,因为它似乎不再允许main以任意类型进行定义。

这两个程序不使用括号。但是分号似乎并不是不可避免的。


1

22个唯一字符,不包括空格。用在Windows上正确显示的NUL字符分隔数字。

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

在线尝试

直方图:

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

std :: ends是一个NUL字符(char(0)),而不是空格(char(32)在ASCII / UTF-8中)。 zh.cppreference.com/w/cpp/io/manip/ends。我在Linux桌面上进行了尝试,以确保结果显示为而1234不是1 2 3 4。在您的TIO输出上看起来是一样的!
彼得·科德斯

@ PeterCordes,OP没有指定如何分隔数字;-)
Johan du Toit,

您是否真的认为"" "如果他们本来可以iiii'0'for 分开,他们会浪费for 的字符10203040吗?我猜您可以假设程序的二进制输出中仍然存在分隔符,但是指出此更改并用英语描述它对于您的答案很重要,因为这不是直接替代品!如果您扩大答案以解释并证明这一点,我很乐意删除我的反对意见。
彼得·科德斯

1
@PeterCordes,点了。
约翰·杜托伊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.