Ближайшая фракция


24

Задача:

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

Для этого ввода необходимо найти две дроби.

  1. Доля, которая меньше, чем вход.
  2. Доля, которая больше, чем вход.

Обе дроби должны иметь меньший знаменатель, чем входные. Из всех возможных дробей они должны иметь самую низкую разницу с входными данными.

Выход:

Вывод вашей программы должен быть:

  • Фракция, которая меньше, чем ввод, в формате <numerator>/<denominator>.
  • Затем следует пробел (ASCII-код 32).
  • Затем следует дробь, которая больше, чем ввод, в формате <numerator>/<denominator>.

Следующее:

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

Правила:

  • Все выведенные фракции должны быть в минимальных сроках .
  • Все выводимые фракции должны быть правильными.
  • Если не существует подходящих дробных значений, которые разрешены правилами, вы должны вывести 0вместо ввода дробной <input и 1вместо дробной> input.
  • Вы можете выбрать, хотите ли вы получить дробь в качестве аргумента командной строки (например, yourprogram.exe 2/5 ) или запросить ввод пользователя.
  • Вы можете предположить, что ваша программа не получит неверный ввод.
  • Самый короткий код (в байтах, на любом языке) выигрывает.
  • Любые нестандартные аргументы командной строки (аргументы, которые обычно не требуются для запуска скрипта) учитываются в общем количестве символов.

  • Что ваша программа не должна делать:

    • Зависит от любых внешних ресурсов.
    • Зависит от наличия определенного имени файла.
    • Выведите что-нибудь кроме требуемого вывода.
    • Возьмите исключительно долго бежать. Если ваша программа выполняется более минуты для дробей с 6-значным числителем и знаменателем (например,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 Ты прав. Исправлена.
user2428118

1
Определите «средний домашний компьютер пользователя». Является ли приемлемым 90 секунд на 1,6-ГГц процессоре Intel Atom?
Джон Дворак

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

Я уже боялся, что не получу новых заявок на эту награду. Отличная работа.
user2428118

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

Как единственный ответ, представленный в течение периода вознаграждения, я тем самым присуждаю вам вознаграждение. Поздравляю.
user2428118

Я просто фиксированной ошибке в одном из примеров. Возможно, вы захотите исправить свою заявку (хотя прошло уже полгода с момента ее отправки).
user2428118

12

Python 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, который вычисляет другой цикл while.

Я уже играл в гольф довольно тяжело, но я хотел бы услышать любые предложения.

Редактирование:

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.
Исаак

Аргументы командной строки просты с Python, поэтому они должны были использоваться для ввода, как указано здесь.
Алекс Торнтон

1
« Вы можете выбрать , хотите ли вы получить дробь в качестве аргумента командной строки (например, yourprogram.exe 2/5) или запросить ввод данных пользователем ».
Исаак

Сохранить 6 символов:print`(a*n+p)/d`+('/'+`a`)*(a>1),
mbomb007

5

Mathematica, 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-битных системах) в противном случае генерируются бессмысленные ответы - если a и b оба выразимы в Int32 (Int16 в 32-разрядных системах), проблем не возникает).

ОБНОВЛЕНИЕ: больше нет необходимости перегружать обратную косую черту для div, используя ÷, экономя 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))

Ungolfed:

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.
user2428118

@ 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-битной машине когда-нибудь, когда у меня будет время для этого.
user2428118

Тестирование на 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 .
user2428118

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

Почему этот ответ был принят так рано?
Евпок

1
@ user2428118: Знаете, вы можете подождать пару дней, прежде чем принимать решения. Также это решение уже не самое короткое.
Исаак

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.