Số StickStack


22

StickStack là một ngôn ngữ lập trình dựa trên ngăn xếp rất đơn giản chỉ với hai hướng dẫn:

  • | đẩy chiều dài của ngăn xếp lên ngăn xếp
  • -bật hai phần tử hàng đầu từ ngăn xếp và đẩy lùi sự khác biệt của chúng ( second topmost - topmost)

Chi tiết ngôn ngữ

  • Ngăn xếp trống khi bắt đầu chương trình.
  • Tất cả các hướng dẫn được thực hiện tuần tự từ trái sang phải.
  • Nếu có ít hơn 2 số trên ngăn xếp thì -lệnh là bất hợp pháp.
  • Khi kết thúc thực hiện, ngăn xếp sẽ chứa chính xác một số .

Bất kỳ số nguyên nào cũng có thể được tạo bởi chương trình StickStack. Ví dụ:

|||--||-- 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]    

Để đánh giá mã StickStack của bạn, bạn có thể sử dụng trình đánh giá trực tuyến (CJam) này . (Cảm ơn @Martin về mã.)

Nhiệm vụ

Bạn nên viết một chương trình hoặc hàm cho số nguyên làm đầu ra đầu vào hoặc trả về một chuỗi đại diện cho chương trình StickStack xuất ra số đã cho.

Chấm điểm

  • Điểm chính của bạn là tổng chiều dài của các chương trình StickStack cho các trường hợp kiểm tra được đưa ra dưới đây. Điểm thấp hơn là tốt hơn.
  • Việc gửi của bạn chỉ có hiệu lực nếu bạn chạy chương trình của mình trên tất cả các trường hợp kiểm tra và tính điểm của bạn.
  • Điểm thứ cấp (tiebreaker) của bạn là độ dài của chương trình hoặc chức năng tạo của bạn.

Trường hợp kiểm tra đầu vào

(Mỗi số là một trường hợp thử nghiệm khác nhau.)

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

Chương trình của bạn nên hoạt động cho mọi số nguyên (loại dữ liệu của bạn có thể xử lý) không chỉ cho các trường hợp kiểm tra nhất định. Các giải pháp cho các số kiểm tra không nên được mã hóa cứng vào chương trình của bạn. Nếu có nghi ngờ về mã hóa cứng, các số kiểm tra sẽ được thay đổi.


Tôi đề nghị thêm một mệnh đề có nội dung "Và chạy trong một khoảng thời gian hợp lý" để ngăn chặn vũ phu.

@Ralityity ngụ ý "chỉ hợp lệ nếu bạn chạy chương trình của mình trên tất cả các trường hợp thử nghiệm"
edc65

Câu trả lời:


7

Trăn 2 - 5188

Khá hiệu quả về thời gian, và dường như (có thể) là giải pháp tối ưu. Tôi quan sát thấy rằng một mô hình như

|||||-|||-|-|-|------ (một giải pháp tối ưu cho 25)

có thể được phác họa như

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

trong đó mỗi tổng giá trị ở cuối là tổng của (mỗi giá trị của cấp nhân với số lượng '|'). Vì vậy, ví dụ ở trên, chúng tôi có -1*1 + 2*1 - 3*1 + 4*2 - 5*1 + 6*4 = 25. Sử dụng điều này tôi đã viết giải pháp này tạo ra đầu ra tương tự cho các câu trả lời khác, và trong một khoảng thời gian không đáng kể.

Tôi tin rằng đây là giải pháp tối ưu, vì tôi kiểm tra mọi chiều cao tối ưu có thể có (tôi thực sự kiểm tra nhiều hơn mức cần thiết) và tôi khá chắc chắn rằng giải pháp luôn liên quan đến nhiều nhất một lớp với hai 'bên cạnh cuối cùng (tôi có thể đảm bảo điều này cho các số dương nhưng không chắc chắn 100% về các tiêu cực).

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

Đây là mã tôi đã sử dụng để kiểm tra nó

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
Giải pháp tuyệt vời! Tôi tin rằng điểm số là tối ưu (dựa trên tính toán bruteforce của tôi) và dựa trên mô tả của bạn, tôi nghĩ rằng thuật toán của bạn luôn đưa ra các giải pháp tốt nhất.
ngẫu nhiên

@randomra Tôi nghĩ có khả năng là như vậy, nhưng tôi chỉ bị buộc phải lên tới khoảng +/- 100 vì vậy tôi chưa sẵn sàng để nói rằng nó nhất thiết phải là tốt nhất, nhưng bây giờ tôi nghĩ về nó tôi không thể thấy làm thế nào nó có thể được thực hiện tốt hơn bất kỳ.
KSab

+1 rất đẹp. Làm thế nào tôi có thể thử nó? Kim tự tháp gì? (Tôi không phải là một con trăn, tôi vừa vô tình cài đặt một con trăn 3,4 trên máy tính xách tay của mình).
edc65

@ edc65 Tôi đã thêm mã để kiểm tra nó; Ngoài ra, đây là sử dụng Python 2.7, vì vậy những thứ như câu lệnh in sẽ không hoạt động với Python 3
KSab

