ส่วนที่ไม่ได้ปัดเศษ


22

เมื่อคุณแปลงเศษส่วนเป็นตัวเลขทศนิยมและคุณต้องการเก็บตัวเลขนั้นคุณต้องปัดเศษเพราะคุณต้องการใช้หน่วยความจำจำนวนหนึ่งเท่านั้น สมมติว่าคุณสามารถเก็บได้ 5 หลักเท่านั้นจากนั้น 5/3 จะกลายเป็น 1.6667 หากคุณสามารถเก็บได้เพียง 2 หลักเท่านั้นมันจะเป็น 1.7 (ตอนนี้สมมติว่ามันอยู่ระหว่าง 0 ถึง 9.99 ... )

หากคุณพยายามย้อนกลับกระบวนการนั้นด้วย 1.7 และคุณต้องการได้รับเศษส่วนกลับมาซึ่งอาจเป็นเรื่องยากเนื่องจากคุณรู้ว่า 1.7 เป็นเพียงตัวเลขที่ปัดเศษ แน่นอนคุณสามารถลอง 17/10 ได้

ดังนั้นเป้าหมายคือการหาเศษ a / b ด้วยตัวส่วนที่น้อยที่สุด b ซึ่งส่งผลให้ตัวเลขทศนิยมที่ปัดเศษเมื่อปัดเศษอย่างถูกต้อง

รายละเอียด

อินพุตประกอบด้วยสตริงที่มีตัวเลข 1 ถึง 5 หลักที่อยู่ระหว่าง 0 (รวมถึง) และ 10 (ไม่รวม) ด้วย '.' หลังจากตัวเลขตัวแรก สมมุติว่าnหมายถึงจำนวนหลัก ผลลัพธ์จะต้องเป็นรายการ / อาร์เรย์ของจำนวนเต็มสองจำนวน[numerator, denominator]หรือประเภทข้อมูลที่มีเหตุผล (คุณสามารถสร้างของคุณเองหรือใช้ในตัว) โดยที่ตัวเศษไม่เป็นลบและตัวหารเป็นค่าบวก เศษ / เศษส่วนจะต้องเท่ากับการป้อนข้อมูลเมื่อถูกปัดเศษเป็นnตัวเลขอย่างถูกต้อง(ซึ่งหมายถึงn-1ตัวเลขหลังจุดทศนิยม)

ข้อ จำกัด : อนุญาตให้ใช้คำสั่งวนซ้ำเดียวเท่านั้น ซึ่งหมายความว่าคุณสามารถใช้คำสั่งวนรอบเดียว (เช่นforหรือwhileหรือgotoอื่น ๆ เช่นเดียวกับห่วงการทำงานเช่นmapหรือfoldที่ใช้รหัสกับองค์ประกอบของรายการ / อาร์เรย์) ในรหัสทั้งหมดของคุณ แต่คุณมีอิสระที่จะ 'ละเมิด' มัน หรือใช้การสอบถามซ้ำเป็นต้น

คุณควรเขียนฟังก์ชั่น หากภาษาของคุณไม่มีฟังก์ชั่น (หรือแม้ว่าจะมี) คุณสามารถสมมติว่าอินพุตถูกเก็บไว้ในตัวแปร (หรืออินพุตผ่าน stdin) และพิมพ์ผลลัพธ์หรือเขียนลงในไฟล์ จำนวนไบต์ต่ำสุดชนะ

การล้อม

การปัดเศษควรเป็นไปตามกฎการปัดเศษ 'ตามธรรมเนียม' เช่นหากตัวเลขหลักสุดท้ายที่จะถูกตัดออกเป็น 5 หรือมากกว่าคุณจะปัดเศษขึ้นและคุณจะปัดเศษสำหรับกรณีอื่น ๆ เช่น:

4.5494 จะส่งผลให้ปัดเศษเป็น

  • 1 หลัก: 5
  • 2 หลัก: 4.5
  • 3 หลัก: 4.55
  • 4 หลัก: 4.549

ตัวอย่าง

โปรดระบุกรณีทดสอบต่อไปนี้และสิ่งที่น่าสนใจอื่น ๆ :

Input 1.7     Output 5/3
Input 0.      Output 0/1
Input 0.001   Output 1/667
Input 3.1416  Output 355/113

