Phân số không tròn


22

Khi bạn chuyển đổi một phân số thành một số thập phân và bạn muốn lưu trữ số đó, bạn thường phải làm tròn nó, bởi vì bạn chỉ muốn sử dụng một lượng bộ nhớ nhất định. Giả sử bạn chỉ có thể lưu trữ 5 chữ số thập phân, sau đó 5/3 trở thành 1.6667. Nếu bạn chỉ có thể lưu trữ 2 chữ số thập phân thì nó sẽ là 1.7 (bây giờ giả sử rằng nó luôn nằm trong khoảng từ 0 đến 9,99 ...).

Nếu bây giờ bạn cố gắng đảo ngược quá trình đó với 1.7 và bạn muốn lấy lại phân số của mình, điều đó có thể khó khăn, vì bạn biết rằng 1.7 chỉ là một số làm tròn. Tất nhiên bạn có thể thử 17/10 nhưng đó là một phần 'xấu xí' so với 5/3 'thanh lịch'.

Vì vậy, mục tiêu hiện đang tìm phân số a / b có mẫu số nhỏ nhất b, dẫn đến số thập phân được làm tròn khi được làm tròn chính xác.

Chi tiết

Đầu vào chứa một chuỗi có số lượng từ 1 đến 5 chữ số nằm trong khoảng từ 0 (bao gồm) đến 10 (không bao gồm) với dấu '.' sau chữ số đầu tiên. Hãy nói rằng nbiểu thị số chữ số. Đầu ra phải là một danh sách / mảng gồm hai số nguyên [numerator, denominator]hoặc kiểu dữ liệu hợp lý (bạn có thể tự tạo hoặc sử dụng tích hợp) trong đó tử số không âm và mẫu số là dương. Tử số / mẫu số phân số phải bằng với đầu vào khi được làm tròn chính xác thành nchữ số (có nghĩa là các n-1chữ số sau dấu thập phân).

Hạn chế: chỉ cho phép một câu lệnh lặp. Điều này có nghĩa là bạn chỉ có thể sử dụng một câu lệnh lặp đơn (như forhoặc whilehoặc gotov.v. cũng như các vòng lặp chức năng như maphoặc foldáp dụng mã cho mọi phần tử của danh sách / mảng) trong toàn bộ mã của bạn, nhưng bạn có thể 'lạm dụng' nó hoặc sử dụng đệ quy vv

Bạn nên viết một hàm. Nếu ngôn ngữ của bạn không có chức năng (hoặc thậm chí nếu có), bạn có thể thay thế giả định rằng đầu vào được lưu trữ trong một biến (hoặc đầu vào qua stdin) và in kết quả hoặc ghi vào tệp. Số byte thấp nhất sẽ thắng.

Làm tròn

Việc làm tròn phải tuân theo quy tắc làm tròn 'thông thường', tức là nếu chữ số cuối cùng bị cắt là 5 hoặc lớn hơn, bạn sẽ làm tròn và bạn sẽ làm tròn cho bất kỳ trường hợp nào khác, ví dụ:

4.5494 sẽ có kết quả khi làm tròn đến

  • 1 chữ số: 5
  • 2 chữ số: 4,5
  • 3 chữ số: 4,55
  • 4 chữ số: 4.549

Ví dụ

Vui lòng bao gồm các trường hợp thử nghiệm sau đây và các trường hợp 'thú vị' khác:

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

1
Nhưng trong các ngôn ngữ chức năng không có thứ gọi là vòng lặp. Ví dụ Foe trong haskell repeattạo ra một danh sách vô hạn đối số của nó. Tôi dường như lặp lại nhưng nó thực sự có độ phức tạp thời gian của O (1). Nhưng tôi đoán sắp xếp từng trường hợp riêng lẻ tốt hơn là không cho phép các ngôn ngữ chức năng.
tự hào

