Виразіть число лише з 0-9 та чотирма операціями


14

Пояснення

Befunge - двовимірна програма, яка використовує стеки .

Це означає, що робити 5 + 6, ви пишете 56+, тобто:

56+
5    push 5 into stack
 6   push 6 into stack
  +  pop the first two items in the stack and add them up, and push the result into stack

(to those of you who do not know stacks, "push" just means add and "pop" just means take off)

Однак, як зауважили ваші розумні, ми не можемо натиснути номер 56прямо в стек.

Щоб зробити це, ми повинні написати 78*замість цього, який примножує 7і 8і штовхає продукт в стек.

Деталі

На розсуд програміста можна вводити будь-який формат, тобто може бути STDIN чи ні.

Вхідним числом буде додатне ціле (без бонусу за включення 0чи від’ємних цілих чисел).

Виведенням буде рядок, що складається лише з цих символів: 0123456789+-*/(я б не використовував %модуль.)

Мета - знайти найкоротший рядок, який може представляти вхід, використовуючи описаний вище формат.

Наприклад, якщо вхід є 123, то вихід буде 67*99*+. Вихід слід оцінювати зліва направо.

Якщо є більш ніж один прийнятний вихід (наприклад 99*67*+, також прийнятний), будь-який з них може бути надрукований (жодного бонусу за друк усіх).

Подальше пояснення

Якщо ви все ще не розумієте, як 67*99*+оцінюється 123, ось детальне пояснення.

stack    |operation|explanation
          67*99*+
[6]       6         push 6 to stack
[6,7]      7        push 7 to stack
[42]        *       pop two from stack and multiply, then put result to stack
[42,9]       9      push 9 to stack
[42,9,9]      9     push 9 to stack
[42,81]        *    pop two from stack and multiply, then put result to stack
[123]           +   pop two from stack and add, then put result to stack

TL; DR

Програмі необхідно знайти найкоротший рядок, який може представляти вхід (число), використовуючи вказаний вище формат.

Примітки

Це завдання з , тому виграє найкоротший код у байтах.

Однозначність

На розсуд програміста -може бути x-yабо y-x. Однак вибір повинен бути узгодженим у межах рішення. Так само і для /.

Зразок програми

Луа, 1862 байт ( спробуйте в Інтернеті )

Оскільки я автор, я взагалі не буду гольфувати.

Пояснення:

This uses the depth-first search method.

Більше про перший глибинний пошук: тут .

Програма:

local input = (...) or 81

local function div(a,b)
    if b == 0 then
        return "error"
    end
    local result = a/b
    if result > 0 then
        return math.floor(result)
    else
        return math.ceil(result)
    end
end

local function eval(expr)
    local stack = {}
    for i=1,#expr do
        local c = expr:sub(i,i)
        if c:match('[0-9]') then
            table.insert(stack, tonumber(c))
        else
            local a = table.remove(stack)
            local b = table.remove(stack)
            if a and b then
                if c == '+' then
                    table.insert(stack, a+b)
                elseif c == '-' then
                    table.insert(stack, b-a)
                elseif c == '*' then
                    table.insert(stack, a*b)
                elseif c == '/' then
                    local test = div(b,a)
                    if test == "error" then
                        return -1
                    else
                        table.insert(stack, a+b)
                    end
                end
            else
                return -1
            end
        end
    end
    return table.remove(stack) or -1
end

local samples, temp = {""}, {}

while true do
    temp = {}
    for i=1,#samples do
        local s = samples[i]
        table.insert(temp, s..'0')
        table.insert(temp, s..'1')
        table.insert(temp, s..'2')
        table.insert(temp, s..'3')
        table.insert(temp, s..'4')
        table.insert(temp, s..'5')
        table.insert(temp, s..'6')
        table.insert(temp, s..'7')
        table.insert(temp, s..'8')
        table.insert(temp, s..'9')
        table.insert(temp, s..'+')
        table.insert(temp, s..'-')
        table.insert(temp, s..'*')
        table.insert(temp, s..'/')
    end
    for i=1,#temp do
        if input == eval(temp[i]) then
            print(temp[i])
            return
        end
    end
    samples = temp