1
แต่ในภาษาที่ใช้งานได้ไม่มีสิ่งใดที่เหมือนกับการวนซ้ำ ตัวอย่างศัตรูใน Haskell repeatสร้างรายการอาร์กิวเมนต์ที่ไม่มีที่สิ้นสุด ดูเหมือนว่าฉันจะวนซ้ำ แต่จริง ๆ แล้วมีความซับซ้อนของเวลา O (1) แต่ฉันเดาว่าการเรียงลำดับแต่ละกรณีนั้นดีกว่าไม่อนุญาตให้ใช้ภาษาที่ใช้งานได้
ภูมิใจ haskeller

3
ฉันไม่ชอบคำจำกัดความปัจจุบันของ "ลูป" ในหลามตัวอย่างเช่นเทียบเท่ากับfor n in numbers: f(g(n)) map(f, map(g, numbers))เวอร์ชั่นสำหรับการใช้งานนั้นใช้mapสองครั้งควรจะไม่ได้รับอนุญาตหรือไม่
flornquake

1
@ MartinBüttnerฉันพูดคุยเกี่ยวกับกรณีที่ภาษาการทำงานจะไม่ได้รับอนุญาตเนื่องจากความกำกวม
Haskeller ภาคภูมิใจ

1
ฉันขอโทษที่ฉันไม่สามารถมีส่วนร่วมในการอภิปรายได้จริง ๆ เพราะความรู้เกี่ยวกับการเขียนโปรแกรมฟังก์ชั่นนั้นเป็นศูนย์ หากคุณมีวิธีการแก้ปัญหาที่คุณไม่แน่ใจว่าสอดคล้องกับ 'กฎ' หรือไม่โปรดส่งต่อไป! ในท้ายที่สุดมันควรจะเป็นความสนุกและความท้าทายทางการศึกษา!
ข้อบกพร่อง

2
@Dennis ไม่มีข้อความที่โชคร้ายคุณสามารถส่งมันในรูปแบบใดก็ได้ที่คุณชอบแนวคิดหลักที่อยู่เบื้องหลังย่อหน้านั้นคือคุณไม่ควรเสียเปรียบหากภาษาของคุณใช้จำนวนไบต์เพิ่มขึ้นสำหรับ 'อ่าน' หมายเลขอินพุต
ข้อบกพร่อง

คำตอบ:


4

CJam, 41 40 36 ไบต์

Q'./1=,:L0\{;)_Qd*mo_d2$/LmOQd-}g'/@

เท่าที่สตริงการป้อนข้อมูลจะถูกเก็บไว้ใน Q ซึ่งได้รับอนุญาตอย่างชัดเจนจากคำถาม ลองออนไลน์

กรณีทดสอบ

$ for d in 1.7 0. 0.001 3.1416; do cjam <(echo "\"$d\":Q;
> Q'./1=,:L0\{;)_Qd*mo_d2$/LmOQd-}g'/@
> "); echo; done
5/3
0/1
1/667
355/113

มันทำงานอย่างไร

Q'./1=,:L  " Count the number of characters after the dot and store it in L.     ";
0\         " Push 0 (denominator) and swap it with L (dummy value).              ";
{          "                                                                     ";
  ;        " Discard the topmost item from the stack (numerator or dummy value). ";
  )        " Increment the denominator.                                          ";
  _Qd*mo   " Multiply a copy by Double(Q) and round.                             ";
  _d2$/    " Cast a copy to Double and it divide it by the denominator.          ";
  LmO      " Round to L digits.                                                  ";
  Qd       " If the result is not Double(Q),                                     ";
}g         " repeat the loop.                                                    ";
./@        " Push a slash and rotate the denominator on top of it.               ";

15

T-SQL 254

ในขณะที่ T-SQL ไม่เหมาะกับสิ่งนี้มันสนุกที่จะลอง ประสิทธิภาพการทำงานแย่ลงจริง ๆ ส่วนที่สูงกว่า มันถูก จำกัด ให้เป็นส่วนที่ 1,000

อินพุตเป็นตัวแปรทศนิยม @

WITH e AS(SELECT *FROM(VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(0))n(n)),t AS(SELECT ROW_NUMBER()OVER(ORDER BY(SELECT \))N FROM e a,e b,e c,e d)SELECT TOP 1concat(n.n,'/',d.n)FROM t d,t n WHERE round(n.n/(d.n+.0),len(parsename(@,1)))=@ ORDER BY d.n,n.n