3
Tôi không thích định nghĩa hiện tại của "vòng lặp". Trong Python, ví dụ, for n in numbers: f(g(n))tương đương với map(f, map(g, numbers)). Phiên bản chức năng sử dụng maphai lần, điều đó có thực sự không được phép?
flornquake

1
@ MartinBüttner Tôi đã nói về trường hợp các ngôn ngữ chức năng sẽ không được phép vì sự mơ hồ
tự hào

1
Tôi xin lỗi vì tôi không thể thực sự đóng góp cho cuộc thảo luận đó vì kiến ​​thức của tôi về lập trình chức năng về cơ bản là bằng không. Nếu bạn có một giải pháp mà bạn không chắc chắn về việc liệu nó có tuân thủ 'quy tắc' hay không, vui lòng gửi nó đi! Cuối cùng, nó được coi là một thử thách thú vị và mang tính giáo dục!
flawr

2
@Dennis Không, đó là từ ngữ đáng tiếc, bạn có thể gửi nó dưới bất kỳ hình thức nào bạn thích, ý tưởng chính đằng sau đoạn đó là bạn không nên gặp bất lợi nếu ngôn ngữ của bạn mất nhiều byte hơn để 'đọc' số đầu vào.
flawr

Câu trả lời:


4

CJam, 41 40 36 byte

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

Asuume chuỗi đầu vào được lưu trữ trong Q, được cho phép rõ ràng bởi câu hỏi. Hãy thử trực tuyến.

Các trường hợp thử nghiệm

$ 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

Làm thế nào nó hoạt động

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

Mặc dù T-SQL không thực sự phù hợp với loại điều này, nhưng nó rất vui để thử. Hiệu suất trở nên thực sự xấu, mẫu số càng cao. Nó được giới hạn ở mẫu số 1000.

Đầu vào là một biến float @

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

Phân tích truy vấ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. Tôi thích nó. Tôi đặt vào 3.14159và nó đã cho tôi một cách hợp lệ355/113
Tom Chantler

1
+1 Tôi không bao giờ mong đợi để thấy một ngôn ngữ SQL ở đây !!!
flawr

@TomChantler Tôi nghi ngờ bạn có nghĩa là cuối cùng :)
MickyT

@flawr Thành thật mà nói tôi không nghĩ rằng nó sẽ hoạt động .. phương pháp rất tàn bạo.
MickyT

12

Haskell, 62 59

nếu chỉ những cái tên không dài ...

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

đây là một hàm trả về một Rationalgiá trị

giải thích: hàm approxRationallà một hàm lấy số float và epsilon float và trả về số hữu tỷ đơn giản nhất nằm trong khoảng cách epsilon của đầu vào. về cơ bản, trả về xấp xỉ đơn giản nhất của số float cho một tỷ lệ hợp lý trong khoảng cách "lỗi có thể tha thứ".

Hãy khai thác chức năng này để sử dụng. để làm điều này, chúng ta sẽ cần phải tìm ra diện tích của những chiếc phao làm tròn đến số đã cho. sau đó nhận được điều này vào approxRationalchức năng sẽ cho chúng ta câu trả lời.

hãy thử 1.7, ví dụ. phao thấp nhất làm tròn đến 1,7 là 1,65. bất kỳ thấp hơn sẽ không làm tròn đến 1.7. tương tự, giới hạn trên của các phao tròn đến 1,7 là 1,75.
cả hai giới hạn là giới hạn là số đầu vào +/- 0,05. có thể dễ dàng thấy rằng khoảng cách này luôn luôn 5 * 10 ^ -(the length of the input - 1)(-1 là vì luôn có một '.' trong đầu vào). từ đây mã khá đơn giản.

trường hợp thử nghiệm:

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

tiếc là nó không hoạt động trên "0." bởi vì chức năng phân tích cú pháp của Haskell không nhận ra .ở cuối phao. điều này có thể được sửa trong 5 byte bằng cách thay thế read sbằng read$s++"0".


