[Thảo luận]Cái bignum hôm bữa Ruby

hôm bữa có test cái ruby tính giai thừa trong tích tắc làm anh em mê đắm thì xin mời đội ruby vô giải thích lý giải cái này cho cái.
đã test trong python chạy nhanh không kém test giới hạn của nó là 984.
Mời anh em chém gió tiếp.

Vấn đề này là mời anh Đôn vào trả lời. Nhưng mà giờ mình cũng có câu hỏi là cấu trúc dữ liệu của Python như thế nào mà nó cũng xử lí nhanh như bên Ruby vậy? Hehe, mời bác :smiley:

P.S: mà hôm bữa anh An tính giai thừa bằng vòng lặp nó cũng nhanh không kém mà giới hạn lên tới hơn 4 chữ số :))

A.Đôn đâu rồi?

[QUOTE=09520134;2581]Vấn đề này là mời anh Đôn vào trả lời. Nhưng mà giờ mình cũng có câu hỏi là cấu trúc dữ liệu của Python như thế nào mà nó cũng xử lí nhanh như bên Ruby vậy? Hehe, mời bác :smiley:

P.S: mà hôm bữa anh An tính giai thừa bằng vòng lặp nó cũng nhanh không kém mà giới hạn lên tới hơn 4 chữ số :))[/QUOTE]

Nó chuyển đổi về mảng các bit để thao tác. (Đọc [1] để biết thêm chi tiết )
Quá trình chọn kiểu giữa int hay float xảy ra tự động bên trong ngôn ngữ , tùy thuộc vào khả năng lưu trữ 1 word của máy , nếu vượt quá thì nó sẽ chuyến sang lưu trữ dựa trên bộ nhớ của máy .

Ví dụ về quá trình chuyển đổi này :

Trước hết ta cần một hàm cho về một số cực lớn , chẳng hạn như hàm tính toán giai thừa như sau :


def back_mul(x):
    if(x>1):
        i = 2
        s = 1
        while(i <= x ):
            s = s * i
            i +=1
        return s
        
def fact(x):
    if x == 0 or x == 1 :
        return 1
    else :
        return back_mul(x)
        

Thử xem , với hàm fact(x) này thì với x bằng mấy thì quá trình chuyển kiểu từ int sang long diễn ra :



if __name__ == '__main__':
    count = 0
    while(str(type(fact(count))) == "<type 'int'>"):
        count += 1
    print "the bigest value of count is : %s" %(count )

Kết quả :


>>> 
the bigest value of count is : 13
>>> 

Với giá trị x = 13 này , số bit ta cần để lưu trữ giá trị (ở dạng nhị phân là ) :


>>> long.bit_length(fact(13))

==>

 33 

Với x = 12 thì sao ? fact(12) trả về kết quả kiểu int


>>> type(fact(12))
>>> <type 'int'>

hoặc


>>> isinstance(fact(12) , int)
>>> True


>>> s = bin(fact(12))
>>> len(s[2:len(s)]

Kết quả là :


29

Thử cái nào to hơn tí !


>>> long.bit_length(fact(2000))
19053
>>> 

tham khảo :
[1] http://www.python.org/dev/peps/pep-0237/
[2] http://stackoverflow.com/questions/5295903/how-many-bits-does-a-word-contain-in-32-64-bit-os-respectively
[3] http://stackoverflow.com/questions/538551/handling-very-large-numbers-in-python

Sorry cả nhà for delay! :cool:
Dạ, điều đầu tiên em muốn nói là về nhận định của các “chuyên gia trong ngành” về Bignum:

  • Ưu điểm: Không giới hạn
  • Khuyết điểm: + CHẬM (Ngược với những gì em và các bác “cảm thấy” nhỉ?! :wink: )
    + Tốn bộ nhớ.
    Vậy giờ chúng ta sẽ từng bước tìm hiểu sao lại có các nhận định này!:cool:

Hmmm, bắt đầu với cái gì giờ nhỉ? :confused:, chẹp, data struct trước nhen.:eek:

struct RBignum {
00837     struct RBasic basic;
00838     union {
00839         struct {
00840             long len;
00841             BDIGIT *digits;
00842         } heap;
00843         BDIGIT ary[RBIGNUM_EMBED_LEN_MAX];
00844     } as;
00845 };

ruby.h

À chết quên mất, trước tiên các bác cần đọc về objects structures của Ruby tại http://rhg.rubyforge.org/chapter02.html đã nhen. (Em đi làm tách trà chờ các bác đã.:D)


Xong rồi đúng hok cả nhà? ;;). Nào, đầu tiên ta thấy gì nhỉ? Thay vì lưu trữ số dưới dạng nhị phân như thông thường, Bignum lưu số dưới dạng một dãy các số thập phân, sau đó thì Bignum sử dụng những math function của máy tính để tính toán số học như…cách mà chúng ta làm trên giấy vậy! :rolleyes: → Chính điều này khiến Bignum chậm. Haizz!

