Єгипетські дроби


20

Огляд:

З Вікіпедії : Єгипетська частка - це сума окремих одиничних дробів. Тобто кожен дріб у виразі має чисельник, рівний 1, і знаменник, який є додатним цілим числом, і всі знаменники відрізняються один від одного. Значення виразу цього типу - додатне раціональне число a / b. Кожне позитивне раціональне число може бути представлене єгипетською дробом.

Виклик:

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

Правила / обмеження:

  • Вхідними даними будуть два цілих цілих значення.
    • Це може бути STDIN, argvчерез кому, через пробіл, або будь-який інший метод , який ви віддаєте перевагу.
  • Перше вхідне значення має чисельник, а друге вхідне значення - знаменник.
  • Перше вхідне значення має бути меншим, ніж друге.
  • Вихід може містити значення (и), що перевищують обмеження пам'яті вашої системи / мови (RAM, MAX_INT або будь-які інші кодові / системні обмеження). Якщо це трапиться, просто обріжте результат за максимально можливим значенням і зауважте, що якимось чином (тобто ...).
  • Вихід повинен мати можливість обробляти значення знаменника до щонайменше 2,147,483,647 (2 31 -1, підписаний 32-бітний int).
    • Більш високе значення ( longтощо) цілком прийнятно.
  • Вихідним результатом повинен бути перелік усіх знаменників найменшого набору знайдених одиниць дробів (або самих дробів, тобто 1/2).
  • Вихід повинен бути впорядкований у порядку зростання відповідно до значення знаменника (зменшується на значення дробу).
  • Вихід може бути обмежений будь-яким бажаним способом, але між ними повинен бути деякий символ, щоб диференціювати одне значення від іншого.
  • Це кодовий гольф, тому найкоротше рішення виграє.

Приклади:

  • Введення 1:

    43, 48

  • Вихід 1:

    2, 3, 16

  • Введення 2:

    8/11

  • Вихід 2:

    1/2 1/6 1/22 1/66

  • Введення 3:

    5 121

  • Вихід 3:

    33 121 363


Вхід / Вихід 2 повинен бути 8, 11і 2, 6, 22, 66правильно?
mellamokb

2
Можливою пропозицією, щоб усунути неясність, було б вимагати найменший набір одиниць дробів з найменшим кінцевим знаменником. Наприклад, 1/2 1/6 1/22 1/66було б кращим 1/2 1/5 1/37 1/4070для введення 8/11.
прим

2
Я пропоную додати 5/121 = 1/33+1/121+1/363до тестових випадків. Усі жадібні програми (включаючи мою) дають 5 фракцій за це. Приклад взятий з Вікіпедії .
угорен

1
@primo Я думаю, що якщо існує кілька мінімумів, то, що б там було знайдено, було б прийнятним. Якщо в результаті один алгоритм може бути записаний із меншою кількістю символів, я не хотів би перешкоджати цьому рішенню.
Гаффі

1
Дав +1, оскільки я насправді дізнався про єгипетські фракції на курсі «Історія математики» (і мав займатися з ними математикою, а також знаходити дробові суми, як ця проблема.) Приємний та творчий виклик.
mbomb007

Відповіді:


6

Ліпп звичайний, 137 чарів

