2012-01-11 47 views
10

Có một số cách để tìm số nguyên căn bậc hai chỉ sử dụng số học số nguyên. Ví dụ: this one. Nó làm cho đọc thú vị và cũng là một lý thuyết rất thú vị, đặc biệt là cho thế hệ của tôi, nơi các kỹ thuật như vậy không còn hữu ích nữa.Tính toán gốc thứ N với số học số nguyên

Điều chính là nó không thể sử dụng số học dấu phẩy động, để quy tắc ra phương thức newton và dẫn xuất của nó. Cách duy nhất tôi biết tìm rễ là nhị thức mở rộng, nhưng điều đó cũng đòi hỏi số học dấu chấm động.

Những kỹ thuật/thuật toán nào có thể sử dụng để tính toán số nguyên nth không thể thiếu bằng số học số nguyên?

Chỉnh sửa: Cảm ơn tất cả các câu trả lời cho đến thời điểm này. Tất cả chúng dường như là một thử nghiệm và cải tiến thông minh hơn một chút. Có cách nào tốt hơn không?

Chỉnh sửa2: Ok, do đó, có vẻ như không có cách thông minh nào để thực hiện việc này mà không cần dùng thử/cải tiến và phương thức newton hoặc tìm kiếm nhị phân. Có ai có thể so sánh hai số trong lý thuyết không? Tôi đã chạy một số điểm chuẩn giữa hai và thấy chúng khá giống nhau.

+0

Phạm vi giá trị đầu vào yêu cầu của bạn là bao nhiêu? –

+0

@PaulR, Lý tưởng nhất nó có thể được mở rộng, nhưng tôi nghĩ rằng bạn có thể giả định cả hai cơ sở và số lượng sẽ là 32 bit (unsigned) số nguyên cho bây giờ. – Matt

+0

Bạn cho phép các phép toán số nguyên nào? Rễ vuông là một trường hợp đặc biệt bởi vì nó có thể trích xuất chúng chỉ bằng cách cộng, trừ và thay đổi. – Neil

Trả lời

8

Bạn có thể sử dụng phương pháp Newton chỉ sử dụng số học số nguyên, bước này giống với số học dấu phẩy động, ngoại trừ bạn phải thay thế toán tử dấu phẩy động bằng toán tử số nguyên tương ứng bằng ngôn ngữ có toán tử khác nhau.

Giả sử bạn muốn tìm số nguyên-k-th của a > 0, số này phải là số nguyên lớn nhất r sao cho r^k <= a. Bạn bắt đầu với bất kỳ số nguyên dương nào (dĩ nhiên điểm bắt đầu tốt sẽ giúp).

int_type step(int_type k, int_type a, int_type x) { 
    return ((k-1)*x + a/x^(k-1))/k; 
} 

int_type root(int_type k, int_type a) { 
    int_type x = 1, y = step(k,a,x); 
    do { 
     x = y; 
     y = step(k,a,x); 
    }while(y < x); 
    return x; 
} 

Ngoại trừ bước đầu tiên, bạn có x == r <==> step(k,a,x) >= x.

+0

