Найближча фракція


24

Завдання:

Вашій програмі надається належна , позитивна проста форма у форматі <numerator>/<denominator>.

Для цього введення він повинен знайти дві дроби.

  1. Фракція, менша за вхід.
  2. Фракція, що більша за вхідний.

Обидві дроби повинні мати менший знаменник, ніж вхідний. З усіх можливих фракцій вони повинні мати найменшу різницю на вході.

Вихід:

Вихід програми повинен бути:

  • Фракція, менша за вхід, у форматі <numerator>/<denominator>.
  • Далі йде пробіл (ASCII-код 32).
  • Далі йде частка, що перевищує вхідний формат <numerator>/<denominator>.

Так:

«fraction that is < input» «fraction that is > input»

Правила:

  • Усі виведені дроби повинні бути найменшими .
  • Усі виведені дроби повинні бути належними дробами.
  • Якщо немає можливих належних дробів, дозволених правилами, ви повинні вивести 0замість дробу <вхід, а 1замість дробу> введення.
  • Ви можете вибрати, чи хочете ви отримувати дріб як аргумент командного рядка (наприклад yourprogram.exe 2/5) або підказку для введення користувачем.
  • Ви можете припустити, що програма не отримає недійсні дані.
  • Виграє найкоротший код (у байтах, будь-якою мовою).
  • Будь-які нестандартні аргументи командного рядка (аргументи, які зазвичай не потрібні для запуску сценарію) зараховуються до загального числа символів.

  • Що ваша програма не повинна робити:

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

Приклади:

Вхід: 2/5
Вихід: 1/3 1/2

Вхід: 1/2
Вихід: 0 1

Вхід: 5/9
Вихід: 1/2 4/7

Вхід: 1/3
Вихід: 0 1/2

Вхід: 2/4
Вихід: 1/3 2/3

Вхід: 179565/987657
Вихід: 170496/937775 128779/708320


1
Ваш перший приклад не відповідає специфікації: Обидва дроби повинні мати менший знаменник, ніж вхідний.
Говард

1
Перший приклад повинен бути результатом 1/3 1/2.
Хайко Обердік

@HeikoOberdiek Ви маєте рацію. Виправлено.
користувач2428118

1
Визначте "середній комп'ютер домашнього користувача". Чи прийнятні 90 секунд на машині Intel Atom 1.6GHz?
Джон Дворак

2
Ваш останній приклад невірний. Вхідна частка дорівнює першій із вихідних дробів.
DavidC

Відповіді:


3

Шавлія - 119 117

x,X=map(int,raw_input().split('/'))
a=0
A=c=C=1
while C<X:exec("ab,,AB"[c*X>C*x::2]+"=c,C");c=a+b;C=A+B
print a/A,b/B

Шалфей потрібен лише в останньому рядку, який піклується про вихід. Все інше також працює в Python.

Замініть raw_input()на, sys.argv[1]щоб вхід зчитувався з аргументу командного рядка замість запиту. Це не змінює кількість символів. (Не працює в Python, не імпортуючи sysспочатку.)

Це, по суті, рекурсивно конструює відповідну послідовність Фарі, використовуючи посередники існуючих елементів, але обмежується лише тими елементами, які є найближчими до входу. З іншого боку, він здійснює пошук вкладених інтервалів у відповідних послідовностях Фарі.

Він правильно обробляє всі приклади менше ніж на секунду на моїй машині.

Ось незворушена версія:

x,X = map(Integer,sys.argv[1].split('/'))
x = x/X
a = 0
c = b = 1
while c.denominator() < X:
    if c > x:
        b = c
    else:
        a = c
    c = ( a.numerator() + b.numerator() ) / ( a.denominator() + b.denominator() )
print a,b

Я вже боявся, що я не отримаю нових заявок на цю винагороду. Чудова робота.
користувач2428118

Хороший трюк з exec!
xnor

Як єдину відповідь, подану протягом періоду щедрості, я дарую вам нагороду. Вітаю.
користувач2428118

Я просто фіксований про помилку в одному з прикладів. Ви можете скорегувати свою заявку (навіть не минувши півроку).
користувач2428118

12

Пітон 2,7 - 138

x,y=n,d=map(int,raw_input().split('/'))
while y:x,y=y,x%y
def f(p,a=d):
 while(a*n+p)%d:a-=1
 print`(a*n+p)/d`+('/'+`a`)*(a>1),
f(-x);f(x)

Я розпочав із очевидного рішення, пов'язаного з грубою силою, але зрозумів, що оскільки ОП хоче розв'язати екземпляри з шестизначними чисельниками та знаменниками за хвилину, мені потрібно краще рішення, ніж спроба трильйона можливостей. Я знайшов зручну формулу на сторінці Вікіпедії для послідовності Фарі: Якщо a / b, c / d є сусідами в одній із послідовностей Фарі, з a/b<c/d, тоді b*c-a*b=1. Цикл while у f в моїй програмі поширює цей факт на не зменшені числа, використовуючи gcd, який обчислює інший цикл.