รายละเอียดของแบบสอบถาม

WITH                                      -- Start CTE(Common Table Expression)
 e AS(                                    --Create a set of 10 rows
   SELECT *
   FROM(VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(0))n(n)
 ),
 t AS(                                    
   SELECT ROW_NUMBER()OVER(ORDER BY(SELECT \))N 
   FROM e a,e b,e c,e d                   --Cross join e to produce 1000 numbered rows
 )
SELECT 
  TOP 1                                   --Grab first result
  concat(n.n,'/',d.n)                     --Build output
FROM t d,t n                              --Cross join t against itself for denominator and numerator
WHERE round(
  n.n/(d.n+.0),                           --Force float division with +.0
  len(parsename(@,1))                     --Get rounding length
  )=@                                     --Filter where the rounded result = input
ORDER BY d.n,n.n                          --Order by denominator then numerator

+1 ฉันรักมัน. ฉันใส่เข้าไป3.14159และมันก็มอบให้ฉันอย่างแน่นอน355/113
Tom Chantler

1
+1 ฉันไม่เคยคาดหวังว่าจะได้เห็นภาษา SQL ที่นี่ !!!
ข้อบกพร่อง

@TomChantler ฉันสงสัยว่าคุณหมายถึงในที่สุด :)
MickyT

@flawr บอกตามตรงฉันไม่คิดว่ามันจะไปทำงาน .. วิธีการบังคับเดรัจฉานมาก
MickyT

12

Haskell, 62 59

ถ้าเพียงชื่อไม่นาน ...

import Data.Ratio
f s=approxRational(read s)$50/10^length s

นี่คือฟังก์ชันที่คืนRationalค่า

คำอธิบาย: ฟังก์ชั่นapproxRationalเป็นฟังก์ชั่นที่ใช้จำนวนลอยและลอยเอปไซลอนและส่งกลับเหตุผลที่ง่ายที่สุดซึ่งอยู่ในระยะทางเอปไซลอนของอินพุต ส่งคืนค่าประมาณแบบง่ายที่สุดของโฟลตไปยังเหตุผลในระยะทาง "ข้อผิดพลาดที่ให้อภัย"

ลองใช้ประโยชน์จากฟังก์ชั่นนี้เพื่อการใช้งานของเรา สำหรับสิ่งนี้เราจะต้องหาว่าอะไรคือพื้นที่ของโฟลตที่วนขึ้นตามจำนวนที่กำหนด จากนั้นการรับสิ่งนี้เข้าสู่approxRationalฟังก์ชั่นจะทำให้เราได้คำตอบ

ตัวอย่างเช่นลอง 1.7 ทุ่นต่ำสุดที่ปัดได้ถึง 1.7 คือ 1.65 ต่ำกว่าจะไม่ปัดเป็น 1.7 ในทำนองเดียวกันขอบเขตบนของลอยที่ปัดถึง 1.7 คือ 1.75
ขีด จำกัด ทั้งสองคือขอบเขตคือหมายเลขอินพุต +/- 0.05 มันสามารถแสดงให้เห็นได้อย่างง่ายดายว่าระยะทางนี้เสมอ5 * 10 ^ -(the length of the input - 1)(-1 เป็นเพราะมี '.' อยู่ในอินพุตเสมอ) จากที่นี่รหัสนั้นค่อนข้างง่าย

กรณีทดสอบ:

*Main> map f ["1.7", "0.001", "3.1416"]
[5 % 3,1 % 667,355 % 113]

น่าเสียดายที่มันใช้ไม่ได้กับ "0" เนื่องจากฟังก์ชันตัวแยกวิเคราะห์ของ Haskell ไม่รู้จัก.จุดสิ้นสุดทุ่น สิ่งนี้สามารถแก้ไขได้ 5 ไบต์โดยแทนที่read sด้วยread$s++"0"โดย


มันเป็นฟังก์ชั่นที่น่าสนใจ โดยทั่วไปแล้วฟังก์ชันดังกล่าวมีอยู่เพื่อจุดประสงค์ในการค้นหาการประมาณด้วยเหตุผลที่ดีที่สุดให้กับตัวเลขในขั้นตอนที่น้อยที่สุดซึ่งสามารถทำได้โดยใช้การตัดเศษส่วนต่อเนื่องที่ถูกตัดทอน อีกวิธีหนึ่งการหาเศษส่วนที่มีตัวส่วนต่ำสุดนั้นเป็นความอยากรู้อยากเห็นทางวิชาการมากกว่า ปกติแล้วจะไม่คาดหวังว่ามันจะเป็นฟังก์ชั่นไลบรารีมาตรฐาน
COTO