end

Бонус

Торт для вас, якщо ви використовуєте Befunge (або будь-який його варіант) для написання коду.


3
Визначивши відповідь, може бути важко, якщо вона завжди створює найрідніший рядок. Однією з ідей було б генерувати великий набір сказуючих 30--50 чисел і оцінювати за сумою всієї довжини вихідного рядка. Однак я не впевнений, як поєднувати цей рахунок із довжиною коду
Луїс Мендо

4
Підмножина цього .
Аддісон Кримп

2
Копіюючи свої думки з чату : "Я думав про це, але я стверджую, що підмножина робить речі набагато простішими, тому що 1) немає шістнадцяткових, 2) не плаває, 3) немає дублювання і 4) тільки позитивних"
Sp3000

1
@CoolestVeto цей досить інший, щоб визнати недійсними старі відповіді.
Rɪᴋᴇʀ

1
@CoolestVeto Я думаю, що інший виклик слід закрити як дублікат цього.
mbomb007

Відповіді:


4

Python 2, 278 байт

Моє найкраще рішення, яке щоразу дає найкоротшу відповідь. (але дуже повільно)

def e(c):
 s=[];x,y=s.append,s.pop
 while c:
  d,c=c[0],c[1:]
  if"/"<d<":":x(d)
  else:a,b=y(),y();x(str(eval(b+d+a)))
 return int(y())
def g(v):
 s="0123456789+-*";t=list(s)
 while 1:
  for x in t:
   try:
    if e(x)==v:return x
   except:0
  t=[x+y for x in t for y in s]

Python 2, 437 байт

Це рішення довше, але дуже швидке (не груба сила). І я впевнений, що це завжди повертає найкоротший можливий результат.

r=range;l=len;a=input()
def f(n):
 if n<=9:return str(n)
 for d in r(9,1,-1):
  if n%d==0:return f(n/d)+"%d*"%d
 h=sum(map(int,list(str(n))))%9
 return f(n-h)+"%d+"%h
m={x:f(x) for x in r(a*9)}
for b in m:
 if a-b in m and l(m[b])+l(m[a-b])+1<l(m[a]):m[a]=m[a-b]+m[b]+"+"
 if a+b in m and l(m[b])+l(m[a+b])+1<l(m[a]):m[a]=m[a+b]+m[b]+"-"
 if b!=0 and a%b==0 and a/b in m and l(m[b])+l(m[a/b])+1<l(m[a]):m[a]=m[a/b]+m[b]+"*"
print m[a]

2
Ласкаво просимо до PPCG ! Сподіваюсь, тут вам буде чудово провести час.
Leaky Nun

1
@pbochinak Я думаю, що я, можливо, знайшов дійсну. f(6551)повертає 25*8*9*7+9*8+(13 символів), а 9999***52*-(11 символів) - кращий. Перевірено моєю власною evalфункцією вище (у питанні).
Лина монашка

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

1
ваш час може бути простоwhile c:
Вересень

Ви можете використовувати ;для розділення призначень змінним (що зберігає байти в відступних блоках), підказки ven, позбутися пробілу між символом і будь-чим іншим, і tможна перейти.
CalculatorFeline

4

Perl, 134 133 132 128 байт