Đó là một chức năng thú vị để có. Thông thường các hàm như vậy tồn tại với mục đích tìm ra xấp xỉ hợp lý tốt nhất cho một số trong các bước ít nhất, có thể được thực hiện bằng cách sử dụng các biểu diễn phân số tiếp tục bị cắt cụt. Ngoài ra, tìm một phân số có mẫu số thấp nhất là một sự tò mò học thuật. Người ta thường không mong đợi tìm thấy nó như là một chức năng thư viện tiêu chuẩn.
COTO

4
@COTO Chà, đây là Haskell, nó chứa đầy nghiên cứu học thuật.
tự hào

7

Ruby, 127 125 byte

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}

Xác định hàm ftrả về kết quả là a Rational. Ví dụ: nếu bạn nối thêm mã này

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

Bạn lấy

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

Vòng lặp là hơn mẫu số. Tôi đang bắt đầu với phần đầy đủ, ví dụ 31416/10000cho ví dụ cuối cùng. Sau đó, tôi đang giảm mẫu số, giảm tỷ lệ tử số (và làm tròn nó). Nếu kết quả hợp lý làm tròn số giống với số đầu vào, tôi đang nhớ một phân số mới tốt nhất.


4

Toán học, 49 53 ký tự

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

Sử dụng:

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

Đầu ra:

5/3

Các trường hợp thử nghiệm:

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

Trường hợp 0,001 tấn công tôi là kỳ lạ; vì hàm hợp lý hóa không hoạt động theo mô tả của nó, khi nó không tìm thấy trường hợp 1/667. Nó sẽ xuất ra số có mẫu số nhỏ nhất nằm trong giới hạn chỉ định.


2
haha tôi đã sử dụng chính xác cùng một giải pháp quá tệ ở Haskell nó lâu hơn. Tuy nhiên, có vẻ như giải pháp của bạn lấy một chuỗi làm đầu vào theo yêu cầu của spec.
tự hào

Đợi đã, đầu vào là một chuỗi? Dang, điều đó có nghĩa là tôi có thể rút một số thứ ra khỏi mã.
Kiểm đếm

Đầu ra của bạn 0.001không khớp với OP vì Rationalizekhông bị ràng buộc để giảm thiểu mẫu số. Như tôi đã đề cập với người bán hàng tự hào, một hàm xấp xỉ hợp lý có thể giảm thiểu mẫu số là rất bí truyền (nói ngắn gọn vì đó là một cách tệ hại và không hiệu quả đối với các số gần đúng). Tôi thường không mong đợi nó là một chức năng thư viện tiêu chuẩn.
COTO

@COTO Theo các tài liệu đó không hạn chế tối đa các mẫu mặc dù.
Martin Ender

@ MartinBüttner: Thật thú vị khi nó xuất ra 1/999. 999 trở thành mẫu số thấp nhất (chấp nhận được) chỉ với một lỗi trong khoảng từ 1e-6 đến 2e-6. Các lỗi ràng buộc rõ ràng là 5e-4. Vì vậy, bất cứ điều gì Mathicala đang làm trong trường hợp đó, nó chắc chắn không hoạt động. : P
COTO

4

Python 2.7+, 111 ký tự

Bằng chứng là bạn có thể viết mã khủng khiếp bằng bất kỳ ngôn ngữ nào:

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

Đầu ra