(defun z(n)(labels((i(n s r)(cond((= n 0)r)((< n(/ 1 s))(i n(ceiling(/ 1 n))r))(t(i(- n(/ 1 s))(1+ s)(cons s r))))))(reverse(i n 2'()))))

(z 43/48) -> (2 3 16)

(z 8/11) -> (2 5 37 4070)

(z 5/121) -> (25 757 763309 873960180913 1527612795642093418846225)

Не потрібно турбуватися про величезну кількість чи обробляти дробові позначення!


(defun z (n) (мітки ((i (nsr) (cond ((= n 0) r) ((<n (/ 1 s)) (в (стеля (/ 1 n)) r)) (t ( i (- n (/ 1 s)) (1+ s) (cons sr)))))) (зворотний (в 2 '()))) (z 43/48) Показати не результат у tio ... Що я повинен використовувати для друку результату?
RosLuP

1
(print (z 103/333)) повертає один список із 5 чисел, але існує один список із 4 чисел як: 1 / 4,1 / 18,1 / 333,1 / 1332. Таким чином, наведена вище функція не поверне мінімум.
RosLuP

8

Пітон 2, 169 167 символів

x,y=input()
def R(n,a,b):
 if n<2:return[b/a][b%a:]
 for m in range((b+a-1)/a,b*n/a):
  L=R(n-1,a*m-b,m*b)
  if L:return[m]+L
n=L=0
while not L:n+=1;L=R(n,x,y)
print L

Знімає розділені комами аргументи на stdin та друкує список python у stdout.

$ echo 8,11 | ./egypt.py 
[2, 5, 37, 4070]

2
1. Я думаю, ви можете зберегти дві символи, скориставшись вкладкою на другому рівні відступу. 2. Сценарій не вказує на усічення через перевищення обмежень системної пам'яті.
хлібопекарня

У Тіо Ваш код лишився пам’яті лише 103/45533
RosLuP

Натомість в Ideone ваш код виходить з помилкою часу запуску для того ж вводу 103,45533: Помилка виконання #stdin #stdout #stderr 0.89s 99264KB
RosLuP

4

PHP 82 байти

<?for(fscanf(STDIN,"%d%d",$a,$b);$a;)++$i<$b/$a||printf("$i ",$a=$a*$i-$b,$b*=$i);

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

Використання зразка:

$ echo 43 48 | php egyptian-fraction.php
2 3 16
$ echo 8 11 | php egyptian-fraction.php
2 5 37 4070

Командний оператор імітується як марні аргументи для printf? Мені слід десь зберегти цю хитрість.
Конрад Боровський

1
Я впевнений, що це жадібний алгоритм , тому він не завжди дає найменший набір дробів. Якщо ви запускаєте його з введенням на зразок 5 121або 31 311, він дасть неправильну відповідь (через дуже довгий час).
grc

@grc 31/311 -> {a [1] -> 11, a [2] -> 115, a [3] -> 13570, a [4] -> 46422970}
доктор belisarius

4

C, 163 177 символів

6/6 : Нарешті, програма тепер правильно обробляє усічення у всіх випадках. Це зайняло набагато більше символів, ніж я сподівався, але воно того варте. Програма повинна на 100% дотримуватися проблемних вимог зараз.

d[99],c,z;
r(p,q,n,i){for(c=n+q%p<2,i=q/p;c?d[c++]=i,0:++i<n*q/p;)q>~0U/2/i?c=2:r(i*p-q,i*q,n-1);}
main(a,b){for(scanf("%d%d",&a,&b);!c;r(a,b,++z));while(--c)printf("%d\n",d[c]);}

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

$ ./a.out
2020 2064
2
3
7
402
242004

$ ./a.out
6745 7604
2
3
19
937
1007747
0

Знаменники у другому прикладі становлять 95485142815/107645519046, що відрізняється від 6745/7604 приблизно 1e-14.


Знову ж таки, я думаю, що це жадібний алгоритм.
grc

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

Вибачте, я повертаю це назад. Це не відповідає жадібному рішенню, але я виявив, що воно не зовсім точне для деяких даних ( 31 311наприклад).
grc

31 311переповнює, але програма не може позначити його.
хлібниця

3

Пітон, 61 чол

Вхід від STDIN, розділений комами.
Вихід до STDOUT, розділений новим рядком.
Не завжди повертається найкоротше представлення (наприклад, за 5/121).

a,b=input()
while a:
    i=(b+a-1)/a
    print"1/%d"%i
    a,b=a*i-b,i*b

Символи рахуються без зайвих нових рядків (тобто приєднання до всіх рядків у межах whileвикористання ;).
Фракція становить a/b.
iце b/aокруглий, так що я знаю 1/i <= a/b.
Після друку 1/iзамінюю a/bна a/b - 1/i, що є (a*i-b)/(i*b).


Я хочу , щоб голосувати цей план, так як він є настільки малий, але це просто НЕ вистачає , що один шматок!
Гаффі

2
Я хочу виправити цю частину, але тоді вона не буде такою маленькою ... У мене є відчуття, що я просто винаходитиму рішення Кіта Рендалла.
угорен

2

C, 94 байти

n,d,i;main(){scanf("%i%i",&n,&d);for(i=1;n>0&++i>0;){if(n*i>=d)printf("%i ",i),n=n*i-d,d*=i;}}

Спробуйте це в Інтернеті

редагувати: У коментарях була розміщена коротша версія коду, тому я її замінив. Спасибі!


2
Привіт, ласкаво просимо на сайт! Це змагання з кодового гольфу , тому мета - зробити ваш код якомога коротшим . Схоже, існує багато речей, які ви могли б зробити, щоб скоротити код. Наприклад, ви можете видалити всю непотрібну пробіл зі своєї відповіді.
DJMcMayhem

@DJMcMayhem Дякую, сер, зрозумів і зробив.
う ち わ 密 か

Привіт, Ласкаво просимо до PPCG! Чи можете ви, можливо, додати спробу TryItOnline з тестовим кодом для тестових випадків у виклику? Крім того, деякі речі, якими ви могли б пограти в гольф: for(i=2;n>0&&i>0;i++)можуть бути for(i=1;n>0&++i>0;); кронштейни фор-петлі можна зняти (оскільки у неї є тільки ifвнутрішня частина ); d=d*i;може бути d*=i;; і я не зовсім впевнений, але я думаю , що #include <stdio.h>може бути без пробілів: #include<stdio.h>. О, і, можливо, буде цікаво прочитати Поради щодо гольфу на С та Поради щодо гольфу на <всіх мовах>
Кевін Круїйсен

@KevinCruijssen Дякую за поради.
う ち わ 密 か



0

AXIOM, 753 байти

L==>List FRAC INT
macro  M(q)==if c<m or(c=m and m<999 and reduce(max,map(denom,q))<xv)then(m:=c;a:=q;xv:=reduce(max,map(denom,a)))
f(x,n)==(y:=x;a:L:=[];c:=0;q:=denom x;q:=q^4;for i in n.. repeat((c:=c+1)>50=>(a:=[];break);1/i>y=>1;member?(1/i,a)=>1;a:=concat(a,1/i);(y:=y-1/i)=0=>break;numer(y)=1 and ~member?(y,a)=>(a:=concat(a,y);break);(i:=floor(1/y))>q=>(a:=[];break));a)
h(x:FRAC INT):L==(a:L:=[];x>1=>a;numer(x)=1=>[x];n:=max(2,floor(1/x));xv:=m:=999;d:=denom x;zd:=divisors d;z:=copy zd;for i in 2..30 repeat z:=concat(z,i*zd);d:=min(10*d,n+9*m);for i in n..d repeat((c:=maxIndex(b:=f(x,i)))=0=>1;c>m+1=>1;M(b);v:=reduce(+,delete(b,1));for j in z repeat((c:=1+maxIndex(q:=f(v,j)))=1=>1;member?(b.1,q)=>1;q:=concat(b.1,q);M(q)));reverse(sort a))

Ідеєю було б застосувати "Жадний алгоритм" з різними початковими пунктами та зберегти список, який має мінімальну довжину. Але не завжди було б знайти рішення min з менш визначеним: "масив A буде меншим, ніж масив B, якщо і лише тоді, коли A має кілька елементів B, або якщо кількість елементів A є однаковою кількістю елементів B , ніж A - це менше B, якщо чим менше елемент A більший за число, тим менший елемент B ". Недолікований і тест

-- this would be the "Greedy Algorithm"
fracR(x,n)==
   y:=x;a:L:=[];c:=0;q:=denom x;q:=q^4
   for i in n.. repeat
      (c:=c+1)>50   =>(a:=[];break)
      1/i>y         =>1
      member?(1/i,a)=>1
      a:=concat(a,1/i)
      (y:=y-1/i)=0  =>break
      numer(y)=1 and ~member?(y,a)=>(a:=concat(a,y);break)
      (i:=floor(1/y))>q           =>(a:=[];break)
   a

-- Return one List a=[1/x1,...,1/xn] with xn PI and x=r/s=reduce(+,a) or return [] for fail
Frazione2SommaReciproci(x:FRAC INT):L==
    a:L:=[]
    x>1       =>a
    numer(x)=1=>[x]
    n:=max(2,floor(1/x));xv:=m:=999;d:=denom x;zd:=divisors d;z:=copy zd
    for i in 2..30 repeat z:=concat(z,i*zd)
    d:=min(10*d,n+9*m) 
    for i in n..d repeat
        (c:=maxIndex(b:=fracR(x,i)))=0=>1 
        c>m+1                         =>1
        M(b)
        v:=reduce(+,delete(b,1))
        for j in z repeat
              (c:=1+maxIndex(q:=fracR(v,j)))=1=>1
              member?(b.1,q)                  =>1
              q:=concat(b.1,q)
              M(q) 
    reverse(sort a)

(7) -> [[i,h(i)] for i in [1/23,2/23,43/48,8/11,5/121,2020/2064,6745/7604,77/79,732/733]]
   (7)
      1   1      2   1  1      43  1 1  1      8  1 1  1  1
   [[--,[--]], [--,[--,---]], [--,[-,-,--]], [--,[-,-,--,--]],
     23  23     23  12 276     48  2 3 16     11  2 6 22 66
      5    1  1   1      505  1 1 1  1    1
    [---,[--,---,---]], [---,[-,-,-,---,----]],
     121  33 121 363     516  2 3 7 602 1204
     6745  1 1  1  1    1      1       77  1 1 1  1  1   1
    [----,[-,-,--,---,-----,------]], [--,[-,-,-,--,---,---]],
     7604  2 3 19 950 72238 570300     79  2 3 8 79 474 632
     732  1 1 1  1   1    1     1
    [---,[-,-,-,--,----,-----,-----]]]
     733  2 3 7 45 7330 20524 26388
                                                      Type: List List Any
       Time: 0.07 (IN) + 200.50 (EV) + 0.03 (OT) + 9.28 (GC) = 209.88 sec
(8) -> h(124547787/123456789456123456)
   (8)
        1             1                         1
   [---------, ---------------, ---------------------------------,
    991247326  140441667310032  613970685539400439432280360548704
                                     1
    -------------------------------------------------------------------]
    3855153765004125533560441957890277453240310786542602992016409976384
                                              Type: List Fraction Integer
                     Time: 17.73 (EV) + 0.02 (OT) + 1.08 (GC) = 18.83 sec
(9) -> h(27538/27539)
         1 1 1  1  1    1      1        1
   (9)  [-,-,-,--,---,-----,------,----------]
         2 3 7 52 225 10332 826170 1100871525
                                              Type: List Fraction Integer
                     Time: 0.02 (IN) + 28.08 (EV) + 1.28 (GC) = 29.38 sec

посилання та номери з: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fractions/egyptian.html

щоб додати щось, це нижче буде оптимізоване для знаходження мінусової дробу, яка має знаменник max (менше, а не оптимізовано для lengh)

L==>List FRAC INT

-- this would be the "Greedy Algorithm"
fracR(x,n)==
   y:=x;a:L:=[];c:=0;q:=denom x;q:=q^20
   for i in n.. repeat
      (c:=c+1)>1000  =>(a:=[];break)
      1/i>y          =>1
      member?(1/i,a) =>1
      a:=concat(a,1/i)
      (y:=y-1/i)=0  =>break
      numer(y)=1 and ~member?(y,a)=>(a:=concat(a,y);break)
      (i:=floor(1/y))>q           =>(a:=[];break)
   a

-- Return one List a=[1/x1,...,1/xn] with xn PI and x=r/s=reduce(+,a) or return [] for fail
Frazione2SommaReciproci(x:FRAC INT):L==
    a:L:=[]
    x>1       =>a
    numer(x)=1=>[x]
    n:=max(2,floor(1/x));xv:=m:=999;d:=denom x;zd:=divisors d;z:=copy zd; 
    w1:= if d>1.e10 then 1000 else 300; w2:= if d>1.e10 then 1000 else if d>1.e7 then 600 else if d>1.e5 then 500 else if d>1.e3 then 400 else 100;
    for i in 2..w1 repeat(mt:=(i*zd)::List PI;mv:=[yy for yy in mt|yy>=n];z:=sort(removeDuplicates(concat(z,mv)));#z>w2=>break)
    for i in z repeat
        (c:=maxIndex(b:=fracR(x,i)))=0=>1 
        c>m+1                         =>1
        if c<m or(c=m and m<999 and reduce(max,map(denom,b))<xv)then(m:=c;a:=b;xv:=reduce(max,map(denom,a)))
        v:=reduce(+,delete(b,1))
        for j in z repeat
              (c:=1+maxIndex(q:=fracR(v,j)))=1=>1
              member?(b.1,q)                  =>1
              q:=concat(b.1,q)
              if c<m or(c=m and m<999 and reduce(max,map(denom,q))<xv)then(m:=c;a:=q;xv:=reduce(max,map(denom,a)))
    reverse(sort a)

результати:

(5) -> [[i,Frazione2SommaReciproci(i)] for i in [1/23,2/23,43/48,8/11,5/121,2020/2064,6745/7604,77/79,732/733]]
   (5)
      1   1      2   1  1      43  1 1  1      8  1 1  1  1
   [[--,[--]], [--,[--,---]], [--,[-,-,--]], [--,[-,-,--,--]],
     23  23     23  12 276     48  2 3 16     11  2 6 22 66
      5    1  1   1      505  1 1 1  1    1
    [---,[--,---,---]], [---,[-,-,-,---,----]],
     121  33 121 363     516  2 3 7 602 1204
     6745  1 1  1  1    1      1       77  1 1 1  1  1   1
    [----,[-,-,--,---,-----,------]], [--,[-,-,-,--,---,---]],
     7604  2 3 19 950 72238 570300     79  2 3 8 79 474 632
     732  1 1 1  1   1    1     1
    [---,[-,-,-,--,----,-----,-----]]]
     733  2 3 7 45 7330 20524 26388
                                                      Type: List List Any
                     Time: 0.08 (IN) + 53.45 (EV) + 3.03 (GC) = 56.57 sec
(6) -> Frazione2SommaReciproci(124547787/123456789456123456)
   (6)
        1            1               1                  1
   [---------, ------------, ----------------, -------------------,
    994074172  347757767307  2764751529594496  1142210063701888512
                      1
    -------------------------------------]
    2531144929865351036156388364636113408
                                              Type: List Fraction Integer
         Time: 0.15 (IN) + 78.30 (EV) + 0.02 (OT) + 5.28 (GC) = 83.75 sec
(7) -> Frazione2SommaReciproci(27538/27539)
         1 1 1  1   1     1       1       1
   (7)  [-,-,-,--,----,-------,-------,-------]
         2 3 7 43 1935 3717765 5204871 7105062
                                              Type: List Fraction Integer
                     Time: 0.05 (IN) + 45.43 (EV) + 2.42 (GC) = 47.90 sec

Здається, багато хороших знаменників є діловими факторами знаменника вхідної фракції.


0

C, 85 78 байт

Покращено за допомогою @ceilingcat , 78 байт:

n,d;main(i){for(scanf("%i%i",&n,&d);n;n*++i/d&&printf("%i ",i,d*=i,n=n*i-d));}

Спробуйте в Інтернеті!


Моя оригінальна відповідь, 85 байт:

n,d,i=1;main(){for(scanf("%i%i",&n,&d);n&&++i;n*i/d?printf("%i ",i),n=n*i-d,d*=i:0);}

Спробуйте в Інтернеті!

Частина заслуги повинна бути приділена Джонатану Фреху , який написав це рішення на байті 94 байти, яке я покращив.


0

APL (NARS), 2502 байти

fdn←{1∧÷⍵}⋄fnm←{1∧⍵}⋄ffl←{m←⎕ct⋄⎕ct←0⋄r←⌊⍵⋄⎕ct←m⋄r}⋄divisori←{a[⍋a←{∪×/¨{0=≢⍵:⊂⍬⋄s,(⊂1⌷⍵),¨s←∇1↓⍵}π⍵}⍵]}

r←frRF w;x;y;c;q;i;j
(x i)←w⋄i-←1⋄y←x⋄r←⍬⋄c←0⋄q←fdn x⋄q←q*20
i+←1
→4×⍳∼1000<c+←1⋄→6
j←÷i⋄→2×⍳j>y⋄→2×⍳(⊂j)∊r⋄r←r,(⊂j)⋄y←y-j⋄→0×⍳y=0⋄→5×⍳1≠fnm y⋄→5×⍳(⊂y)∊r⋄r←r,⊂y⋄→0
→2×⍳∼q<i←ffl ÷y
r←⍬

r←fr2SumF x;n;xv;m;d;zd;z;i;b;c;t;v;j;k;q;w1;w2;t;b1
z←r←⍬⋄→0×⍳1≤ffl x
:if 1=fnm x⋄r←,⊂x⋄→0⋄:endif
n←2⌈ffl÷x⋄xv←m←999⋄d←fdn x⋄zd←divisori d
w1←1000⋄w2←50⋄:if d>1.e10⋄w2←700⋄:elseif d>1.e7⋄w2←600⋄:elseif d>1.e5⋄w2←500⋄:elseif d>1.e3⋄w2←400⋄:elseif d>1.e2⋄w2←100⋄:endif
:for i :in ⍳w1⋄z←∪z∪k/⍨{⍵≥n}¨k←i×zd⋄:if w2<≢z⋄:leave⋄:endif⋄:endfor
z←∪z∪zd⋄z←z[⍋z]
:for i :in z
    :if 0=c←≢b←frRF x i ⋄:continue⋄:endif
    :if      c>m+1      ⋄:continue⋄:endif
    :if      c<m        ⋄m←c⋄r←b⋄xv←⌈/fdn¨b
    :elseif (c=m)∧(m<999)
         :if xv>t←⌈/fdn¨b⋄m←c⋄r←b⋄xv←t⋄:endif
    :endif
    :if c≤2⋄:continue⋄:endif
    v←↑+/1↓b⋄b1←(⊂↑b)
    :for j :in z
       :if 1=c←1+≢q←frRF v j⋄:continue⋄:endif
       :if        b1∊q      ⋄:continue⋄:endif
       q←b1,q
       :if  c<m⋄m←c⋄r←q     ⋄xv←⌈/fdn¨q
       :elseif (c=m)∧(m<999)
           :if xv>t←⌈/fdn¨q⋄m←c⋄r←q⋄xv←t⋄:endif
       :endif
    :endfor
:endfor
→0×⍳1≥≢r⋄r←r[⍋fdn¨r]

Переклад з коду AXIOM для цієї проблеми, до APL, використовуючи вперше (для мене) тип дробу (тобто bignum ...).

103r233 означає фракцію 103/233. Тест:

  ⎕fmt fr2SumF 1r23
┌1────┐
│ 1r23│
└~────┘
  ⎕fmt fr2SumF 2r23
┌2──────────┐
│ 1r12 1r276│
└~──────────┘
  ⎕fmt fr2SumF 43r48
┌3────────────┐
│ 1r2 1r3 1r16│
└~────────────┘
  fr2SumF 8r11
1r2 1r6 1r22 1r66 
  fr2SumF 5r121
1r33 1r121 1r363 
  fr2SumF 2020r2064
1r2 1r3 1r7 1r602 1r1204 
  fr2SumF 6745r7604
1r2 1r3 1r19 1r950 1r72238 1r570300 
  fr2SumF 77r79
1r2 1r3 1r8 1r79 1r474 1r632 
  fr2SumF 732r733
1r2 1r3 1r7 1r45 1r7330 1r20524 1r26388 
  fr2SumF 27538r27539
1r2 1r3 1r7 1r43 1r1935 1r3717765 1r5204871 1r7105062 
  fr2SumF 124547787r123456789456123456
1r994074172 1r347757767307 1r2764751529594496 1r1142210063701888512 
  1r2531144929865351036156388364636113408 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.