Nomor StickStack

22

StickStack adalah bahasa pemrograman berbasis stack yang sangat sederhana dengan hanya dua instruksi:

  • | mendorong panjang tumpukan ke tumpukan
  • -muncul dua elemen teratas dari tumpukan dan mendorong kembali perbedaannya ( second topmost - topmost)

Detail bahasa

  • Tumpukan kosong di awal program.
  • Semua instruksi dijalankan secara berurutan dari kiri ke kanan.
  • Jika ada kurang dari 2 angka di tumpukan, -instruksi tersebut ilegal.
  • Pada akhir eksekusi stack harus berisi tepat satu nomor .

Bilangan bulat apa pun dapat dihasilkan oleh program StickStack. Sebagai contoh:

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

Untuk mengevaluasi kode StickStack Anda, Anda dapat menggunakan evaluator online (CJam) ini . (Terima kasih atas @Martin untuk kodenya.)

Tugas

Anda harus menulis program atau fungsi yang memberikan angka integer sebagai input output atau mengembalikan string yang mewakili program StickStack yang menampilkan nomor yang diberikan.

Mencetak gol

  • Skor utama Anda adalah total panjang program StickStack untuk kasus uji yang diberikan di bawah ini. Skor yang lebih rendah lebih baik.
  • Kiriman Anda hanya valid jika Anda menjalankan program pada semua kasus uji dan menghitung skor Anda.
  • Skor sekunder (tiebreak) Anda adalah panjang dari program atau fungsi pembangkit Anda.

Masukkan kasus uji

(Setiap angka adalah test case yang berbeda.)

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

Program Anda harus bekerja untuk bilangan bulat apa pun (yang tipe data Anda bisa tangani) tidak hanya untuk kasus uji yang diberikan. Solusi untuk nomor tes tidak boleh di-hardcode ke dalam program Anda. Jika ada keraguan tentang hardcoding nomor tes akan diubah.

randomra
sumber
Saya sarankan menambahkan klausa yang mengatakan "Dan berjalan dalam jumlah waktu yang wajar" untuk mencegah kekerasan.
@Reticality yang tersirat dalam "valid hanya jika Anda menjalankan program Anda pada semua kasus uji"
edc65

Jawaban:

7

Python 2 - 5188

Cukup hemat waktu, dan tampaknya (mungkin) solusi optimal. Saya mengamati bahwa pola seperti

|||||-|||-|-|-|------ (solusi optimal untuk 25)

dapat digambarkan sebagai

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

di mana masing-masing nilai total pada akhir adalah jumlah dari (nilai setiap tingkat dikalikan jumlah '|' s). Jadi misalnya di atas, sudah kita miliki -1*1 + 2*1 - 3*1 + 4*2 - 5*1 + 6*4 = 25. Dengan menggunakan ini saya menulis solusi ini yang menghasilkan output serupa dengan jawaban lain, dan dalam jumlah waktu yang sepele.

Saya percaya ini adalah solusi optimal, karena saya menguji setiap ketinggian optimal yang mungkin (saya benar-benar menguji lebih dari yang diperlukan) dan saya cukup yakin solusinya selalu melibatkan paling banyak satu lapisan dengan dua 's selain yang terakhir (saya bisa garansi ini untuk angka positif tetapi tidak 100% yakin tentang negatif).

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

Ini kode yang saya gunakan untuk mengujinya

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
KSab
sumber
2
Solusi hebat! Saya percaya skornya optimal (berdasarkan perhitungan bruteforce saya) dan berdasarkan deskripsi Anda, saya pikir algoritma Anda selalu memberikan solusi terbaik.
randomra
@randomra Saya pikir sepertinya memang begitu, tapi saya hanya dipaksa sampai +/- jadi saya tidak siap untuk mengatakan bahwa itu adalah yang terbaik, tapi sekarang saya berpikir tentang itu saya tidak bisa melihat bagaimana itu bisa dilakukan dengan lebih baik.
KSab
+1 sangat bagus. Bagaimana saya bisa mencobanya? Apa pyton? (Saya bukan seorang pythonist, saya baru saja menginstal python 3.4 yang terinstal di laptop saya).
edc65
@ edc65 Saya menambahkan kode untuk mengujinya; juga ini menggunakan Python 2.7, jadi hal-hal seperti pernyataan cetak tidak akan berfungsi dengan Python 3
KSab
Untuk apa nilainya, saya dapat mengonfirmasi bahwa hasil ini optimal: saya mencoba solusi brute force (memang BFS), memverifikasi hingga panjang 450 (dan masih berjalan). Hasil yang sama milik Anda.
edc65
12

Jawa, 5208 5240 5306 6152