4
@COTO นี่คือ Haskell มันเต็มไปด้วยงานวิจัยทางวิชาการ
ภูมิใจ haskeller

7

ทับทิม, 127 125 ไบต์

f=->n{b=q=r=(m=n.sub(?.,'').to_r)/d=10**p=n.count('0-9')-1
b=r if(r=(q*d-=1).round.to_r/d).round(p).to_f.to_s==n while d>1
b}

กำหนดฟังก์ชั่นfซึ่งผลตอบแทนเป็นRationalซึ่งผลตอบแทนเป็นเช่นถ้าคุณต่อท้ายรหัสนี้

p f["1.7"]
p f["0."]
p f["0.001"]
p f["3.1416"]

คุณได้รับ

(5/3)
(0/1)
(1/667)
(355/113)

ลูปอยู่เหนือตัวส่วน ฉันเริ่มต้นด้วยเศษส่วนเต็ม31416/10000ตัวอย่างเช่นตัวอย่างสุดท้าย จากนั้นฉันจะลดค่าส่วนที่เป็นตัวลดสัดส่วน (และปัดเศษ) ตามสัดส่วน หากผลการปัดเศษมีเหตุผลเหมือนกับหมายเลขอินพุตฉันจะจำเศษส่วนที่ดีที่สุดใหม่ได้


4

Mathematica, 49 53 ตัวอักษร