Я вже досить важко гольфував, але хотів би почути будь-які пропозиції.

Зміни:

166-> 162: Видалено aта bіз зовнішньої програми. Вони були непотрібні.
162-> 155: str()-> ``
155-> 154: Додано k.
154-> 152: Вилучено xзсередини функції, передав її як аргумент.
152-> 150: Дайте aзначення за замовчуванням замість того, щоб передавати його як аргумент.
150-> 146: Змінено ініціалізацію xта y.
146-> 145: Вилучено k.
145-> 144: Змінено ... і ... або ... на (..., ...) [...], заощадивши таким чином простір.
144-> 138: Змінено (..., ...) [...] на ... + ... * (...). Завдяки @ mbomb007

Тестові приклади:

2/5
1/3 1/2

1/2
0 1

2/4
1/3 2/3

179565/987657
170496/937775 128779/708320

12345678/87654321
12174209/86436891 11145405/79132382

Другий останній тест зайняв секунду на моєму комп’ютері, тоді як останній - на 5-10 секунд.


Це k=1чисте зло.
Євпок

1
@Evpok: Я намагався змусити k = y = n працювати, але, мабуть, якщо ви зміните змінну всередині функції, python хоче, щоб вона була локальною. Це був єдиний спосіб отримати локальну змінну в 4 символи. Крім того, оскільки частка є позитивною та належною, знаменник не може бути 1.
isaacg

Аргументи командного рядка зручні для Python, тому їх слід було використовувати для введення, як тут указано.
Алекс Торнтон

1
" Ви можете вибрати, чи хочете ви отримати фракцію як аргумент командного рядка (наприклад, yourprogram.exe 2/5) або як запит для введення користувачем ."
isaacg

Збережіть 6 символів:print`(a*n+p)/d`+('/'+`a`)*(a>1),
mbomb007

5

Математика, 163 байти

{a,b}=FromDigits/@InputString[]~StringSplit~"/";r=Range[b-1];""<>Riffle[#~ToString~InputForm&/@(#@DeleteCases[#2[a/b*r]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}})," "]

Це суворо обмежено вимогою введення / виводу як введення користувача, так і рядків. Справа зі струнами дійсно громіздка в Mathematica (принаймні, коли ви хочете займатися гольфом). Роблячи це природним шляхом у Mathematica (використовуючи лише цілі числа та раціональні знаки), я, мабуть, зменшив би це значення до 50% від розміру.

Це може зробити 6-значний цифри за кілька секунд на моїй машині.

Трохи більш читабельні (хоча насправді не гольф):

{a, b} = FromDigits /@ InputString[]~StringSplit~"/";
r = Range[b - 1];
"" <> Riffle[#~ToString~
     InputForm & /@ (#[DeleteCases[#2[a/b*r]/r, a/b]] & @@@ {{Max, 
       Floor}, {Min, Ceiling}}), " "]

Для задоволення від цього, роблячи це "природним шляхом", тобто як функція, що приймає чисельник і знаменник і повертає два раціональних значень, це лише 84 символи (тому моя 50% оцінка насправді була досить близькою):

f[a_,b_]:=#@DeleteCases[#2[a/b*(r=Range[b-1])]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}}

3

Джулія - 127 125 байт

Я підходив до цього з математичної точки зору, щоб уникнути потреби в циклі, тому цей код працює досить швидко для великих входів (зауважте: якщо a / b є входом, то a * b повинен вміщуватися в Int64 (Int32 для 32-бітових систем) , інакше генеруються дурницькі відповіді - якщо а і b обидва виразні в Int32 (Int16 на 32-бітових системах), проблем не виникає).

ОНОВЛЕННЯ: Більше не потрібно перевантажувати зворотну косу рису на діви, використовуючи ÷, чисту економію 2 байтів.

a,b=int(split(readline(),"/"));k=gcd(a,b);f=b-invmod(a÷k,b÷k);d=2b-f-b÷k;print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1))

Безголівки:

a,b=int(split(readline(),"/")) # Read in STDIN in form a/b, convert to int
k=gcd(a,b)           # Get the greatest common denominator
f=b-invmod(a÷k,b÷k)  # Calculate the denominator of the next biggest fraction
d=2b-f-b÷k           # Calculate the denominator of the next smallest fraction
print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1)) # Calculate numerators and print

Основна ідея: знайти найбільші d і f менше b, що задовольняє ad-bc = gcd (a, b) (наступний найменший) та be-af = gcd (a, b) (наступний найбільший), а потім обчислити c і e від там. Отриманий вихід дорівнює c / de / f, за винятком випадків, коли d або f дорівнює 1, в цьому випадку / d або / f опущено.

