Стандартизуйте фінічне число


32

Фон

Більшість людей тут мають ознайомитись з кількома цілими базовими системами: десятковою, двійковою, шістнадцятковою, восьмеричною. Наприклад, у шістнадцятковій системі число abc.de 16 буде представляти

a*16^2 + b*16^1 + c*16^0 + d*16^-1 + e*16^-2

Однак можна також використовувати не цілі бази, як ірраціональні числа. Після того, як така база використовує золотий перетин ф = (1 + √5) / 2 ≈ 1.618 ... . Вони визначаються аналогічно цілим основам. Отже, число abc.de φ (де a до e - цілі цифри) буде представляти собою

a*φ^2 + b*φ^1 + c*φ^0 + d*φ^-1 + e*φ^-2

Зауважте, що в принципі будь-яка цифра може бути негативною (хоча ми до цього не звикли) - ми будемо представляти негативну цифру з провідною ~. Для цього питання ми обмежимось цифрами від ~9до 9, тому ми можемо однозначно записати число як один рядок (з тильдами між ними). Так

-2*φ^2 + 9*φ^1 + 0*φ^0 + -4*φ^-1 + 3*φ^-2

буде написано як ~290.~43. Таке число ми називаємо фінальним числом .

Фінальне число завжди може бути представлене в стандартній формі , це означає, що представлення використовує лише цифри 1і 0, не містять 11ніде, і з необов'язковим знаком мінус, щоб вказати, що все число від’ємне. (Цікаво, що кожне ціле число має унікальне кінцеве подання у стандартній формі.)

Представлення, які не знаходяться у стандартній формі, завжди можна перетворити на стандартну форму, використовуючи наступні спостереження:

  1. 011 φ = 100 φ (тому що φ 2 = φ + 1)
  2. 0200 φ = 1001 φ (тому що φ 2 + 1 / φ = 2φ)
  3. 0 ~ 10 φ = ~ 101 φ (тому що φ - 1 / φ = 1)

В додаток:

  1. Якщо найзначнішою цифрою є ~1(якщо решта числа є стандартною формою), число є негативним, і ми можемо перетворити його в стандартну форму, замінивши все 1і ~1, попередньо встановивши знак мінус, і знову застосувати вищевказані три правила, поки ми не отримати стандартну форму.

Ось приклад такої нормалізації (я використовую додаткові пробіли для позитивних цифр, щоб підтримувати вирівнювання кожної цифри): 1~3.2~1φ

      1~3. 2~1φ         Rule:
=     0~2. 3~1φ         (3)
=    ~1~1. 4~1φ         (3)
=  ~1 0 0. 4~1φ         (3)
=  ~1 0 0. 3 0 1φ       (3)
=  ~1 0 1. 1 0 2φ       (2)
=  ~1 1 0. 0 0 2φ       (1)
=  ~1 1 0. 0 1 0 0 1φ   (2)
= - 1~1 0. 0~1 0 0~1φ   (4)
= - 0 0 1. 0~1 0 0~1φ   (3)
= - 0 0 1.~1 0 1 0~1φ   (3)
= - 0 0 0. 0 1 1 0~1φ   (3)
= - 0 0 0. 0 1 1~1 0 1φ (3)
= - 0 0 0. 0 1 0 0 1 1φ (3)
= - 0 0 0. 0 1 0 1 0 0φ (1)

Врожайність .-0.0101φ

Для подальшого читання у Вікіпедії є дуже інформативна стаття на цю тему.

Змагання

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

Ви можете взяти вхід через STDIN, ARGV або аргумент функції та повернути результат або роздрукувати його в STDOUT.

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

Це код гольфу, найкоротша відповідь (у байтах) виграє.

Випробування

Input       Output

1           1.
9           10010.0101
1.618       10000.0000101
1~3.2~1     -0.0101
0.~1021     0. (or -0.)
105.~2      1010.0101
~31~5.~1    -100000.1001

Тепер я хочу використовувати негативні цифри у своїх номерах! 1 ~ 3 * 6 == 5 ~ 8
Аарон

Відповіді:


6

Javascript (ES6) - 446 418 422 420 байт

Мінімізовано:

F=s=>{D=[];z='000000000';N=t=n=i=e=0;s=(z+s.replace(/^([^.]*)$/,'$1.')+z).replace(/~/g,'-').replace(/-?\d/g,s=>((D[n++]=s/1),0));for(;i<n-3;i=j){if(p=D[j=i+1]){if(!e&&p<0){D=D.map(k=>-k);N=~N;p=-p}e=1}d=D[i];x=D[i+2];m=D[i+3];if(p<0){d--;p++;x++;e=j=0}if(p>1){d++;m++;p-=2;e=j=0}if(!d&&p*x==1){d=p;e=j=p=x=0}D[i]=d;D[i+1]=p;D[i+2]=x;D[i+3]=m}return(N?'-':'')+s.replace(/0/g,()=>D[t++]).replace(/^(0(?!\.))+|0+$/g,'')}

Розширено:

F = s => {
    D = [];
    z = '000000000';
    N = t = n = i = e = 0;
    s = (z + s.replace( /^([^.]*)$/, '$1.' ) + z).replace( /~/g, '-' ).
        replace( /-?\d/g, s => ((D[n++]=s/1),0) );

    for( ; i < n-3; i = j ) {
        if( p = D[j = i+1] ) {
            if( !e && p < 0 ) {
                D = D.map( k=>-k );
                N = ~N;
                p = -p;
            }
            e = 1;
        }
        d = D[i];
        x = D[i+2];
        m = D[i+3];

        if( p < 0 ) {
            d--;
            p++;
            x++;
            e = j = 0;
        }
        if( p > 1 ) {
            d++;
            m++;
            p-=2;
            e = j = 0;
        }
        if( !d && p*x == 1 ) {
            d = p;
            e = j = p = x = 0;
        }

        D[i] = d;
        D[i+1] = p;
        D[i+2] = x;
        D[i+3] = m;
    }

    return (N ? '-' : '') + s.replace( /0/g, ()=>D[t++] ).replace( /^(0(?!\.))+|0+$/g, '' );
}

Код виробляє функцію, Fяка виконує вказане перетворення.

Гольф - це важка проблема. Поповзають численні крайові корпуси, які перешкоджають спрощенню коду. Зокрема, поводження з негативами - це біль, як з точки зору розбору, так і з точки зору логічного поводження.

Слід зазначити, що код обробляє лише "розумний діапазон" входів. Щоб розширити область функції без прив’язки, кількість нулів у zможна збільшити, а константу, що обмежує while( c++ < 99 )цикл, можна збільшити. Наразі підтримуваний діапазон вже є надмірним для поставлених тестових випадків.

Вибіркові виходи

F('1')          1.
F('9')          10010.0101
F('1~3.2~1')    -0.0101
F('0.~1021')    -0.
F('105.~2')     1010.0101
F('~31~5.~1')   -100000.1001

Це -0.не дуже, але відповідь все-таки правильна. Я можу це виправити за потреби.


@ MartinBüttner: Можна, але це буде важко. Він обмежує кількість "проходів" над повним входом, і кожен прохід включає кілька операцій. Я відчуваю, що кількість пропусків, необхідних для нормалізації будь-яких nвхідних знаків, буде десь між nі n log(n). У будь-якому випадку кількість проходів можна збільшити в 10 разів на кожен доданий символ. Кількість нулів у zпостійній також є цікавою проблемою. Я підозрюю, що 9 є надмірним для будь-якого можливого введення.
COTO

@ MartinBüttner: Дякую. Я вбрав втечу в класі персонажів. Щодо того $0, Javascript його не підтримує. Або принаймні Firefox цього не робить. : P
COTO

Гаразд, я думаю, що ніколи не потрібно більше 7 провідних нулів як буфера, але я думаю, що остаточні нулі будуть дещо складніше оцінити. Що стосується зовнішньої петлі, я не думаю, що вам це навіть не потрібно, якщо ви просто зробите певний цикл (або інтегруєте його у внутрішній для циклу) і просто вирветесь, коли більше змін не знайдено. Я думаю, моя специфікація могла бути дещо зрозумілішою в цьому відношенні, але "в принципі правильною і точною для довільних (дійсних) входів" я мав на увазі, що єдиним теоретичним обмеженням повинен бути розмір вбудованих типів даних / вашої ОЗУ.
Мартін Ендер

1
@COTO Щоб зберегти 1 байти, ви можете спробувати переміщення першої частини for( i = e = 0; i < n-3; i = j )шляху for(; i < n-3; i = j )і переміщення оголошення в верхню частину, будучи N = t = n = 0;заміненаN = t = n = i = e = 0;
Ісмаель Miguel

1
@IsmaelMiguel: jне підтримується постійним при значенні i+1. Повідомлення в трьох ifблоках jскидається до 0. Отже, в будь-якій точці після першого ifблоку його не можна використовувати як проксі i+1. iСаму змінну не можна оновити до кінця циклу (використовуючи третій вислів в for), оскільки її значення використовується до самого кінця циклу. Але сказавши це, можливо, я щось пропускаю. Якщо ви можете скоротити код, протестувати його та переконатися, що він все ще працює, будь ласка, опублікуйте копію на pastebin.com та посилання тут. Я вам надаю належний кредит у відповіді. :)
COTO

2

Haskell, 336 байт

z=[0,0]
g[a,b]|a*b<0=g[b,a+b]
g x=x<z
k![a,b,c,d]=[b,a+b,d-c+read k,c]
p('.':s)=1:0:2`drop`p s
p('~':k:s)=['-',k]!p s
p(k:s)=[k]!p s
p[]=1:0:z
[1,0]&y='.':z?y
[a,b]&y=[b,a+b]?y
x@[a,b]?y@[c,d]|x==z,y==z=""|g y='-':x?[-c,-d]|g[c-1,d]='0':x&[d,c+d]|g[c,d-1]='1':x&[d,c+d-1]|0<1=[b-a,a]?[d-c,c]
m[a,b,c,d]=[1,0]?[a*d+b*c-a*c,a*c+b*d]
f=m.p

Це жадібний алгоритм, але з точним поданням [a,b]чисел a + ( a , b ∈ ℤ), щоб уникнути помилок з плаваючою комою. g[a,b]тестує, чи a + <0. Приклад використання:

*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.