Rationalize[ToExpression@#,5 10^(1-StringLength@#)]&@

การใช้งาน:

Rationalize[ToExpression@#,5 10^(1-StringLength@#)]&@"1.7"

เอาท์พุท:

5/3

กรณีทดสอบ:

input: 1.7     output: 5/3
input: 0.      output: 0
input: 0.001   output: 1/999
input: 3.1416  output: 355/113

กรณี 0.001 ทำให้ฉันประหลาด เนื่องจากฟังก์ชั่นหาเหตุผลเข้าข้างตนเองไม่ทำงานตามคำอธิบายของมันเมื่อมันไม่พบเคส 1/667 มันควรจะส่งออกจำนวนที่มีตัวหารที่เล็กที่สุดที่อยู่ภายในขอบเขตที่ระบุ


2
ฮ่าฮ่าฉันใช้วิธีการเดียวกันแน่นอน เลวร้ายเกินไปใน Haskell มันนานกว่า btw ดูเหมือนว่าโซลูชันของคุณใช้สตริงเป็นอินพุตตามที่ spec ต้องการ
ภูมิใจ haskeller

เดี๋ยวก่อนอินพุตเป็นสตริงหรือไม่? นั่นหมายความว่าฉันสามารถดึงบางสิ่งออกจากรหัส
Tally

ผลลัพธ์ของคุณสำหรับ0.001ไม่ตรงกับ OP เนื่องจากRationalizeไม่อยู่ภายใต้ข้อ จำกัด เพื่อย่อส่วน ดังที่ฉันได้กล่าวถึง Haskeller ที่มีความภาคภูมิใจฟังก์ชั่นการประมาณที่มีเหตุผลเพื่อลดส่วนที่เป็นความลับสูง (ในระยะสั้นเพราะมันเป็นวิธีที่มีหมัดและไม่มีประสิทธิภาพในการประมาณตัวเลข) ปกติแล้วฉันจะไม่คาดหวังว่ามันจะเป็นฟังก์ชั่นไลบรารีมาตรฐาน
COTO

@COTO ตามเอกสารมันไม่ลดหารแม้ว่า
Martin Ender

@ MartinBüttner: 1/999มันเป็นชนิดของที่น่าสนใจว่ามันเอาท์พุท 999 กลายเป็นตัวส่วนต่ำสุด (ยอมรับได้) สำหรับข้อผิดพลาดระหว่าง 1e-6 และ 2e-6 โดยประมาณ ข้อผิดพลาดที่ถูกผูกไว้อย่างชัดเจน 5e-4 ดังนั้นไม่ว่า Mathematica จะทำอะไรในกรณีนั้น : P
COTO

4

Python 2.7+, 111 ตัวอักษร

พิสูจน์ว่าคุณสามารถเขียนโค้ดที่น่ากลัวในภาษาใด ๆ :

def f(s):
 t,e,y=float(s),50*10**-len(s),1;n=d=x=0
 while x|y:n,d=n+x,d+y;a=1.*n/d;x,y=a<t-e,a>t+e
 return n,d

เอาท์พุต

>>> [f(s) for s in ("1.7", "0.", "0.001", "3.1416")]
[(5, 3), (0, 1), (1, 667), (355, 113)]

3

APL, 50

2↑⍎⍕(⍎x←⍞){50>|(10*⍴x)×⍺-⍵÷⍨n←⌊.5+⍺×⍵:n ⍵⋄''}¨⍳1e5

ตราบใดที่คุณไม่นับevalและtoStringห่วง

คำอธิบาย

วิธีการคือวนซ้ำมากกว่า 1 ถึง 10,000 ในฐานะส่วนและคำนวณตัวเศษที่ใกล้เคียงกับ float มากที่สุดจากนั้นตรวจสอบว่าข้อผิดพลาดนั้นอยู่ภายในขอบเขตหรือไม่ สุดท้ายเลือกคู่ที่เล็กที่สุดจากเศษส่วนทั้งหมดที่พบ

(⍎x←⍞)รับอินพุตสตริงจากหน้าจอกำหนดให้xและ
⍳1e5สร้างอาร์เรย์จาก 1 ถึง 10,000
{...}¨สำหรับแต่ละองค์ประกอบของอาร์เรย์ให้เรียกใช้ฟังก์ชันด้วยและ(⍎x←⍞)และอาร์กิวเมนต์(วนซ้ำ)

⍺×⍵คูณด้วยข้อโต้แย้ง
⌊.5+ปัดเศษ (โดยการเพิ่ม 0.5 แล้วปัดเศษลง)
n←กำหนดให้n
⍺-⍵÷⍨หารด้วยอาร์กิวเมนต์ที่เหมาะสมจากนั้นลบจากอาร์กิวเมนต์ซ้าย
(10*⍴x)×คูณด้วย 10 ด้วยพลังของ "ความยาวของx"
|ใช้ค่าสัมบูรณ์
50>ตรวจสอบว่าน้อยกว่า 50 (ความยาวเท่ากับx2 เพิ่มเติม มากกว่าจำนวน dp ดังนั้นใช้ 50 ที่นี่แทน 0.5)
:n ⍵⋄''ถ้าการตรวจสอบครั้งก่อนกลับคืนเป็นจริงให้ส่งคืนอาร์เรย์ของnและอาร์กิวเมนต์ที่ถูกต้องมิฉะนั้นส่งคืนสตริงว่าง

⍎⍕ toStringและจากนั้นevalจะได้รับอาร์เรย์ของตัวเลขทั้งหมดในอาร์เรย์
2↑เลือกเฉพาะ 2 องค์ประกอบแรกซึ่งเป็นคู่แรกที่พบเศษ - เศษที่พบ


2

GNU dc, 72 ไบต์

ไม่มีลูป - dc ไม่มีแม้กระทั่ง แต่ตัวควบคุมมาจากมาโครแบบเรียกซ้ำหาง - เป็นสำนวนสำหรับ dc

?dXAr^d2*sf*sq1sd0[ld1+sd]sD[r1+r]sN[dlf*ld/1+2/dlq>Ndlq<Dlq!=m]dsmxpldp

เอาท์พุท:

$ for n in 1.7 0. 0.001 3.1416; do echo "    n = $n:"; dc unround.dc <<< $n; done
    n = 1.7:
5
3
    n = 0.:
0
1
    n = 0.001:
1
667
    n = 3.1416:
355
113
$ 

วุ้ย. คำอธิบายบางส่วนในคำตอบนี้


2

Mathematica, 111 ตัวอักษร

f=Module[{a=0,b=1,k},While[Round[a/b,10^-(StringLength[#]-2)]!=(k=ToExpression)@#,If[N[a/b]>k@#,b++,a++]];a/b]&

ค่อนข้างง่ายจริงๆและฉันไม่คิดว่ามันจะมาบรรจบกันที่ใดก็ได้เร็วเท่ากับโซลูชันอื่น ๆ เนื่องจากตัวเศษและส่วนเพิ่มขึ้นทีละตัวเท่านั้น ฉันส่วนใหญ่ต้องการค้นหาวิธีแก้ปัญหาง่าย ๆ นี้ ฉันจะต้องดูคำตอบอื่น ๆ และดูว่ามีอะไรที่ฉลาดเกิดขึ้นบ้าง

เอาท์พุต

f/@{"1.7","0.0","0.001","3.1416","3.14"}
{5/3, 0, 1/667, 355/113, 22/7}

ใครที่นี่ฉลองวันPi ประมาณ ?


ไม่ฉันกำลังเฉลิมฉลองวันที่เอกภาพเท่านั้น = P แต่ฉันเพิ่งสังเกตเห็นว่า | 355/113 - pi | <10 ^ -6 =)
ข้อบกพร่อง

2

Applescript,> 300 ไบต์

ฉันต้องการทำสิ่งนี้ในภาษาที่จำเป็นต้องมีการปัดเศษ ปรากฎว่า Applescript เหมาะกับการเรียกเก็บเงิน จากนั้นฉันเห็น enum rounding as taught in schoolและไม่สามารถต้านทานการใช้งานได้แม้จะมีความไม่ชัดเจนของ Applescript สำหรับการเล่นกอล์ฟ:

on u(q)
    set n to 0
    set d to 1
    set x to 0
    set AppleScript's text item delimiters to "."
    set f to 10 ^ (q's text item 2's length)
    repeat until x = q as real
        set x to (round n * f / d rounding as taught in school) / f
        if x < q then set n to n + 1
        if x > q then set d to d + 1
    end repeat
    return {n, d}
end u

log my u("1.7")
log my u("0.")
log my u("0.001")
log my u("3.1416")

นี่สามารถเล่นกอล์ฟได้มากกว่านี้ แต่อาจไม่คุ้มค่า

เอาท์พุท:

(*5, 3*)
(*0, 1*)
(*1, 667*)
(*355, 113*)

2

พ.ศ. , 151 148 ไบต์

แก้ไข - รุ่นที่เร็วกว่าและสั้นกว่า

define f(v){s=scale(x=v);for(i=r=1;i<=10^s;i+=1){t=v*i+1/2;scale=0;p=t/=1;scale=s+1;t=t/i+10^-s/2;scale=s;t=t/1-v;if((t*=-1^(t<0))<r){r=t;n=p;d=i}}}

กรณีทดสอบเดียวกัน

มากคล้ายกับรุ่นก่อนหน้านี้ แต่แทนที่จะลองชุดที่เป็นไปได้ทั้งหมดเราจะไต่ข้ามส่วนที่เหลือของ v และผลหารย้อนหลังของทวีคูณ m == v * d และตัวส่วน d ความแม่นยำของการคำนวณก็เหมือนกัน

นี่มันไม่พันกัน:

define f(v)
{
    s= scale(x=v)
    for( i=r=1; i <= 10^s; i+=1 ){
        t= v * i +1/2
        scale=0
        m=t/=1 # this rounded multiple becomes nominator if
               # backward quotient is first closest to an integer
        scale=s+1
        t= t / i +10^-s/2 # divide multiple back by denominator, start rounding again...
        scale=s
        t= t/1 - v # ...rounding done. Signed residue of backward quotient
        if( (t*= -1^(t < 0)) < r ){
            r=t
            n=m
            d=i
        }
    }
}

รุ่นนี้มีการวนซ้ำเพียงครั้งเดียวและมีเพียง $ \ Theta \ left (\ operatorname {fractional_decimals} (v) \ right) $ การดำเนินการทางคณิตศาสตร์

ต้นฉบับ - รุ่นช้า

ฟังก์ชันนี้คำนวณตัวเล็กที่สุด n และตัวส่วน d ที่เศษส่วน n / d ถูกปัดเศษเป็น fractional_decimals (v) ดิจิตจะเท่ากับค่าทศนิยมที่กำหนด v

define f(v){s=scale(v);j=0;for(i=r=1;j<=v*10^s;){scale=s+1;t=j/i+10^-s/2;scale=s;t=t/1-v;if((t*=-1^(t<0))<r){r=t;n=j;d=i};if((i+=1)>10^s){i=1;j+=1}};v}

กรณีทดสอบ:

define o(){ print "Input ",x,"\tOutput ",n,"/",d,"\n" }
f(1.7); o()
> 0
> Input 1.7       Output 5/3
> 0
f(0.); o()
> 0
> Input 0 Output 0/1
> 0
f(0.001); o()
> 0
> Input .001      Output 1/667
> 0
f(3.1416); o()
> 0
> Input 3.1416    Output 355/113
> 0

และนี่คือ untangled:

define f(v)
{
    s=scale(x=v) # save in global for later print
    j=0
    # do a full sequential hill-climb over the residues r of v and all possible
    # fractions n / d with fractional_decimals(v) == s precision.
    for( i=r=1; j <= v * 10^s; ){
        scale=s+1
        t= j / i +10^-s/2 # start rounding...
        scale=s
        t= t/1 - v # ...rounding done. New residue, but still signed
        if( (t*= -1^(t < 0)) < r ){ # absolute residue better?
            # climb hill
            r=t
            n=j
            d=i
        }
        if( (i+=1) > 10^s ){ # next inner step. End?
            # next outer step
            i=1
            j+=1
        }
    }
    v
}

ฉันยอมรับว่าฉันโกงนิดหน่อยโดยเลียนแบบลูปภายในที่สองภายในลูปภายนอกเดียว แต่ไม่ต้องใช้คำสั่งลูปใด ๆ เพิ่มเติม และนั่นเป็นสาเหตุที่ทำให้ $ \ Theta \ left (v \ operatorname {fractional_decimals} (v) ^ 2 \ right) $ การดำเนินการทางคณิตศาสตร์


1
คุณควรย้ายเวอร์ชั่นใหม่ไปไว้ด้านหน้าของโพสต์
Haskeller ภาคภูมิใจ

@proudhaskeller เรียบร้อยแล้ว
Franki

1

C, 233

สิ่งนี้ทำงานได้โดยการเรียกฟังก์ชั่นหาเหตุผลเข้าข้างตนเอง r () ด้วยตัวเริ่มต้นที่ 1 ฟังก์ชันเริ่มเพิ่มตัวเศษและการตรวจสอบที่เพิ่มขึ้นทุกครั้งไม่ว่าจะเป็นหมายเลขผลลัพธ์เมื่อปัดเศษเป็นตัวเลขเดียวกันกับต้นฉบับมีสตริงเหมือนกัน ตัวแทนเป็นต้นฉบับ เมื่อตัวเศษได้รับการเพิ่มขึ้นจนทำให้ผลลัพธ์นั้นมีค่ามากกว่าเดิมฟังก์ชั่นจะเพิ่มตัวส่วนและเรียกตัวเอง

ของหลักสูตรนี้ใช้รหัสมากขึ้น แต่ฉันคิดว่าจิตวิญญาณของปัญหา exonerates วิธีกระดูกเปลือยนี้ สำหรับทุกสิ่งที่เรารู้ฟังก์ชั่นหาเหตุผลเข้าข้างตนเอง () ของภาษาที่ทันสมัยมีห่วงภายในจำนวนมาก

โปรดทราบว่าวิธีนี้ใช้ไม่ได้กับอินพุตของ "0" เพราะนั่นไม่ใช่วิธีมาตรฐานในการเขียน float ดังนั้นเมื่อมันเขียน float เป็นสตริงอีกครั้งผลลัพธ์จะไม่เป็น "0"

รายละเอียดต้องการฟังก์ชั่นที่คืนค่าแทนที่จะพิมพ์ไปที่หน้าจอดังนั้นจึงผ่านการโต้แย้ง

รหัส (ไม่ดี):

void r(char* x, int* a, int* b) {
    int i = -1;
    char z[32];
    double v =atof(x);
    while(1) {
        i++;
        double y = ((double)i)/((double)(*b));
        double w;
        sprintf(z, "%.*f", strlen(strchr(x,'.'))-1, y);
        if(strcmp(x, z)==0) {
            *a = i;
            return;
        }
        w = atof(z);
        if(w > v) {
            (*b)++;
            r(x, a, b);
            return;
        }
    }
}

การใช้งาน:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[]) {
    int num;
    int denom = 1; // start with a denominator of 1
    r(argv[1], &num, &denom);
    printf("%d/%d\n", num, denom);
    return 0;
}

รหัส Golfed:

typedef double D;
void r(char*x,int*a,int*b){int i=-1;char z[32];D v=atof(x);while(1){i++;D y=((D)i)/((D)(*b));D w;sprintf(z,"%.*f",strlen(strchr(x,'.'))-1,y);if(!strcmp(x,z)){*a=i;return;}w=atof(z);if(w>v){(*b)++;r(x,a,b);return;}}}

ที่จริงแล้วในการใช้งานไลบรารี Haskell ( hackage.haskell.org/package/base-4.7.0.1/docs/src/ … ) คำจำกัดความของapproxRationalฟังก์ชั่นผู้ช่วยแบบเรียกซ้ำเพียงครั้งเดียวและไม่มีการวนซ้ำมากกว่านั้น
ภูมิใจ haskeller

ดีฉันผิดจริง ๆ แล้วมันมีสองฟังก์ชั่นผู้ช่วยแบบเรียกซ้ำ แต่โดยสเป็คก็โอเค
ภูมิใจ haskeller

ผมไม่ได้พยายามที่จะพูดการแก้ปัญหาของทุกคนที่ไม่ถูกต้องเพียงแค่ต้องการที่จะโพสต์หนึ่งโดยไม่ต้องสร้างขึ้นในการหาเหตุผลเข้าข้าง :)
RT

แน่นอน แต่ความจริงที่ว่าคำจำกัดความของตัวเองไม่มีลูปเป็นสิ่งที่ดีและคุณเขียนลงในโพสต์ของคุณ "สำหรับทุกสิ่งที่เรารู้การใช้เหตุผลภายใน () ฟังก์ชั่นของภาษาสมัยใหม่มีลูปภายในมากมาย ดังนั้นฉันตรวจสอบมัน
ภูมิใจ haskeller

อย่างไรก็ตามการแก้ปัญหาทำงานอย่างไร
ภูมิใจ haskeller

1

Pure Bash ขนาด 92 ไบต์

ในฐานะที่เป็นคำอธิบายบางส่วนสำหรับคำตอบนี้ที่นี่มันเป็นพอร์ตที่ทุบตี:

f=${1#*.}
q=${1//.}
for((n=0,d=1;x-q;x=2*10**${#f}*n/d+1>>1,n+=x<q,d+=x>q));{ :;}
echo $n/$d

สะดุดตา:

  • bash มีเลขคณิตเป็นจำนวนเต็มเท่านั้น ดังนั้นเราปรับขนาดทุกอย่างให้เหมาะสมด้วย 2 * 10 ^ (จำนวนตัวเลขเศษส่วน)
  • bash ปัดเศษลงเป็นจำนวนเต็มที่ใกล้ที่สุด; 2 ในการแสดงออกข้างต้นคือเพื่อให้เราสามารถปัดเศษเป็นจำนวนเต็มที่ใกล้เคียงที่สุด ( ขึ้นหรือลง )
  • แค่วงเดียว
  • เราตรวจสอบว่าเหตุผลสำคัญเกินกว่าหรือขีดล่างทศนิยมและเพิ่มตัวส่วนหรือเศษตาม

เอาท์พุท:

$ for n in 1.7 0. 0.001 3.1416; do echo "    n = $n:"; ./unround.sh $n; done
    n = 1.7:
5/3
    n = 0.:
0/1
    n = 0.001:
1/667
    n = 3.1416:
355/113
$ 

ควรเป็นintพอร์ตที่ค่อนข้างตรงไปตรงมา- ไปยัง c
Digital Trauma

1

JavaScript (E6) 85

F=r=>(l=>{for(n=r,d=1;l&&r!=((n=r*d+1/2|0)/d).toFixed(l);d++);})(r.length-2)||[n|0,d]

Ungolfed

F=r=>{
  l = r.length-2; // decimal digits
  if (l==0) return [r|0, 1] // if no decimal return the same (conv to number) with denominator 1

  // loop for increasing denominator 
  for(d = 2; 
      r != ( // loop until find an equal result
      // given R=N/D ==> N=R*D
      (n=r*d+1/2|0) // find possible numerator, rounding (+0.5 and trunc)
      /d).toFixed(l); // calc result to given decimals
      d++);
  return [n,d]
}

ทดสอบในคอนโซล FireFox / FireBug

;["1.7","0.","0.001","3.1416","9.9999"].forEach(v => console.log(v,F(v)))

เอาท์พุต

1.7 [5, 3]
0. [0, 1]
0.001 [1, 667]
3.1416 [355, 113]
9.9999 [66669, 6667]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.