Цікаво, що це означає, що код також працює для позитивних неправильних дробів, доки вхід не є цілим числом (тобто gcd (a, b) = a).

У моїй системі введення 194857602/34512958303не потребує значного часу для виведення171085289/30302433084 23772313/4210525219


Тестування з 55552/999999дає мені -396/920632 486/936509.
користувач2428118

@ user2428118 - Ви використовуєте 32-бітну систему (або використовуєте 32-бітну Джулію)? Я використовував "int", це означає, що в 32-бітовій системі він буде використовувати Int32, а не Int64. int32(55552*999999)дає -282630400. Для мене з цього тесту я отримую 51143/920632 52025/936509- зауважте, що знаменники однакові, і що 52025-51143 = 486 - (- 396). Додам ноту, щоб згадати це питання.
Глен О

Якщо ви хочете переконатися, що код буде працювати для всіх входів розміру Int64, ви можете замінити "int" на "int128". З цією зміною, введення 1234567891234567/2145768375829475878результатів у 869253326028691/1510825213275018197 365314565205876/634943162554457681. Ця зміна додає лише 3 зайвих символи.
Глен О

Так, я використовую 32-розрядний комп'ютер. Я спробую це на 64-бітній машині колись, коли в мене з’явиться час.
користувач2428118

Тестування на 64-бітному комп’ютері дає правильний результат, тому я приймаю цю відповідь.
user2428118

2

JavaScript, 131

З позначенням жирної стрілки та evalдзвінками:

m=>{for(e=eval,n=e(m),i=p=0,q=1;++i</\d+$/.exec(m);)if(n*i>(f=n*i|0))g=f+1,p=f/i>e(p)?f+'/'+i:p,q=g/i<e(q)?g+'/'+i:q;return p+' '+q}

179565/987657Стрес - тест виконується приблизно 35 секунд на Firefox, набагато більше на Chrome (~ 6 хвилин)

Швидший метод, без evalпозначення стрілки та жиру

for(n=eval(m=prompt(a=i=p=0,b=c=d=q=1));++i<m.match(/\d+$/);)if(n*i>(f=n*i|0))g=f+1,p=f*c>i*a?(a=f)+'/'+(c=i):p,q=g*d<i*b?(b=g)+'/'+(d=i):q;alert(p+' '+q)

179565/987657Стрес - тест виконується протягом 5 секунд.

Не гольф:

m=prompt(); //get input
a=0; c=1; //first fraction
b=1; d=1; //second fraction
n=eval(m); //evaluate input
for (i=1; i<m.match(/\d+$/); i++) { //loop from 1 to input denominator
  f=Math.floor(n*i);
  if (n*i > f) { //if fraction not equal to simplification of input
    g=f+1; // f/i and g/i are fractions closer to input
    if (f/i>a/c) a=f, c=i;
    if (g/i<b/d) b=g; d=i; 
  }
}
alert(a+'/'+c+' '+b+'/'+d); //output values handling 0 and 1 correctly

теж ... багато ... eval. EEK
Джон Дворак

3
Проте тестування з 2/6дає 1/3 2/5, але 1/3не менше, але рівнятися 2/6 .
користувач2428118

@ user2428118 виправлено
Майкл М.

Чому ця відповідь була прийнята так рано?
Євпок

1
@ user2428118: Ви знаєте, ви можете дозволити пройти пару днів, перш ніж приймати рішення. Також це рішення вже не найкоротший.
isaacg

2

perl, 142 байти (155 без CPAN)

use bare A..Z;$/="/";N=<>;D=<>;F=N/D;K=G=1;for$H(1..D){J<F&&J>E?(E,I):J>F&&J<G?(G,K):()=(J=$_/H,"$_/$H")for(Z=int F*H)..Z+1}print I||0," $K\n"

Або якщо модулі CPAN заборонені / потрібен 3-4 рази швидший код:

$/="/";$N=<>;$D=<>;$F=$N/$D;$g=$G=1;for$d(1..$D){$f<$F&&$f>$E?($E,$e):$f>$F&&$f<$G?($G,$g):()=($f=$_/$d,"$_/$d")for($z=int$F*$d)..$z+1}print$e||0," $g\n"

Перша версія займає 9,55 секунди на моїй машині, остання версія 2,44 секунди.

Менш нечитабельні:

($N, $D) = split(m[/], <>);
$F = $N / $D;
$G = 1;
foreach $d (1 .. $D) {
    $z = int $F * $d;
    foreach $_ ($z .. $z + 1) {
        $f = $_ / $d;
        ($f < $F && $f > $E ? ($E, $e) :
        ($f > $F && $f < $G ? ($G, $g) : ())) = ($f, "$_/$d");
    }
}
print $e || 0, ' ', $g || 1, "\n";
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.