Đối với giá trị của nó, tôi có thể xác nhận rằng kết quả này là tối ưu: tôi đã thử một giải pháp vũ phu (thực sự là BFS), xác minh tới độ dài 450 (và vẫn đang chạy). Kết quả tương tự của bạn.
edc65

12

Java, 5208 5240 5306 6152

Đây là một hàm đệ quy cạnh gần hơn với mục tiêu, với các trường hợp cơ sở khi nó nằm trong phạm vi 5 (thường chỉ là một bước).

Về cơ bản, bạn có thể nhận được (a*b)+(a/2)cho (a+b)*2gậy với một mô hình đơn giản. Nếu alà số lẻ, kết quả sẽ là âm, do đó dẫn đến một số logic kỳ lạ.

Điều này mất một phút hoặc lâu hơn cho 2 31 -1, với độ dài 185.367 là kết quả. Nó hoạt động gần như ngay lập tức cho tất cả các trường hợp thử nghiệm, mặc dù. Nó điểm 4*(sqrt|n|)trung bình. Trường hợp thử nghiệm riêng lẻ dài nhất là 9730, kết quả là một ngăn xếp có chiều dài 397:

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

Cập nhật:

Tìm thấy một cách ngắn hơn để thêm / bớt từ mẫu cơ bản. Trở lại vị trí dẫn đầu (bây giờ)!


Với một khai thác và trường hợp thử nghiệm:

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

Sẽ golf (nhiều hơn) trong trường hợp không chắc chắn của một tie chính xác.


Bạn có chắc chắn về điểm số của bạn cho 2 ^ 31? Điểm của tôi cho 2 ^ 30 là 131099 và 185369 cho 2 ^ 31-1.
edc65

@ edc65 Tôi phải gõ sai. Tôi nghĩ rằng nó có vẻ hơi thấp ... Dù sao, cảm ơn vì đã chú ý và đưa ra một số cạnh tranh. Bây giờ là lúc để xem liệu tôi có thể làm tốt hơn không :)
Geobits

4

JavaScript (ES6) 5296 6572

Chỉnh sửa Như tôi đã nói trong phần giải thích của mình, tôi không giỏi trong việc giải phương trình số nguyên. Tôi đoán giá trị b không tốt lắm, vì vậy tôi đã mở rộng phạm vi giá trị để thử. Và (wow) Bây giờ tôi đang dẫn đầu.

Chỉnh sửa 2 Sửa lỗi, kết quả tương tự. Tôi có một ý tưởng, nhưng không thể đóng nó xuống.

Byte: ~ 460, khá golf. Nó hoạt động trên số nguyên 32 bit, thời gian chạy gần 0.

Mã này là hàm F (ẩn trong đoạn trích) bên dưới.
Chạy đoạn trích để kiểm tra (trong FireFox).

Giải trình

Số dương, để bắt đầu với. Bắt đầu với một "cơ sở" (thử trong CJam nếu bạn thích, không gian cho phép)

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

Tóm tắt: 1 thanh, sau đó b * 2 gậy, sau đó b * 2 dấu gạch ngang

Sau đó thử thêm một hoặc nhiều '- |' ở giữa chia. Mỗi cái thêm một gia số cố định là hai lần cơ sở bắt đầu và có thể được lặp lại nhiều lần. Vì vậy, chúng tôi có một công thức, với b = cơ sở và r = hệ số lặp lại tăng

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

Xem? Giá trị gia tăng tăng nhanh và mỗi lần thêm vẫn chỉ là 2 ký tự. Gia số cơ sở cho thêm 4 ký tự mỗi lần.

Cho v và công thức của chúng tôi v = b + r * 2 * b, chúng ta cần tìm 2 int b và r. Tôi không phải là chuyên gia về loại phương trình này, nhưng b = int sqrt (v / 2) là một phỏng đoán khởi đầu tốt.

Sau đó, chúng ta có một r và b cùng đưa ra một giá trị gần v. Chúng ta đạt v chính xác với mức tăng lặp lại (|| -) hoặc giảm (| -).

Thực hiện theo cùng một lý do cho các số âm, than ôi công thức tương tự nhưng không bằng nhau.


1

JavaScript, 398710

94 byte / ký tự mã

Tôi đã đưa ra một giải pháp! ... Và sau đó đọc câu trả lời của Sparr và nó hoàn toàn giống nhau.

Hình tôi sẽ đăng nó bằng mọi cách, vì js cho phép ký tự ít hơn một chút.

Đây là phiên bản chưa hoàn thành của mã:

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

1
ok, nếu chúng ta đang chơi các giải pháp 398710, hãy chơi! ai đó sẽ đến với cjam hoặc pyth và đánh bại cả hai chúng tôi, mặc dù :(
Sparr

1

Python, 398710 (71 byte)

Giải pháp đơn giản nhất có thể, tôi nghĩ. Sử dụng 4 * n (+/- 1) ký tự của stickstack để thể hiện n.

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