Включає +5 для -Xlp(2 додаткові, оскільки код містить ')

Виконати з цільовим номером на STDIN:

perl -Xlp befour.pl <<< 123

befour.pl:

@1{1..9}=1..9;$.+=2,map{for$a(%1){"+-*/"=~s%.%"\$1{\$-=$a$&$_/'$1{$a}$1{$_}$&'=~/^.{$.}\$/}||=\$&"%eegr}}%1until$\=$1{$_}}{

Він не має штучних обмежень і є концептуально дещо ефективним, але має жахливі часи роботи, хоча я приніс у жертву кілька байт, щоб прискорити його. Генерування рішення довжиною 11 (наприклад, цільовий номер 6551) займає у моїй системі близько 5 годин.

Пожертвуючи ще 7 байтами, швидкість стає дещо переносимішою.

@1{1..9}=1..9;$.+=2,map{for$a(@a){"+-*/"=~s%.%"\$1{\$-=$a$&$_/'$1{$a}$1{$_}$&'=~/^.{$.}\$/}||=\$&"%eegr}}@a=keys%1until$\=$1{$_}}{

17 хвилин для розчину довжиною 11, приблизно 5 годин для тривалості 13 розчину. Перше число, яке потребує довжини 15, - 16622, що займає близько 2 днів. Перше число, яке потребує довжини 17, - 73319.

Зауважте, що він передбачає, що ділення повертає ціле число, обрізаючи його до 0 (за специфікацією befunge 93)


Що роблять знаки долара? (Я взагалі не говорю про Перл)
Лина монахиня

1
@KennyLau $отримує доступ до скалярного значення. Де на більшості мов ви писали a=4, perl використовуватиме $a=4. Але також використовується для скалярного доступу до складніших змінних. Напр. $a{$b}З хеша (карта, словник) виводиться %aскалярне значення$b
Тон Євангелія

2

C, 550 545 байт

#define L strlen
#define y strcpy
#define t strcat
char c[9999][99];i=1,k=3;main(j){for(;i<10;i++)*c[i]=i+'0';for(;k--;){
for(i=1;i<9999;i++)for(j=1;j<=i;j++)*c[i]&&*c[j]&&(i+j>9998||*c[i+j]&&
L(c[i+j])<L(c[i])+L(c[j])+2||t(t(y(c[i+j],c[i]),c[j]),"+"),
i*j>9998||*c[i*j]&&L(c[i*j])<L(c[i])+L(c[j])+2||t(t(y(c[i*j],c[i]),c[j]),"*"));
for(i=9999;--i;)for(j=i;--j;)*c[i]&&*c[j]&&(*c[i/j]&&
L(c[i/j])<L(c[i])+L(c[j])+2||t(t(y(c[i/j],c[i]),c[j]),"/"),
*c[i-j]&&L(c[i-j])<L(c[i])+L(c[j])+2||t(t(y(c[i-j],c[i]),c[j]),"-"));}
scanf("%d",&i);printf("%s",c[i]);}

550 545 байт після видалення зайвих нових рядків (усі, крім трьох нових рядків після директив попередньої обробки).

@Kenny Lau - Він може отримати як вхід ціле число між 1 і 9998, але я думаю, що діапазон вводу, для якого обчислюється оптимальне рішення, менший, ніж 9998. З іншого боку, обидва діапазони можна розширити, якщо пам'ять дозволяє це.

Програма не може натиснути на стек будь-якого числа, що перевищує 9998. (9998 можна змінити.) Я запускав програму в іншій версії, повторюючи зовнішній цикл (той, який має k), до тих пір, поки не відбудеться покращення для будь-якого числа між 1 і 9998 (як в алгоритмі Діккстри). Після трьох ітерацій поліпшення не відбувається. Отже, щоб зберегти байти, я жорстко кодував k = 3.

Для розширення діапазону необхідні дві речі - модифікація констант 9999 і 9998, запуск її зі змінною кількістю ітерацій над петлею зовнішньої плати протягом тих пір, поки не відбудеться поліпшення, щоб побачити, скільки часу потрібно, поки не відбудеться поліпшення, тоді змінити константу k = 3 на це значення.


Ласкаво просимо до PPCG ! Сподіваюсь, тут вам буде чудово провести час.
Leaky Nun

Це прекрасно проходить мій тест 6551. Який ефективний діапазон цієї програми?
Лина монашка

Я вважаю, що це 9999. Чи можете ви, будь ласка, додати цю інформацію до свого рішення?
Leaky Nun

Він повинен бути 9998. Крім того , ви можете з'їсти деякі байти ініціалізації i, jі kдо того main().
Leaky Nun

1
@Kenny Lau - Дякую за редагування Щодо розширення діапазону, я помітив, що насправді потрібно трохи більше для розширення діапазону. Я включив цю інформацію у відповідь.
mIllIbyte

2

Python 2, 284 байт

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

def f(v):
 i,z=0,'+-*/'
 while 1:
  s=('%x'%i).translate(__import__('string').maketrans('abcd',z),'ef');t=s;q=[];a,p=q.append,q.pop;i+=1
  try:
   while t:
    o,t=t[0],t[1:]
    if o in z:n,m=p(),p();a(eval(`m`+o+`n`))
    else:a(int(o))
   if p()==v and not q:return s
  except:pass

Алгоритм:

  • Починати з i = 0
  • Візьміть рядок , що представляє шістнадцяткове значення i, і замінити abcdз +-*/відповідно, і видалітьef
  • Спроба обробити рядок як нотацію постфікса (RPN)
  • Якщо успіх, а результат відповідає вхідному значенню, поверніть використаний рядок.
  • В іншому випадку зросте iі повторіть спробу.

"[t] завжди відповідає [...] деяким значенням" Ви перевірили це? Які значення?
Leaky Nun

@KennyLau я просто написав тест , який розважливий f(i)з 0 <= i <= 6551(захопити 6551значення , яке використовується для анулювання @pbochniak «s початкового уявлення). Зараз він працює лише кілька хвилин, і ось останній результат випробування: 91 : 49+7* 3.020 s (total 108.174 s, worst 89: 5.827 s) Оновлення - воно щойно закінчилося значенням 92: 92 : 149+7*+ 258.761 s (total 366.935 s, worst 92: 258.761 s)
Кен 'Джої' Мошер

@KennyLau: Тест працює понад годину, і лише до значення 113... дивіться повний тестовий результат тут (пастбін), якщо вас цікавить ...
Кен 'Джоуї' Мошер

2

Python 2, 182 байт

n=input()
L=[[[],""]]
while 1:
 s,c=L.pop(0);L+=[[s+[i],c+`i`]for i in range(10)]+(s[1:]and[[s[:-2]+[eval(`s[-2]`+o+`s[-1]`)],c+o]for o in"/+-*"[s[-1]==0:]])
 if[n]==s[-1:]:print c;E

Так непристойно повільно, я залишив його протягом години на вході, 221і він досі не припинився. Велика повільність полягає в тому, що я використовую список як чергу для першого пошуку в широті, і.pop(0) також O(n)для списків.

L це просто черга, що містить (stack, code to reach stack) пари. На кожному кроці цифри завжди додаються, і оператори виконуються, якщо стек має щонайменше два елементи. Ділення виконується лише в тому випадку, якщо останній елемент не дорівнює 0, хоча я сильно підозрюю, що поділ ніколи не потрібен (хоча я не можу довести це, але я перевірив, що це справа до 500).

Програма завершується через NameErrorдрук результату (зрештою).


Що ;Eв кінці роблять?
Leaky Nun

@KennyLau Це NameErrorдля припинення, оскільки Eніде більше не визначено
Sp3000

Ого, така кмітливість.
Leaky Nun



0

Пітон 3, 183 байти

e,n=enumerate,input()
v=list('0123456789')+[' '*n]*n*2
for i,s in e(v):
 for j,t in e(v):
  for o,k in zip('+*-',(i+j,i*j,i-j)):
   if 9<k<2*n:v[k]=min(v[k],s+t+o,key=len)
print(v[n])

Швидкість не зовсім безпідставна (123, 221, 1237, 6551 закінчення в порядку секунд або хвилин). Змінаif оператор, щоб if j<=i and <k<2*nпришвидшити його більше, ціною на 9 байт. Я залишив поділ ( /), тому що не можу зрозуміти, як це потрібно.


Підказка: поділ потрібен.
Leaky Nun
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.