Номери 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 для наведених нижче тестових випадків. Нижча оцінка - краща.
  • Ваше подання дійсне лише в тому випадку, якщо ви запустили свою програму на всі тестові випадки та підрахували ваш результат.
  • Ваш вторинний (tiebreaker) бал - це тривалість вашої генеруючої програми чи функції.

Вхідні тестові справи

(Кожне число є різним тестом.)

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

Ваша програма повинна працювати для будь-яких цілих чисел (з якими може працювати ваш тип даних), а не лише для наведених тестових випадків. Рішення тестових номерів не повинні бути жорстко закодовані у вашій програмі. Якщо виникнуть сумніви в жорсткому кодуванні, тестові номери будуть змінені.


Я пропоную додати пункт, який говорить "І працює протягом розумного часу", щоб запобігти грубій силі.

@Reticality, що мається на увазі в "дійсно, лише якщо ви запускали свою програму на всіх тестових випадках"
edc65

Відповіді:


7

Пітон 2 - 5188

Досить ефективний час, і, здається, є оптимальним рішенням. Я помітив, що такий зразок, як

|||||-|||-|-|-|------ (оптимальне рішення для 25)

можна виділити як

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

де кожне сумарне значення в кінці є сумою (значення кожного рівня, кратне кількості '|' s). Так, наприклад, у нас вище -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

@randomra Я думаю, що це, мабуть, так, але я був змушений приблизно до +/- 100, тому я не був готовий сказати, що це обов'язково найкраще, але тепер, коли я думаю про це, я не бачу як це можна зробити краще.
KSab

+1 дуже приємно. Як я можу спробувати? Який пітон? (Я не пітоніст, я просто випадково встановив python 3.4 на своєму ноутбуці).
edc65

@ edc65 Я додав код, щоб перевірити його; також для цього використовується Python 2.7, тому такі речі, як заяви про друк, не працюватимуть із Python 3
KSab

Для чого це варто, я можу підтвердити, що цей результат є оптимальним: я спробував жорстоке рішення (дійсно 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, а 185369 - 2 ^ 31-1.
edc65

@ edc65 Я, мабуть, ввів його неправильно. Я подумав, що це здається трохи низьким ... У будь-якому випадку, дякую за те, що помітили і дали певну конкуренцію. Тепер прийшов час побачити, чи можу я зробити краще :)
Геобіт

4

JavaScript (ES6) 5296 6572

Редагувати Як я вже говорив у своєму поясненні, я не вмію розв’язувати цілі рівняння. Моя здогадка значення b не була такою вдалою, тому я розширив діапазон значень, щоб спробувати. І (вау) я зараз веду.

Редагуйте 2 виправлення помилок, однакові результати. Я маю ідею, але не можу її знести.

Байтів: ~ 460, досить гольф. Він працює на 32 бітових цілих числах, час роботи біля 0.

Код - функція F (прихована у фрагменті) нижче.
Запустіть фрагмент для тестування (у FireFox).

Пояснення

Позитивні числа, для початку. Почніть з «бази» (спробуйте в CJam, якщо вам подобається, пробіли дозволені)

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

Короткий зміст: 1 паличка, потім b * 2 палички, потім b * 2 тире

Потім спробуйте додати одну або декілька '- |' в середній розкол. Кожен додає фіксований приріст, який є в два рази стартовою базою і може повторюватися багато разів. Отже, у нас формула з b = основа та 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

Побачити? Значення добавок швидко збільшується, і кожне додавання все ще складає лише 2 знаки. Приріст основи дає кожен раз ще 4 символи.

Враховуючи v і нашу формулу v = b + r * 2 * b, нам потрібно знайти 2 ints b і r. Я не знавець такого рівняння, але b = int sqrt (v / 2) - хороша вихідна здогадка.

Тоді у нас є r і b, які разом дають значення біля v. Ми досягаємо v точно з повторним збільшенням (|| -) або декрементом (| -).

Дотримуйтесь тих же міркувань щодо від'ємних чисел, на жаль, формула схожа, але не однакова.


1

JavaScript, 398710

94 байти / символи коду

Я придумав рішення! ... а потім прочитайте відповідь Спарра, і це було точно так само.

Я подумав, що я все-таки опублікую його, оскільки 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 і побив нас обох :(
Спарр

1

Python, 398710 (71 байт)

Я думаю, найпростіше можливе рішення. Для позначення n використовується 4 * n (+/- 1) символів stickstack.

def s(n):return'|'*(n*2+1)+'-'*n*2 if n>=0 else'|'*(n*-2)+'-'*(n*-2-1)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.