>>> [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

Miễn là bạn không đếm evaltoStringnhư các vòng lặp

Giải trình

Cách tiếp cận là lặp lại từ 1 đến 10000 dưới dạng mẫu số và tính toán tử số phù hợp nhất với số float, sau đó kiểm tra xem lỗi có nằm trong giới hạn không. Cuối cùng, chọn cặp nhỏ nhất từ ​​tất cả các phân số được tìm thấy.

(⍎x←⍞)Lấy chuỗi đầu vào từ màn hình, gán cho xvà eval
⍳1e5Tạo mảng từ 1 đến 10000
{...}¨Đối với mỗi phần tử của mảng, hãy gọi hàm với nó (⍎x←⍞)và các đối số (vòng lặp)

⍺×⍵Nhân các đối số Làm
⌊.5+tròn (bằng cách thêm 0,5 rồi làm tròn xuống)
n←Chỉ định n
⍺-⍵÷⍨Chia cho đối số bên phải, sau đó trừ đi đối số bên trái
(10*⍴x)×Nhân với 10 với sức mạnh của "độ dài x"
|Lấy giá trị tuyệt đối
50>Kiểm tra xem có nhỏ hơn 50 không (độ dài bằng x2 hơn số dp, vì vậy hãy sử dụng 50 ở đây thay vì 0,5)
:n ⍵⋄''Nếu kiểm tra trước đó trả về true, sau đó trả về mảng nvà đối số đúng, khác trả về chuỗi rỗng.

⍎⍕ toStringvà sau đó evalđể có được một mảng gồm tất cả các số trong mảng
2↑Chỉ chọn 2 phần tử đầu tiên, đó là cặp mẫu số-tử số đầu tiên được tìm thấy


2

GNU dc, 72 byte

Không có vòng lặp - dc thậm chí không có chúng. Thay vào đó, điều khiển đến từ một macro đệ quy đuôi đơn - thành ngữ cho dc.

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

Đầu ra:

$ 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
$ 

Phù. Giải thích một phần trong câu trả lời này .


2

Toán học, 111 ký tự

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]&

Thực sự khá đơn giản và tôi không nghĩ nó hội tụ ở bất cứ đâu nhanh như các giải pháp khác, vì tử số và mẫu số chỉ tăng thêm một. Tôi chủ yếu muốn tìm giải pháp đơn giản cho việc này. Tôi sẽ phải xem các câu trả lời khác và xem những gì thông minh xảy ra ở đó.

Đầu ra

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

Có ai ở đây kỷ niệm Ngày xấp xỉ Pi không?


Không, tôi chỉ kỷ niệm ngày gần đúng tau. = P Nhưng tôi chỉ nhận thấy rằng | 355/113 - pi | <10 ^ -6 =)
flawr

2

Bản thảo,> 300 byte

Tôi muốn làm điều này bằng một ngôn ngữ mà thực chất là loại làm tròn cần thiết. Hóa ra Applescript phù hợp với hóa đơn. Sau đó, tôi thấy enum rounding as taught in schoolvà không thể cưỡng lại việc sử dụng nó, mặc dù tính không cạnh tranh trắng trợn của Applescript cho mục đích chơi gôn:

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")

Điều này có thể được chơi golf nhiều hơn một chút, nhưng có lẽ không đáng.

Đầu ra:

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

2

BC, 151 148 byte

Chỉnh sửa - phiên bản nhanh hơn và ngắn hơn

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}}}

trường hợp kiểm tra tương tự.

Rất nhiều thứ tương tự như phiên bản trước, nhưng thay vì thử tất cả các kết hợp n / d có thể, chúng tôi leo lên trên phần dư của v và các chỉ số lạc hậu của bội số m == v * d và mẫu số d. Một lần nữa độ chính xác của tính toán là như nhau.

Đây là gỡ rối:

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
        }
    }
}

Phiên bản này thực sự chỉ có một vòng lặp đơn và chỉ thực hiện các phép toán số học $ \ Theta \ left (\ operatorname {binaryal_decimals} (v) \ right) $.

Bản gốc - phiên bản chậm

Hàm này tính toán chỉ định n và mẫu số nhỏ nhất d sao cho phân số n / d được làm tròn thành các chữ số phân số (v) bằng với một giá trị thập phân cho trước 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}

trường hợp thử nghiệm:

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

Và đây là gỡ rối:

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
}

Tôi thừa nhận, tôi đã lừa dối một chút bằng cách mô phỏng một vòng lặp bên trong thứ hai bên trong một vòng lặp bên ngoài duy nhất, nhưng không sử dụng bất kỳ câu lệnh lặp tiếp theo nào. Và đó là lý do tại sao nó thực sự thực hiện $ \ Theta \ left (v \ operatorname {binaryal_decimals} (v) ^ 2 \ right) $ hoạt động số học.