Sau khi nhìn lại tại newton raphson, tôi thấy có một cách để làm điều đó, nhưng nó thường đạt đến một điểm được lật giữa hai con số (ví dụ căn bậc hai của 15 flips giữa 3 và 4). Để truy cập cho giải pháp đầy đủ này là [ở đây] (http://pastebin.com/3UbgNMHG) – Matt

+0

Đối với căn bậc hai, nó lật chính xác cho 'a = n * n-1' giữa' n-1' và 'n' . Tôi không chắc chắn nếu cho quyền hạn cao hơn có nhiều giá trị dẫn đến lật, nhưng bất cứ khi nào bước tăng xấp xỉ đến gốc - ngoại trừ bước đầu tiên, nếu điểm khởi đầu nhỏ hơn mục tiêu - bạn đã hoàn tất, giá trị nhỏ hơn là số nguyên gốc. –

+0

Đó là cùng một kết luận tôi đã đạt được, đó là lý do tại sao tôi đến mã được đăng trong bình luận ở trên của tôi. Bất kể cơ sở các giá trị mà flip dường như luôn luôn ở trên và dưới gốc, do đó, gốc là inbetween hai con số (do đó tại sao nó flips) mã của tôi giao dịch với điều đó. – Matt

3

Một giải pháp dễ dàng là sử dụng tìm kiếm nhị phân.

Giả sử chúng tôi đang tìm gốc thứ n của x.

Function GetRange(x,n): 
    y=1 
    While y^n < x: 
     y*2 
    return (y/2,y) 

Function BinSearch(a,b,x,): 
    if a == b+1: 
     if x-a^n < b^n - x: 
      return a 
     else: 
      return b 
    c = (a+b)/2 
    if n< c^n: 
     return BinSearch(a,c,x,n) 
    else: 
     return BinSearch(c,b,x,n) 

a,b = GetRange(x,n) 
print BinSearch(a,b,x,n) 

=== Version nhanh hơn ===

Function BinSearch(a,b,x,): 
    w1 = x-a^n 
    w2 = b^n - x 
    if a <= b+1: 
     if w1 < w2: 
      return a 
     else: 
      return b 
    c = (w2*a+w1*b)/(w1+w2) 
    if n< c^n: 
     return BinSearch(a,c,x,n) 
    else: 
     return BinSearch(c,b,x,n) 
6

Một cách rõ ràng sẽ được sử dụng cùng với binary searchexponentiation by squaring. Điều này sẽ cho phép bạn tìm thấy nthRoot(x, n) trong O(log (x + n)): tìm kiếm nhị phân trong [0, x] cho số nguyên lớn nhất k sao cho k^n <= x. Đối với một số k, nếu k^n <= x, hãy giảm tìm kiếm thành [k + 1, x], nếu không hãy giảm số này thành [0, k].

Bạn có yêu cầu điều gì đó thông minh hơn hoặc nhanh hơn không?

+0

Tôi quan tâm để xem nếu có bất kỳ phương pháp mà không liên quan đến thử nghiệm một cải tiến. Mặc dù lũy thừa bởi bình phương là một cảm ơn tìm kiếm tốt, – Matt

4

Dường như với tôi rằng Shifting nth root algorithm cung cấp chính xác những gì bạn muốn:

Thuật toán gốc thứ n chuyển là một thuật toán để giải nén vào thư mục gốc thứ n của một số thực dương mà tiến hành lặp đi lặp lại bằng cách thay đổi trong n chữ số radicand, bắt đầu với ý nghĩa quan trọng nhất, và tạo ra một chữ số của gốc trên mỗi lần lặp, theo cách tương tự như phân chia dài.

Có các ví dụ được làm việc trên trang wikipedia được liên kết.

+0

Từ trang wikipedia: "Khi cơ sở lớn hơn radicand, thuật toán thoái hóa thành tìm kiếm nhị phân". Tôi sẽ có một cái nhìn nếu có thể làm việc với (hiệu quả) hex hơn là nhị phân sẽ cải thiện các thuật toán. – Matt

0

Thuật toán đơn giản hơn trong VBA.

Public Function RootNth(radicand As Double, degree As Long) As Double 
    Dim countDigits As Long, digit As Long, potency As Double 
    Dim minDigit As Long, maxDigit As Long, partialRadicand As String 
    Dim totalRadicand As String, remainder As Double 

    radicand = Int(radicand) 
    degree = Abs(degree) 
    RootNth = 0 
    partialRadicand = "" 
    totalRadicand = CStr(radicand) 
    countDigits = Len(totalRadicand) Mod degree 
    countDigits = IIf(countDigits = 0, degree, countDigits) 
    Do While totalRadicand <> "" 
    partialRadicand = partialRadicand + Left(totalRadicand, countDigits) 
    totalRadicand = Mid(totalRadicand, countDigits + 1) 
    countDigits = degree 
    minDigit = 0 
    maxDigit = 9 
    Do While minDigit <= maxDigit 
     digit = Int((minDigit + maxDigit)/2) 
     potency = (RootNth * 10 + digit)^degree 
     If potency = Val(partialRadicand) Then 
      maxDigit = digit 
      Exit Do 
     End If 
     If potency < Val(partialRadicand) Then 
      minDigit = digit + 1 
     Else 
      maxDigit = digit - 1 
     End If 
    Loop 
    RootNth = RootNth * 10 + maxDigit 
    Loop 
    End Function 
+0

"Đơn giản hơn"? –

Các vấn đề liên quan