StickStack编号


22

StickStack是一种非常简单的基于堆栈的编程语言,只有两条指令:

  • | 将堆栈长度推到堆栈上
  • -从堆栈中弹出前两个元素,然后将它们的差值推回(second topmost - topmost

语言细节

  • 程序开始时堆栈为空。
  • 所有指令从左到右依次执行。
  • 如果堆栈中的数字少于2,则该-指令是非法的。
  • 在执行结束时,堆栈应恰好包含一个数字

StickStack程序可以生成任何整数。例如:

|||--||-- generates the number 2 through the following stack states:

[]
[0]
[0, 1]
[0, 1, 2]
[0, -1]
[1]
[1, 1]
[1, 1, 2]
[1, -1]
[2]    

要评估您的StickStack代码,您可以使用此在线(CJam)评估程序。(感谢@Martin提供的代码。)

任务

您应该编写一个给定整数值作为输入输出的程序或函数,或者返回一个代表输出给定数字的StickStack程序的字符串。

计分

  • 您的主要得分是以下给定测试用例的StickStack程序总长度。分数越低越好。
  • 仅当您在所有测试用例上运行了程序并计算了分数时,您的提交才有效。
  • 您的次要(抢七)得分是生成程序或函数的长度。

输入测试用例

(每个数字都是不同的测试用例。)

-8607 -6615 -6439 -4596 -4195 -1285 -72 12 254 1331 3366 3956 5075 5518 5971 7184 7639 8630 9201 9730

您的程序应该适用于任何整数(数据类型可以处理),而不仅仅是给定的测试用例。测试编号的解决方案不应硬编码到您的程序中。如果不确定是否要进行硬编码,则将更改测试编号。


我建议添加一个“在合理的时间内运行”的条款,以防止暴力行为。

@Reticality暗示“仅当您在所有测试用例上运行程序时才有效”
edc65 2015年

Answers:


7

Python 2-5188

在时间上非常有效,并且(可能)是最佳解决方案。我观察到像

|||||-|||-|-|-|------ (25的最佳解决方案)

可以描述为

 0  |
-1  |                  
+2   |                 -
-3    |               -
+4     | |           -
-5      - |         -
+6         | | | | -
-7          - - - -

其中末尾的每个总值是(每个级别的值乘以'|的数量)的总和。因此,例如,上面有-1*1 + 2*1 - 3*1 + 4*2 - 5*1 + 6*4 = 25。使用这个我写了这个解决方案,它在很短的时间内产生了与其他答案相似的输出。

我相信这是最佳解决方案,因为我测试了每个可能的最佳高度(实际上我测试了很多不必要的高度),并且我很确定解决方案总是最多包含一层,最后一层除了上面还有两个“ |”(我可以请保证此值为正数,但不能100%确定负数)。

def solution(num):
    if num == 0:
        return '|'

    neg = num<0
    num = abs(num)
    high = int(num**0.5)

    def sub(high):
        counts = [1]*high
        total = num - (high+neg)/2

        if total%high == 0:
            counts[-1] += total/high
        else:
            counts[-1] += total/high
            if (total%high)%2==1 and not neg:
                counts[-1] += 1
                counts[-(total%high)-1] += 1
            elif (total%high)%2==0 and neg:
                counts[(total%high)-2] += 1
                counts[0] += 1
            else:
                counts[total%high-1] += 1

        string = ""
        for c in counts[::-1]:
            string = '|-'*(c-1)+'|'+string+'-'
        return '|'+string

    return min((sub(h) for h in range(2-neg,2*high+2,2)), key=lambda s: len(s))

这是我用来测试的代码

string = "-8607 -6615 -6439 -4596 -4195 -1285 -72 12 254 1331 3366 3956 5075 5518 5971 7184 7639 8630 9201 9730"
total = 0

def string_to_binary(string):
    total = 0
    for i,char in enumerate(string[::-1]):
        total += (char=='|')*(2**i)
    return total

def stickstack(bits,length):
    stack = []
    for i in range(length):
        d,bits = divmod(bits,2**(length-i-1))
        if d == 1:
            stack.append(len(stack))
        else:
            stack[-2] -= stack[-1]
            stack = stack[:-1]
    return stack

for num in string.split():
    s = solution(int(num))
    print '%s:' % num
    print s
    result = stickstack(string_to_binary(s),len(s))
    print 'Result: %s' % result
    print 'Length: %s' % len(s)
    total += len(s)
    print

print 'Total length: %s' % total

2
很好的解决方案!我认为分数是最佳的(基于我的蛮力计算),并且根据您的描述,我认为您的算法始终会提供最佳解决方案。
randomra'5

@randomra我认为很可能是这样,但是我只是被蛮力地逼到了大约+/- 100,所以我还没准备好说它一定是最好的,但是现在我考虑了一下,所以我看不到如何可以做得更好。
KSab 2015年

+1非常好。我该如何尝试?什么pyton?(我不是pythonist,只是在笔记本电脑上偶然安装了python 3.4)。
edc65

@ edc65我添加了代码进行测试;同样使用的是Python 2.7,所以诸如print语句之类的内容将不适用于Python 3
KSab,2015年

对于它的价值,我可以确认该结果是最佳的:我尝试了蛮力解决方案(确实是BFS),并验证了长达450的长度(并且仍在运行)。同样的结果。
edc65

12

爪哇,5208 5240 5306 6152

这是一个递归函数,其边缘更接近目标,具有在5以内(通常仅一步)之内的基本情况。

基本上,你可以(a*b)+(a/2)(a+b)*2一个简单的模式棍子。如果a为奇数,结果将为负,从而导致某些奇怪的逻辑。

2 31 -1大约需要一分钟,因此长度为185,367。但是,它几乎可以在所有测试用例中立即起作用。4*(sqrt|n|)平均得分。最长的单个测试用例是9730,这将导致397长的木棍堆栈:

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||-|||||||||||||||||||||-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|--------------------------------------------------------------------------------------------------|-

更新:

找到了一种简单的方法来添加/减去基本模式。回到领先(现在)!


带有线束和测试用例:

import static java.lang.Math.*;

public class StickStacker {

    public static void main(String[] args){
        StickStacker stacker = new StickStacker(); 
        int tests[] = {-8607,-6615,-6439,-4596,-4195,-1285,-72,12,254,1331,3366,3956,5075,5518,5971,7184,7639,8630,9201,9730};
        int sum = 0;
        for(int test : tests){
            String sticks = stacker.stickStack3(test);
            sum += sticks.length();
            System.out.println("In: " + test + "\t\tLength: " + sticks.length());
            System.out.println(sticks+"\n");
        }
        System.out.println("\n\nTotal: "+sum);          
    }

    String stickStack3(int n){return"|"+k(n);}
    String k(int n){
        String o="";
        int q=(int)sqrt(abs(n)),a,b,d=1,e=0,c=1<<30,
        z[]={232,170,42,10,2,0,12,210,52,844,212};
        a=n>0?q:-q;
        a-=n>0?a%2<1?0:1:a%2<0?0:-1;

        for(b=0;b<abs(a)+10;b++)
            if(abs(n-(a*b+a/2-(n>0?0:1)))<abs(a)&&abs(a)+b<c){
                    c=abs(a)+b;
                    d=a;e=b;
            }

        for(a=0;a++<e;)o+="-|";
        for(a=0;a++<abs(d);)o="|"+o+"-";

        c=n-(d*e+d/2-(n>0?0:1));
        if(c>0&&c<abs(d)){
            if(c%2==0)
                o=o.substring(0,c)+"-|"+o.substring(c);
            else
                o=o.substring(0,c+1)+"-|"+o.substring(c+1)+"|-";
            c=0;
        }else if(c<0&-c<abs(d)){
            if(c%2!=0)
                o=o.substring(0,-c)+"-|"+o.substring(-c);
            else
                o=o.substring(0,-c-1)+"-|"+o.substring(-c-1)+"|-";  
            c=0;
        }

        return n==0?"":n<6&&n>-6?
                Long.toBinaryString(z[n+5])
                .replaceAll("0","-")
                .replaceAll("1","|"):
                o+k(c);
    }
}

在出现平局的情况下,高尔夫会(更多)打高尔夫球。


您确定2 ^ 31的分数吗?我的2 ^ 30分数是131099,而2 ^ 31-1的分数是185369。
edc65

@ edc65我肯定打错了。我认为它似乎有点低...无论如何,感谢您的关注并提供了一些竞争。现在是时候看看我是否可以做得更好:)
Geobits 2015年

4

JavaScript(ES6)5296 6572

编辑正如我在解释中所说,我不擅长求解整数方程。我对b值的猜测不太好,因此我扩大了尝试的值范围。(哇)我现在正在领导。

编辑2个错误修复,结果相同。我有一个主意,但无法确定。

字节:〜460,非常打高尔夫球。它适用于32位整数,运行时间接近0。

代码是下面的F函数(隐藏在代码段中)。
运行代码片段进行测试(在FireFox中)。

说明

以正数开头。以“ base”开头(如果需要,请在CJam中尝试,允许使用空格)

| gives 0  
||| -- gives 1
||||| ---- gives 2
||||||| ------ gives 3 

摘要:1条,然后b * 2条,然后b * 2破折号

然后尝试添加一个或多个“-|” 在中间分裂。每增加一个固定增量,该增量是起始基数的两倍,并且可以重复多次。因此我们有一个公式,其中b = base,r =递增重复因子

v=b+r*2*b

b=1, r=0 to 3, inc=2
| || -- 1 
| || -| -- 3 
| || -| -| -- 5 
| || -| -| -| -- 7

b=3, r=0 to 3, inc=6
| |||||| ------ 3
| |||||| -| ------ 9
| |||||| -| -| ------ 15
| |||||| -| -| -| ------ 21

看到?addes的值迅速增加,每个加法仍然只有2个字符。基本增量每次给出4个以上的字符。

给定v和公式v = b + r * 2 * b,我们需要找到2个整数b和r。我不是这种方程式的专家,但是b = int sqrt(v / 2)是一个很好的起点。

然后我们有一个r和b,它们的值都接近v。我们通过重复递增(||-)或递减(|-)精确地达到v。

对负数遵循相同的推理,可惜公式相似但不相等。


1

JavaScript,398710

94个字节/字符的代码

我想出了一个解决方案!...然后阅读Sparr的答案,那是完全一样的。

想通了无论如何我都会发布它,因为js允许更少的字符。

这是代码的未缩小版本:

function p(a){
    s = "";
    if(a<=0){
        for(i=0; i<-2*a-1;i++)
            s="|"+s+"-";
        return "|"+s;
    }
    return "|"+p(0-a)+"-";
}

1
好吧,如果我们要打高尔夫球398710解决方案,那就继续比赛吧!有人会遇到cjam或pyth并击败我们两个人,但:(
Sparr

1

Python,398710(71字节)

我认为,最简单的解决方案。使用stickstack的4 * n(+/- 1)个字符表示n。

def s(n):return'|'*(n*2+1)+'-'*n*2 if n>=0 else'|'*(n*-2)+'-'*(n*-2-1)
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.