1
bạn có lẽ nên di chuyển phiên bản mới lên phía trước của bài đăng
tự hào

@proudhaskeller đã hoàn thành
Franki

1

C, 233

Điều này hoạt động bằng cách gọi hàm hợp lý r () với mẫu số bắt đầu là 1. Hàm bắt đầu tăng tử số và kiểm tra ở mọi mức tăng xem số kết quả, khi được làm tròn đến cùng một số chữ số như ban đầu, có cùng chuỗi không đại diện như bản gốc. Khi tử số đã được tăng lên rất nhiều đến mức kết quả lớn hơn ban đầu, hàm sẽ tăng mẫu số và gọi chính nó.

Điều này tất nhiên sử dụng nhiều mã hơn, nhưng tôi nghĩ rằng tinh thần của vấn đề làm nổi bật cách tiếp cận xương trần này; đối với tất cả những gì chúng ta biết, các hàm hợp lý hóa nội bộ () của các ngôn ngữ hiện đại có rất nhiều vòng lặp bên trong.

Lưu ý rằng điều này không hoạt động đối với đầu vào là "0." bởi vì đó không phải là một cách tiêu chuẩn để viết float, nên khi nó viết lại float thành chuỗi, kết quả sẽ không bao giờ là "0.".

Thông số kỹ thuật muốn một hàm trả về các giá trị thay vì chỉ in ra màn hình, do đó truyền đối số.

Mã (không được phép):

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;
        }
    }
}

Sử dụng:

#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;
}

Mã đánh gôn:

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;}}}

trên thực tế, trong triển khai thư viện Haskell ( hackage.haskell.org/package/base-4.7.0.1/docs/src/ mẹo ), định nghĩa approxRationalchỉ có một hàm trợ giúp đệ quy và không lặp nhiều hơn thế.
tự hào

tốt, tôi đã sai, nó thực sự có hai chức năng trợ giúp đệ quy, nhưng theo thông số kỹ thuật thì không sao
tự hào

Tôi đã không cố gắng nói rằng giải pháp của bất kỳ ai là không hợp lệ, chỉ muốn đăng một mà không cần hợp lý hóa tích hợp :)
RT

tất nhiên, nhưng thực tế là bản thân định nghĩa không có vòng lặp là tốt và còn nguyên vẹn, bạn đã viết trong bài đăng của mình "cho tất cả những gì chúng ta biết, các hàm hợp lý hóa () của các ngôn ngữ hiện đại có rất nhiều vòng lặp bên trong." vì vậy tôi đã kiểm tra nó
tự hào

Dù sao, giải pháp hoạt động như thế nào?
tự hào

1

Bash thuần túy, 92 byte

Như một lời giải thích một phần cho câu trả lời này , ở đây nó được chuyển sang bash:

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

Đáng chú ý:

  • bash có số học chỉ số nguyên. Vì vậy, chúng tôi quy mô thích hợp mọi thứ lên 2 * 10 ^ (số chữ số phân số).
  • bash làm tròn xuống số nguyên gần nhất; 2 trong biểu thức trên là để chúng ta có thể làm tròn đến số nguyên gần nhất ( lên hoặc xuống ).
  • Chỉ một vòng lặp
  • chúng tôi kiểm tra xem phần vượt quá hợp lý hay dưới mức thập phân và tăng mẫu số hoặc tử số tương ứng.

Đầu ra:

$ 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
$ 

Phải là một intcổng khá đơn giản - chỉ dành cho c
Chấn thương kỹ thuật số

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]

Bị đánh cắp

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]
}

Kiểm tra trong bảng điều khiển FireFox / FireBug

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

Đầu ra

1.7 [5, 3]
0. [0, 1]
0.001 [1, 667]
3.1416 [355, 113]
9.9999 [66669, 6667]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.