Như chúng ta đã biết thì số nguyên 32 bits có thể lưu được một trời ơi số (trong khoảng của nó) chỉ trong vòng 4 byte, nhưng với Bignum? 4 byte chỉ lưu được 4 CHỮ SỐ! → Quá tốn bộ nhớ! Nhưng bù lại được cái ưu điểm là không giớ hạn về kích thước, trên lý thuyết thì các bác có thể làm việc với số SIÊU LỚN (có thể vượt quá cả số lượng RAM mà các bác có! Nhưng nhớ, đây là trên lý thuyết nhá! Haha) Và em đã thử với tình giai thừa tới 12000, vẫn bỉnh thường!:rolleyes:

Giờ chúng ta xem thử cách mà Ruby khởi tạo một số lớn nha:

00133 static VALUE
00134 bignew_1(VALUE klass, long len, int sign)
00135 {
00136     NEWOBJ(big, struct RBignum);
00137     OBJSETUP(big, klass, T_BIGNUM);
00138     RBIGNUM_SET_SIGN(big, sign?1:0);
00139     if (len <= RBIGNUM_EMBED_LEN_MAX) {
00140         RBASIC(big)->flags |= RBIGNUM_EMBED_FLAG;
00141         RBIGNUM_SET_LEN(big, len);
00142     }
00143     else {
00144         RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
00145         RBIGNUM(big)->as.heap.len = len;
00146     }
00147 
00148     return (VALUE)big;
00149 }
00150 
00151 #define bignew(len,sign) bignew_1(rb_cBignum,len,sign)
00152 
00153 VALUE
00154 rb_big_new(long len, int sign)
00155 {
00156     return bignew(len, sign != 0);
00157 }

bignum.c

rồi đến xem thử phương thức cộng 2 số nữa:

VALUE
01886 rb_big_plus(VALUE x, VALUE y)
01887 {
01888     long n;
01889 
01890     switch (TYPE(y)) {
01891       case T_FIXNUM:
01892         n = FIX2LONG(y);
01893         if ((n > 0) != RBIGNUM_SIGN(x)) {
01894             if (n < 0) {
01895                 n = -n;
01896             }
01897             return bigsub_int(x, n);
01898         }
01899         if (n < 0) {
01900             n = -n;
01901         }
01902         return bigadd_int(x, n);
01903 
01904       case T_BIGNUM:
01905         return bignorm(bigadd(x, y, 1));
01906 
01907       case T_FLOAT:
01908         return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
01909 
01910       default:
01911         return rb_num_coerce_bin(x, y, '+');
01912     }
01913 }

bignum.c

Về cơ bản là vậy các bác ạ, còn việc sao cả nhà mình cảm thấy nhanh thế, em chỉ còn biết nghĩ đến: Cảm ơn các nhà thiết kế phần cứng! :">

Có vẻ như với cấu trúc dữ liệu như vậy thì bên Python xử lí nhanh hơn nhỉ. Nhưng mà thôi kệ test thử chơi cho biết. Mình thử tính thời gian thực thi hàm tính giai thừa bằng đệ quy với tham số truyền vào là 5000 thử xem thằng nào tính nhanh hơn :smiley:

def fact(n)
if n==0
1
else
n*fact(n-1)
end
end
begin_time = Time.now
result = fact(5000)
end_time = Time.now
puts "Execution time = #{(end_time - begin_time) * 1000} milliseconds

Kết quả:

Bên python show hàng thử đi :smiley:

[QUOTE=09520134;2589]Có vẻ như với cấu trúc dữ liệu như vậy thì bên Python xử lí nhanh hơn nhỉ. Nhưng mà thôi kệ test thử chơi cho biết. Mình thử tính thời gian thực thi hàm tính giai thừa bằng đệ quy với tham số truyền vào là 5000 thử xem thằng nào tính nhanh hơn :smiley:
[/QUOTE]

Tình hình là như vậy là không thể so sánh được nha Khải! :smiley:

Sao vậy :frowning:

[QUOTE=09520134;2592]Sao vậy :([/QUOTE]

Đồng ý với ý kiến của bạn Đôn , không thể so sánh được như trên , bởi trong khoảng thời gian từ khi gọi :

 
begin_time = Time.now


end_time = Time.now

Hàm fact() không nhất thiết được thực hiện liên tục , điều này phụ thuộc vào việc cấp phát tài nguyên CPU của hệ điều hành .
Có thể trong thời gian này ,có thể có rất nhiều tiến trình khác xen vào :-ss
Thế nên không thể so sánh đơn giản như vậy được

Sử dụng vòng lặp thay vì đệ quy, tính giai thừa của 100.000. Chạy khi máy đang rảnh (Ctrl + Alt + F1, login và test bằng dòng lệnh thay vì vào X), vẫn có thể có một so sánh tương đối, thử đi.

Và dù là trong điều kiện lý tưởng, tức là mình nó chạy thì cũng chẳng thể so sánh vậy được, CPU khác nhau, khả năng tính toán cũng khác nhau rồi! :smiley:

[QUOTE=07520004;2594]Sử dụng vòng lặp thay vì đệ quy, tính giai thừa của 100.000. Chạy khi máy đang rảnh (Ctrl + Alt + F1, login và test bằng dòng lệnh thay vì vào X), vẫn có thể có một so sánh tương đối, thử đi.[/QUOTE]

limit của nó là 984 anh :smiley:
nhân tiện hỏi vì sao limit của nó là 984 :-?

[QUOTE=09520017;2596]limit của nó là 984 anh :smiley:
nhân tiện hỏi vì sao limit của nó là 984 :-?[/QUOTE]

Đọc kỹ cái câu “sử dụng vòng lặp thay vì đệ quy” chứ

@Đôn: Chạy trên cùng một máy chứ ai điên lại chạy trên 2 máy khác nhau

[QUOTE=07520004;2604]Đọc kỹ cái câu “sử dụng vòng lặp thay vì đệ quy” chứ

@Đôn: Chạy trên cùng một máy chứ ai điên lại chạy trên 2 máy khác nhau[/QUOTE]

Tại thấy chú Khải đang có ý định đó mà!:cool:

Ý định của em là test 2 thằng Python với Ruby chứ đâu có nói là phải test trên 2 máy đâu :))