Ini adalah fungsi rekursif yang tepi lebih dekat ke target, dengan kasus dasar ketika sampai dalam 5 (yang sering hanya satu langkah).

Pada dasarnya, Anda bisa mendapatkan (a*b)+(a/2)untuk (a+b)*2tongkat dengan pola sederhana. Jika aaneh, hasilnya akan negatif, sehingga mengarah ke beberapa logika aneh.

Ini membutuhkan satu menit atau lebih untuk 2 31 -1, dengan panjang 185.367 sebagai hasilnya. Ini bekerja hampir seketika untuk semua kasus uji. Skornya 4*(sqrt|n|)rata-rata. Kasing uji individual terpanjang adalah 9730, yang menghasilkan tumpukan stick sepanjang 397:

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

Memperbarui:

Menemukan cara yang lebih pendek untuk menambah / mengurangi dari pola dasar. Kembali memimpin (untuk sekarang)!


Dengan harness dan test case:

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

Akan bermain golf (lebih banyak) jika tidak ada pertandingan yang pasti.

Geobit
sumber
Apakah Anda yakin dengan skor Anda untuk 2 ^ 31? Skor saya untuk 2 ^ 30 adalah 131099, dan 185369 untuk 2 ^ 31-1.
edc65
@ edc65 Saya salah mengetik. Saya pikir itu tampak agak rendah ... Pokoknya, terima kasih telah memperhatikan, dan memberikan beberapa kompetisi. Sekarang saatnya untuk melihat apakah saya bisa melakukan yang lebih baik :)
Geobits
4

JavaScript (ES6) 5296 6572

Sunting Seperti yang saya katakan dalam penjelasan saya, saya tidak pandai memecahkan persamaan bilangan bulat. Tebakan saya tentang nilai b tidak begitu baik, jadi saya telah memperluas rentang nilai untuk dicoba. Dan (wow) saya memimpin sekarang.

Edit 2 Bug fix, hasil yang sama. Saya punya ide, tapi tidak bisa memahaminya.

Bytes: ~ 460, cukup golf. Ini bekerja pada bilangan bulat 32 bit, jalankan waktu dekat 0.

Kode tersebut adalah fungsi F (tersembunyi dalam cuplikan) di bawah ini.
Jalankan cuplikan untuk menguji (di FireFox).

Penjelasan

Angka-angka positif, untuk memulai. Mulai dengan "basis" (coba di CJam jika Anda suka, spasi diperbolehkan)

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

Ringkasan: 1 stick, lalu b * 2 stick, lalu b * 2 strip

Kemudian coba tambahkan satu atau lebih '- |' di tengah split. Masing-masing menambahkan kenaikan tetap yang merupakan dua kali basis awal dan dapat diulang berkali-kali. Jadi kami memiliki rumus, dengan b = basis dan r = faktor pengulangan kenaikan

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

Lihat? Nilai tambah bertambah dengan cepat dan setiap penambahan hanya 2 karakter. Peningkatan basis memberi 4 karakter lagi setiap kali.

Diberikan v dan rumus kita v = b + r * 2 * b, kita perlu menemukan 2 ints b dan r. Saya bukan ahli dalam persamaan semacam ini, tetapi b = int sqrt (v / 2) adalah tebakan awal yang bagus.

Kemudian kita memiliki r dan b yang bersama-sama memberikan nilai dekat v. Kita mencapai v persis dengan kenaikan berulang (|| -) atau penurunan (| -).

Ikuti alasan yang sama untuk angka negatif, sayangnya rumusnya sama tetapi tidak sama.

edc65
sumber
1

JavaScript, 398710

94 byte / karakter kode

Saya datang dengan solusi! ... dan kemudian baca jawaban Sparr dan itu persis sama.

Kupikir aku akan mempostingnya, karena js memungkinkan untuk karakter yang sedikit lebih sedikit.

Berikut adalah versi kode yang tidak dijinakkan:

function p(a){
    s = "";
    if(a<=0){
        for(i=0; i<-2*a-1;i++)
            s="|"+s+"-";
        return "|"+s;
    }
    return "|"+p(0-a)+"-";
}
Nate Young
sumber
1
ok, jika kita bermain golf solusi 398710, mainkan! seseorang akan datang dengan cjam atau pyth dan mengalahkan kami berdua, meskipun :(
Sparr
1

Python, 398710 (71 byte)

Solusi yang paling sederhana, saya pikir. Menggunakan 4 * n (+/- 1) karakter stickstack untuk mewakili n.

def s(n):return'|'*(n*2+1)+'-'*n*2 if n>=0 else'|'*(n*-2)+'-'*(n*-2-1)
Sparr
sumber