Chuyển đổi số thập phân thành phân số trong Python

Một trong những vấn đề quan trọng trong việc lập trình liên quan đến những phép toán là làm sao hiển thị được số thập phân dưới dạng phân số. Trong hầu hết các thuật toán lập trình cơ bản, người ta thường chỉ quan tâm đến đáp số mà ít khi chú trọng đến cách hiển thị kết quả sao cho trực quan, dễ dùng hơn. Đơn cử là thuật toán giải phương trình bậc 2, rất thường gặp trong các chương trình nhập môn lập trình như Pascal, C, Python… Tôi thấy người ta cứ tính Delta, chia trường hợp rồi in ra kết quả, và chấp nhận đáp số thập phân; trong khi đó, các máy tính cầm tay hiện nay đều có thể hiển thị các kết quả đó dưới dạng phân số, thậm chí căn bậc hai. Nói vậy chứ có nhiều trường hợp mà máy tính fx-580VN X cũng phải đầu hàng,  chẳng hạn như phương trình $4x^2+2\left(\sqrt{3}-\sqrt{2}\right)x-\sqrt{6}=0$ có nghiệm $x=\dfrac{\sqrt{3}}{2}$ và $x=-\dfrac{\sqrt{2}}{2}$. Tôi đã viết được function giải quyết điều này trong PHP, kết hợp với Mathjax để render ra kết quả thì rất good, nhưng với Python thì chưa, bữa nay quyết định tìm tòi một phen.

Trước tiên, để làm việc với các phép tính trong Toán, ta cần import thư viện numpy. Đặc biệt, để có thể hiển thị số thập phân dưới dạng phân số, ta cần import thêm thư viện Fraction nữa.

import numpy as np
from fractions import Fraction as frac

Tiếp theo, ta sẽ xây dựng hàm Dec2Frac(), hàm này nhận đầu vào là số thập phân, đầu ra là phân số. Trong trường hợp các số thập phân hữu hạn như $0,5$, $0,75$ hay số thập phân vô hạn tuần hoàn như $0,333$, $4,567567567$ thì hàm Fraction hoàn toàn có thể thực hiện được yêu cầu.

frac(4,567567567).limit_denominator()

Tuy nhiên, cái tôi muốn là xử lý luôn những kết quả có căn bậc hai, như đã đề cập ở phần đầu. Cách xử lý của tôi là bình phương số đầu vào, sau đó rút căn tử và mẫu, khi đó ta có các tình huống sau:

  • Nếu mẫu số là số chính phương: ta rút căn bậc 2 để được mẫu số mới
  • Nếu mẫu số không phải số chính phương: ta trục căn thức ở mẫu bằng cách nhân mẫu số vào tử số
  • Nếu tử số là số chính phương: ta rút căn bậc 2 để được tử số mới, ngược lại thì hiển thị là sqrt(tử) hoặc can(tử) tùy thị hiếu của mình

Với ý tưởng trên, function đầy đủ của mình sẽ như sau:

def Dec2Frac(dec_num):
    #Nếu là số âm thì phải thêm dấu - vào kết quả
    new_dec_sign = ''
    if dec_num<0:
        new_dec_sign = '-'
    #Bình phương lên
    new_dec_num = frac(np.power(dec_num,2)).limit_denominator()
    #Lấy tử và mẫu
    new_dec_nu = new_dec_num.numerator
    new_dec_de = new_dec_num.denominator

    #Kiểm tra mẫu có phải số chính phương không, nếu phải thì rút căn, không thì trục căn
    if np.sqrt(new_dec_de).is_integer()==True:
        new_dec_de = int(np.sqrt(new_dec_de))
    else:
        new_dec_nu = new_dec_nu*new_dec_de
    #Kiểm tra tử có phải số chính phương không, nếu có thì rút căn
    if np.sqrt(new_dec_nu).is_integer()==True:
        new_dec_nu = int(np.sqrt(new_dec_nu))
    else:
        new_dec_nu = 'sqrt('+str(new_dec_nu)+')'

    #Nếu là số nguyên thì chỉ hiển thị tử số
    if new_dec_de==1:
        return str(new_dec_nu)
    else:
        return str(new_dec_nu)+'/'+str(new_dec_de)

Nào, bây giờ test thử:

print(Dec2Frac(np.sin(np.pi/3)))

Nếu kết quả ra sqrt(3)/2 là thành công rồi đó!

Rồi, bây giờ ta thử giải phương trình bậc 2 đã đề cập lúc đầu nhe. Lưu ý, nếu muốn input đầu vào dạng căn số thì ta nên import thêm thư viện sqrt nữa nhe.

from math import sqrt

Bây giờ ta viết thử chương trình giải phương trình bậc 2:

a = eval(input('Nhập hệ số a:'))
b = eval(input('Nhập hệ số b:'))
c = eval(input('Nhập hệ số c:'))
d = np.power(b,2)-4*a*c
print('Delta='+str(d))
if d<0:
    print('Phương trình vô nghiệm')
elif d==0:
    x0 = Dec2Frac(-b/(2*a))
    print('Phương trình có nghiệm kép'+x0)
else:
    x1 = Dec2Frac((-b+np.sqrt(d))/(2*a))
    x2 = Dec2Frac((-b-np.sqrt(d))/(2*a))
    print('Phương trình có 2 nghiệm là x1='+x1+' và x2='+x2)

Kết quả rất ok:

Bình luận

Chia sẻ