[QUOTE=07520004;2604]Đọc kỹ cái câu “sử dụng vòng lặp thay vì đệ quy” chứ

@Đôn: Chạy trên cùng một máy chứ ai điên lại chạy trên 2 máy khác nhau[/QUOTE]
thế à thế thì vì sao đệ quy limit của nó 984 :-?

[QUOTE=09520134;2614]Ý định của em là test 2 thằng Python với Ruby chứ đâu có nói là phải test trên 2 máy đâu :))[/QUOTE]

Nhưng ý em là để cho bên kia chạy Python bên máy họ thì thành 2 máy mất tiêu rồi còn đâu! :stuck_out_tongue:

[pntruongan@myhost ~]$ for i in {1…5} ; do ruby test.rb ; done
Execution time = 16665.249226 milliseconds
Execution time = 16770.90626 milliseconds
Execution time = 17424.745088000003 milliseconds
Execution time = 11975.103197999999 milliseconds
Execution time = 8119.571873 milliseconds
[pntruongan@myhost ~]$ for i in {1…5} ; do python test.py ; done
Excution take 15.000612020492554 seconds
Excution take 14.943933010101318 seconds
Excution take 14.681034803390503 seconds
Excution take 14.694528818130493 seconds
Excution take 14.696242094039917 seconds
[pntruongan@myhost ~]$ for i in {1…5} ; do ruby test.rb ; done
Execution time = 16998.403903 milliseconds
Execution time = 17213.061538 milliseconds
Execution time = 16735.761197 milliseconds
Execution time = 17109.171642 milliseconds
Execution time = 17592.870357 milliseconds
[pntruongan@myhost ~]$ for i in {1…5} ; do python test.py ; done
Excution take 14.777976036071777 seconds
Excution take 14.74532413482666 seconds
Excution take 14.618777990341187 seconds
Excution take 6.212836027145386 seconds
Excution take 14.822159051895142 seconds

Các chú cứ chém lung tung quá, anh ra tay đây. Tính giai thừa của Một trăm ngàn (100000) bằng python và ruby, ghi lại kết quả của 5 lần chạy. Ai đưa ra nhận xét và lý giải phát nào

Ới, Ruby chạy nhanh thế cơ áh. Python mần tới 1x seconds luôn… Mà pro nào giải thích đi nhỉ, đặt gạch ngồi nghe ngóng tin tức :smiley:

[QUOTE=07520004;2622][pntruongan@myhost ~]$ for i in {1…5} ; do ruby test.rb ; done
Execution time = 16665.249226 milliseconds
Execution time = 16770.90626 milliseconds
Execution time = 17424.745088000003 milliseconds
Execution time = 11975.103197999999 milliseconds
Execution time = 8119.571873 milliseconds
[pntruongan@myhost ~]$ for i in {1…5} ; do python test.py ; done
Excution take 15.000612020492554 seconds
Excution take 14.943933010101318 seconds
Excution take 14.681034803390503 seconds
Excution take 14.694528818130493 seconds
Excution take 14.696242094039917 seconds
[pntruongan@myhost ~]$ for i in {1…5} ; do ruby test.rb ; done
Execution time = 16998.403903 milliseconds
Execution time = 17213.061538 milliseconds
Execution time = 16735.761197 milliseconds
Execution time = 17109.171642 milliseconds
Execution time = 17592.870357 milliseconds
[pntruongan@myhost ~]$ for i in {1…5} ; do python test.py ; done
Excution take 14.777976036071777 seconds
Excution take 14.74532413482666 seconds
Excution take 14.618777990341187 seconds
Excution take 6.212836027145386 seconds
Excution take 14.822159051895142 seconds

Các chú cứ chém lung tung quá, anh ra tay đây. Tính giai thừa của Một trăm ngàn (100000) bằng python và ruby, ghi lại kết quả của 5 lần chạy. Ai đưa ra nhận xét và lý giải phát nào[/QUOTE]

Sao bất chợt nó chạy nhanh ta ?