Căn bậc hai của số nguyên [đã đóng]


12

Vấn đề:

Trong lựa chọn ngôn ngữ của bạn, hãy viết hàm ngắn nhất trả về sàn của căn bậc hai của một số nguyên 64 bit không dấu.

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

Chức năng của bạn phải hoạt động chính xác cho tất cả các đầu vào, nhưng đây là một số giúp minh họa ý tưởng:

               INPUT ⟶ OUTPUT

                   0 ⟶  0
                   1 ⟶  1
                   2 ⟶  1
                   3 ⟶  1
                   4 ⟶  2
                   8 ⟶  2
                   9 ⟶  3
                  15 ⟶  3
                  16 ⟶  4
               65535 ⟶ 255
               65536 ⟶ 256
18446744073709551615 ⟶ 4294967295

Quy tắc:

  1. Bạn có thể đặt tên cho chức năng của bạn bất cứ điều gì bạn thích. (Các chức năng không tên, ẩn danh hoặc lambda đều ổn, miễn là chúng có thể gọi được bằng cách nào đó.)
  2. Số lượng nhân vật là điều quan trọng nhất trong thử thách này, nhưng thời gian chạy cũng rất quan trọng. Tôi chắc chắn rằng bạn có thể quét ngược lên để tìm câu trả lời trong thời gian O (√n) với số lượng ký tự rất nhỏ, nhưng thời gian O (log (n)) sẽ thực sự tốt hơn (nghĩa là giả sử giá trị đầu vào là n, không phải là một bit dài của n).
  3. Bạn có thể sẽ muốn thực hiện hàm bằng cách sử dụng hoàn toàn số nguyên và / hoặc số học boolean. Tuy nhiên, nếu bạn thực sự muốn sử dụng các phép tính dấu phẩy động, thì điều đó cũng tốt miễn là bạn gọi không có chức năng thư viện. Vì vậy, chỉ cần nói return (n>0)?(uint32_t)sqrtl(n):-1;trong C là vượt quá giới hạn mặc dù nó sẽ tạo ra kết quả chính xác. Nếu bạn đang sử dụng dấu chấm động số học, bạn có thể sử dụng *, /, +, -, và lũy thừa (ví dụ, **hoặc ^nếu đó là một built-in điều hành bằng ngôn ngữ của bạn lựa chọn, nhưng chỉ lũy thừa quyền lực không nhỏ hơn 1 ). Hạn chế này là để ngăn chặn "gian lận" bằng cách gọi sqrt()hoặc một biến thể hoặc nâng giá trị lên sức mạnh.
  4. Nếu bạn đang sử dụng các phép toán dấu phẩy động (xem # 3), bạn không yêu cầu loại trả về là số nguyên; chỉ có điều rằng giá trị trả về là một số nguyên, ví dụ: floor (sqrt (n)) và có thể giữ bất kỳ giá trị 32 bit không dấu nào.
  5. Nếu bạn đang sử dụng C / C ++, bạn có thể giả sử sự tồn tại của các loại số nguyên 64 bit và 32 bit không dấu, ví dụ, uint64_tuint32_tnhư được định nghĩa trong stdint.h. Mặt khác, chỉ cần đảm bảo loại số nguyên của bạn có khả năng chứa bất kỳ số nguyên không dấu 64 bit nào.
  6. Nếu ngôn ngữ của bạn không hỗ trợ số nguyên 64 bit (ví dụ: Brainfuck rõ ràng chỉ có hỗ trợ số nguyên 8 bit), thì hãy cố gắng hết sức với điều đó và nêu giới hạn trong tiêu đề câu trả lời của bạn. Điều đó nói rằng, nếu bạn có thể tìm ra cách mã hóa số nguyên 64 bit và lấy chính xác căn bậc hai của nó bằng số học nguyên thủy 8 bit, thì sẽ tiếp thêm sức mạnh cho bạn!
  7. Hãy vui vẻ và sáng tạo!

7
"nhưng thời gian O (log₄ (n)) sẽ thực sự tốt hơn." - tốt hơn bao nhiêu? Có tiền thưởng không? Đó có phải là một yêu cầu khó khăn? Đây có phải là một thách thức riêng biệt? Có phải đó chỉ là một ý tưởng hay mà thực sự không ảnh hưởng đến việc ghi bàn?
John Dvorak

3
Thông thường người ta sử dụng kích thước của đầu vào chứ không phải giá trị đầu vào để rút ra độ phức tạp thuật toán. Theo nghĩa đó, thuật toán tăng và thử lại là theo cấp số nhân về tốc độ.
John Dvorak

3
Umm ... O(log_2 n) === O(log_4 n). log_4(n) = log_2(n) / log_2(2) = log_2(n) / 2
John Dvorak

1
Có 2/4 không?
Milo

1
Hầu hết các kiểu dữ liệu dấu phẩy động không có độ chính xác cần thiết cho tác vụ này. 53 bit đáng kể không đủ cho toàn bộ phạm vi đầu vào.
user2357112 hỗ trợ Monica

Câu trả lời:


14

CJam, 17 (hoặc 10) byte

{_1.5#\/i}

Dùng thử trực tuyến bằng cách xác minh các trường hợp thử nghiệm:

[0 1 2 3 4 8 9 15 16 65535 65536 18446744073709551615]{_1.5#\/i}%N*

Nó sẽ không vượt qua trường hợp thử nghiệm cuối cùng vì các vấn đề làm tròn, nhưng vì 18446744073709551615không phải là Số nguyên trong CJam (đó là Số nguyên lớn ), chúng tôi vẫn tốt, phải không?

Nếu không, đoạn mã sau (và dài hơn một chút) sẽ sửa các lỗi đó:

{__1.5#\/i_2#@>-}

Không phải là giải pháp ngắn nhất nữa, nhưng faaast .

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

__    " Duplicate the integer twice. ";
1.5#  " Raise to power 1.5. Note that, since 1.5 > 1, this doesn't break the rules. ";
\     " Swap the result with the original integer. ";
/     " Divide. ";
i     " Cast to integer. ";
_2#   " Push square of a copy. ";
@     " Rotate the orginal integer on top of the stack. ";
>-    " If the square root has been rounded up, subtract 1. ";

Hahaha! Rất tiếc, ok, bạn đã cho tôi một kỹ thuật ở đó. Tôi nên nói không có quyền hạn phân số. Nhưng mã của bạn thực sự tuân theo các quy tắc đã nêu, vì vậy tôi ủng hộ nó. :)
Todd Lehman

2
Liệu CJam có số thập phân chính xác tùy ý, để bao quát toàn bộ phạm vi đầu vào?
isaacg

Ngoài ra, hack đẹp khi sử dụng NaN -> 0 trên cast sang int.
isaacg

Ý tưởng gọn gàng, nó cũng có thể được biểu diễn trong J trong cùng một số ký tự : <.@%~^&1.5. Tôi có thể đăng câu hỏi này dưới dạng câu trả lời riêng biệt không (vì về cơ bản nó là một cổng chính xác của bạn)?
ɐɔıʇǝɥʇuʎs

@ ıʇǝɥʇuʎs: Đi tiếp. Nhưng tôi chỉ tìm ra rằng giải pháp của tôi có thể làm tròn không chính xác cho số lượng lớn, bao gồm cả trường hợp thử nghiệm cuối cùng. Để bảo vệ tôi, nó đã vượt qua sự kiểm tra của tôi chỉ vì 42949672954294967296 trông rất giống nhau ...
Dennis

10

Haskell, 28 26

Tôi tin rằng đây là mục nhập ngắn nhất từ ​​bất kỳ ngôn ngữ nào không được thiết kế để chơi gôn.

s a=[x-1|x<-[0..],x*x>a]!!0

Nó đặt tên một hàm svới tham số avà trả về một trừ đi số đầu tiên có bình phương lớn hơn a. Chạy rất chậm (O (sqrt n), có thể?).


1
Sẽ không một chỉ mục danh sách ( [...]!!0) ngắn hơn đầu?
isaacg

@isaacg Vâng, nó sẽ. Cảm ơn :-)
Zaq

7

Golf, 17 ký tự

{).,{.*1$<},,\;(}

Tôi có thể đặt tên cho chức năng của mình theo bất kỳ cách nào tôi thích, nhưng tôi quyết định không đặt tên cho nó. Thêm hai ký tự để đặt tên cho nó, thêm ba ký tự để đặt tên và không để nó trên ngăn xếp, trừ đi một ký tự nếu cung cấp một chương trình đầy đủ là OK.

Sự ghê tởm này chạy không theo thời gian logarit trong giá trị của đầu vào, không phải trong thời gian O (sqrt n), phải mất một lượng thời gian tuyến tính để tạo ra kết quả. Nó cũng mất nhiều không gian. Hoàn toàn khủng khiếp. Nhưng ... đây là môn đánh gôn.

Thuật toán là:

n => [0..n].filter(x => x*x < n+1).length - 1

Tôi thích nó!! Công việc tốt đẹp! Đó là đẹp hư hỏng.
Todd Lehman

7

Pyth , 14 ký tự

DsbR;fgb*TTL'b

Cung cấp một hàm được đặt tên, s, tính toán căn bậc hai bằng cách lọc danh sách từ 0 đến n cho hình vuông lớn hơn đầu vào, sau đó in số cuối cùng như vậy. Sử dụng không lũy ​​thừa hoặc nổi.

Dsb       def s(b):
R;        return last element of
f         filter(lambda T:
gb*TT                     b>=T*T,
L'b                       range(b+1))

Ví dụ sử dụng:

python3 pyth.py <<< "DsbR;fgb*TTL'b       \msd[0 1 2 3 4 8 9 15 16 65535 65536"
[0, 1, 1, 1, 2, 2, 3, 3, 4, 255, 256]

7

Retina (không cạnh tranh - Ngôn ngữ mới hơn thử thách), 43

Trong khi thực hiện câu trả lời này , tôi nhận thấy rằng một phương pháp tương tự có thể được sử dụng để tính căn bậc hai số nguyên bằng võng mạc:

.+
$*
^
1:
+`(1+):(11\1)
1 $2:
1+:$|:1+

1+

Điều này phụ thuộc vào thực tế là các hình vuông hoàn hảo có thể được biểu thị bằng 1+3+5+7+..., và do hệ quả là số lượng thuật ngữ trong biểu thức này là căn bậc hai.

Hãy thử trực tuyến. (Dòng đầu tiên được thêm vào để cho phép nhiều testcase được chạy.)

Rõ ràng do chuyển đổi thập phân sang đơn nguyên, điều này sẽ chỉ hoạt động cho các đầu vào tương đối nhỏ.


4
(Ngôn ngữ mới hơn thử thách)
mbomb007

@ mbomb007 Đủ công bằng - Tiêu đề được chỉnh sửa. Câu trả lời này chắc chắn nằm trong danh mục "bởi vì nó có thể được thực hiện", và không có nghĩa là cạnh tranh trong thử thách theo bất kỳ cách có ý nghĩa nào.
Chấn thương kỹ thuật số


6

Perl, 133 ký tự

Không phải là ngắn nhất cho đến nay, nhưng sử dụng thuật toán từng chữ số để xử lý bất kỳ kích thước đầu vào nào và chạy trong thời gian O (log n). Chuyển đổi tự do giữa các số dưới dạng chuỗi và số dưới dạng số. Vì sản phẩm lớn nhất có thể là gốc từ trước đến nay với bình phương của một chữ số, nên nó có thể lấy căn bậc hai của các số lên tới 120 bit hoặc hơn trên hệ thống 64 bit.

sub{($_)=@_;$_="0$_"if(length)%2;$a=$r="";while(/(..)/g){
$a.=$1;$y=$d=0;$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for 1..9;$r.=$d;$a-=$y}$r}

Giải nén, đó là:

sub {
  my ($n) = @_;
  $n = "0$n" if length($n) % 2; # Make an even number of digits
  my ($carry, $root);
  while ($n =~ /(..)/g) { # Take digits of $n two at a time
    $carry .= $1;         # Add them to the carry
    my ($product, $digit) = (0, 0);
    # Find the largest next digit that won't overflow, using the formula
    # (10x+y)^2 = 100x^2 + 20xy + y^2 or
    # (10x+y)^2 = 100x^2 + y(20x + y)
    for my $trial_digit (1..9) {
      my $trial_product = $trial_digit * (20 * $root + $trial_digit);
      if ($trial_product <= $carry) {
        ($product, $digit) = ($trial_product, $trial_digit);
      } 
    } 
    $root .= $digit;
    $carry -= $product;
  } 
  return $root;
}

Đẹp! Tôi đã tự hỏi khi ai đó sẽ đăng một câu trả lời Perl. BTW, nó hoạt động để nói if length%2thay vì if(length)%2? Điều đó sẽ cạo đi 1 ký tự. Ngoài ra, nó sẽ làm việc để nói $y=$z,$d=$_ ifthay vì ($y,$d)=($z,$_)if? Tôi nghĩ rằng sẽ loại bỏ thêm 3 nhân vật.
Todd Lehman

Và điều này đang trở nên hơi sai lầm, nhưng tôi nghĩ bạn có thể loại bỏ thêm 1 lần nữa bằng cách viết lại forvòng lặp như sau:$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
Todd Lehman

Gợi ý đầu tiên không hoạt động (nó cố gắng lấy độ dài của hàm băm có tên %2), nhưng các gợi ý khác là hợp lệ. Tôi sẽ làm việc với họ.
hobbs 28/07 '

1
@ToddLehman postfix forkhông cần dấu ngoặc đơn; thêm vào đó để đề xuất của bạn tổng cộng 6 ký tự. Cảm ơn!
hobbs

5

Matlab (56) / Octave (55)

Nó hoạt động ra căn bậc hai bằng cách sử dụng một phương pháp điểm cố định. Nó hội tụ tối đa 36 bước (đối với 2 ^ 64-1 làm đối số) và sau đó kiểm tra xem đó có phải là một trong các số nguyên 'có thể' thấp hơn không. Vì nó luôn sử dụng 36 lần lặp nên nó có thời gian chạy là O (1) = P

Đối số được giả định là uint64.

Matlab:

function x=q(s)
x=1
for i = 1:36
    x = (x+s/x)/2
end
if x*x>s
    x=x-1
end

Octave:

function x=q(s)
x=1
for i = 1:36
    x = (x+s/x)/2
end
if x*x>s
    x-=1
end

Đây là một phương pháp mới với tôi, và nó rất tuyệt. +1
xem

1
Về cơ bản, nó là en.wikipedia.org/wiki/ , đây là một trong những phương pháp số được biết đến sớm nhất được ước tính là khoảng 3700 năm tuổi. Nó có thể được chứng minh bằng en.wikipedia.org/wiki/Banach_fixed-point_theorem có một bằng chứng dễ dàng đáng ngạc nhiên, nó thực sự tốt đẹp =)
flawr

5

Ruby - 36 ký tự

s=->n{g=n;g=(g+n/g)/2 while g*g>n;g}

Làm tốt lắm! Thời gian thực hiện trường hợp xấu nhất là gì?
Todd Lehman

Thế còn trong trường hợp g * g <n và câu trả lời vẫn không gần với giá trị mong muốn thì sao? Kịch bản không dừng lại à?
WallyWest

1
@ToddLehman Thật lòng tôi không biết. : - / Đây là phương pháp Babylon . Đây là những gì dường như là một bằng chứng tốt về độ phức tạp trung bình . Dự đoán ban đầu về con số này khá tệ, nhưng tôi cần phải ngồi xuống và thực sự mò mẫm bằng chứng đó để hiểu trường hợp xấu nhất. Sẽ cho nó đi khi tôi có thêm thời gian rảnh. :-)
OI

@WallyWest Hiểu biết của tôi là whilevòng lặp chấm dứt chính xác khi g hội tụ đến sàn (√n) là giá trị mong muốn. Bạn có thấy trường hợp nào điều này không đúng?
OI

4

Con trăn (39)

f=lambda n,k=0:k*k>n and k-1or f(n,k+1)

Cách tiếp cận đệ quy tự nhiên. Đếm các căn bậc hai tiềm năng cho đến khi hình vuông của chúng quá cao, sau đó giảm xuống 1. Sử dụng Stackless Python nếu bạn lo lắng về việc vượt quá độ sâu ngăn xếp.

Thành and/orngữ này tương đương với toán tử ternary như

f=lambda n,k=0:k-1 if k*k>n else f(n,k+1)

Chỉnh sửa: Tôi thay vì có thể nhận được 25 ký tự bằng cách khai thác sự cai trị "bạn có thể sử dụng *, /, +, -, và lũy thừa (ví dụ, **hoặc ^nếu đó là một built-in điều hành bằng ngôn ngữ của bạn lựa chọn, nhưng chỉ có lũy thừa quyền lực không nhỏ hơn 1). " (Chỉnh sửa: Rõ ràng Dennis đã tìm thấy và khai thác thủ thuật này.)

lambda n:n**1.5//max(n,1)

Tôi sử dụng toán tử chia số nguyên //của Python 3 để làm tròn xuống. Thật không may, tôi dành rất nhiều ký tự cho trường hợp n=0không cho phép chia cho 0 lỗi. Nếu không, tôi có thể làm 18 ký tự

lambda n:n**1.5//n 

Các quy tắc cũng không nói hàm phải được đặt tên (tùy theo cách bạn diễn giải "Bạn có thể đặt tên cho hàm của mình theo bất cứ thứ gì bạn thích."), Nhưng nếu có, đó là hai ký tự nữa.


- Cảm ơn, tôi sẽ làm rõ điều đó. Nó chỉ phải là một chức năng. Nó không phải được đặt tên. Vì vậy, chức năng lambda là tốt. Tôi đã đề cập đến điều này ngay từ đầu nếu tôi nghĩ về nó. Tôi đã suy nghĩ quá nhiều về C khi tôi đăng câu hỏi.
Todd Lehman

4

C99 (58 ký tự)

Đây là một ví dụ về một câu trả lời mà tôi sẽ không coi là một câu trả lời hay, mặc dù nó thú vị đối với tôi từ quan điểm golf mã bởi vì nó quá đồi trụy, và tôi chỉ nghĩ rằng sẽ rất vui khi ném vào hỗn hợp:

Bản gốc: 64 ký tự

uint64_t r(uint64_t n){uint64_t r=1;for(;n/r/r;r++);return r-1;}

Lý do này là khủng khiếp là vì nó chạy trong thời gian O (n) chứ không phải thời gian O (log (n)). (Trong đó n là giá trị đầu vào.)

Chỉnh sửa: 63 ký tự

Thay đổi r-1thành --rvà đưa nó vào return:

uint64_t r(uint64_t n){uint64_t r=1;for(;n/r/r;r++);return--r;}

Chỉnh sửa: 62 ký tự

Di chuyển gia số vòng lặp vào bên trong phần có điều kiện của vòng lặp (lưu ý: điều này có hành vi không được bảo vệ vì thứ tự các thao tác đối với toán tử preincrement là dành riêng cho trình biên dịch):

uint64_t r(uint64_t n){uint64_t r=0;for(;n/++r/r;);return--r;}

Chỉnh sửa: 60 ký tự

Thêm một typedefđể ẩn uint64_t(tín dụng cho người dùng technosaurus cho đề xuất này).

typedef uint64_t Z;Z r(Z n){Z r=0;for(;n/++r/r;);return--r;}

Chỉnh sửa: 58 ký tự

Bây giờ yêu cầu tham số thứ hai được truyền là 0 trong lệnh gọi hàm, ví dụ, r(n,0)thay vì chỉ r(n). Ok, đối với cuộc sống của tôi, tại thời điểm này tôi không thể thấy làm thế nào để nén điều này thêm nữa ... bất cứ ai?

typedef uint64_t Z;Z r(Z n,Z r){for(;n/++r/r;);return--r;}

Nếu bạn sẵn sàng gọi nó là C ++ và giảm dần thay vì tăng, bạn sẽ có thể loại bỏ một vài ký tự : uint64_t s(uint64_t n){for(uint64_t r=n;--n>r/n;);return n;}.
Dành cho

@Fors - Cách tiếp cận tốt đẹp! Thật không may, điều đó có gây ra sự chia rẽ cho đầu vào 1 không? Ngoài ra, nó sẽ làm gì cho đầu vào bằng 0? Bởi vì --nkhi nào n==0sẽ là 1, và đây là những giá trị không dấu, vì vậy ,1 sẽ là 2⁶⁴.
Todd Lehman

1
#define Z uint64_t ... hoặc typedef sẽ cứu một cặp vợ chồng
technosaurus

@technosaurus - À đúng, tiết kiệm được 2. Cảm ơn bạn. :-)
Todd Lehman

1
Biểu thức n/++r/rcó hành vi không xác định ....
aschepler

4

Golfscript - 14 ký tự

{.,\{\.*<}+?(}

Tìm số nhỏ nhất iít hơn đầu vào nn < i*i. Trở vềi - 1 .

I E [0..n-1].first(i => n < i*i) - 1

Giải thích cho những người không biết Golfscript, cho cuộc gọi mẫu với đầu vào 5:

.        //Duplicate input.  Stack: 5 5
,        //Get array less than top of stack.  Stack: 5 [0 1 2 3 4]
\        //Switch top two elements of stack.  Stack: [0 1 2 3 4] 5
{\.*<}+  //Create a block (to be explained), and prepend the top of the stack.  
         //Stack: [0 1 2 3 4]{5\.*<}
?        //Find the first element of the array for which the block is true. 
         //So, find the first element of [0 1 2 3 4] for which {5\.*<} evaluates to true.
         //The inner block squares a number and returns true if it is greater than the input.
(        //Decrement by 1 

Ồ, đó là 3 ký tự ngắn hơn câu trả lời Golfscript tốt nhất trước đó. Công việc tốt đẹp!
Todd Lehman

Sửa lỗi này để đưa ra câu trả lời chính xác cho đầu vào 1có thể mất hai ký tự.
Peter Taylor

4

Haskell, 147 138 134 128 byte

Không phải là mã ngắn nhất trên thế giới, nhưng nó chạy trong O (log n) và trên các số có kích thước tùy ý:

h x=div(x+1)2
n%(g,s)|g*g<n=(g+s,h s)|g*g>n=(g-s,h s)|0<1=(g,0)
f(x:r@(y:z:w))|x==z=min x y|0<1=f r
s n=fst$f$iterate(n%)(n,h n)

Điều này thực hiện tìm kiếm nhị phân của phạm vi [0..n] để tìm xấp xỉ thấp nhất tốt nhất cho sqrt (n). Đây là một phiên bản chưa được chỉnh sửa:

-- Perform integer division by 2, rounding up
half x = x `div` 2 + x `rem` 2

-- Given a guess and step size, refine the guess by adding 
-- or subtracting the step as needed.  Return the new guess
-- and step size; if we found the square root exactly, set
-- the new step size to 0.
refineGuess n (guess, step)
    | square < n  =  (guess + step, half step)
    | square > n  =  (guess - step, half step)
    | otherwise   =  (guess, 0)
    where square = guess * guess     

-- Begin with the guess sqrt(n) = n and step size (half n),
-- then generate the infinite sequence of refined guesses.
-- 
-- NOTE: The sequence of guesses will do one of two things:
--         - If n has an integral square root m, the guess 
--           sequence will eventually be m,m,m,...
--         - If n does not have an exact integral square root,
--           the guess sequence will eventually alternate
--           L,U,L,U,.. between the integral lower and upper
--           bounds of the true square root.
--        In either case, the sequence will reach periodic
--        behavior in O(log n) iterations.
guesses n = map fst $ iterate (refineGuess n) (n, half n)

-- Find the limiting behavior of the guess sequence and pick out
-- the lower bound (either L or m in the comments above)
isqrt n = min2Cycle (guesses n)
    where min2Cycle (x0:rest@(x1:x2:xs))
            | x0 == x2    =   min x0 x1
            | otherwise   =   min2Cycle rest

Chỉnh sửa: Đã lưu hai byte bằng cách thay thế các mệnh đề "khác" bằng "0 <1" dưới dạng phiên bản ngắn hơn của "True" và một vài điều nữa bằng cách nội tuyến g * g.

Ngoài ra, nếu bạn hài lòng với O (sqrt (n)), bạn có thể chỉ cần làm

s n=(head$filter((>n).(^2))[0..])-1

cho 35 ký tự, nhưng có gì vui?

Chỉnh sửa 2: Tôi mới nhận ra rằng các cặp được sắp xếp theo thứ tự từ điển, thay vì thực hiện min2Cycle. bản đồ fst, tôi chỉ có thể làm fst. min2Cycl. Trong mã đánh gôn, có nghĩa là thay thế f $ map fst bằng fst $ f, tiết kiệm thêm 4 byte.

Chỉnh sửa 3: Đã lưu thêm sáu byte nhờ người tự hào!


1
bạn có thể thay thế (div x 2 + rem x 2) bằng div (x + 1) 2, tại chức năng "một nửa" của bạn
tự hào

Tôi thực sự có một giải pháp của riêng mình với 49 ký tự và giải quyết trong O (log n), nhưng tôi chỉ có 2
upvote ;-(

4

JavaScript 91 88 86: Tối ưu hóa tốc độ

function s(n){var a=1,b=n;while(Math.abs(a-b)>1){b=n/a;a=(a+b)/2}return Math.floor(a)}

JavaScript 46: Không được tối ưu hóa cho tốc độ

function s(n){a=1;while(a*a<=n)a++;return a-1}

Đây là một JSFiddle: http://jsfiddle.net/rmadhuram/1Lnjuo4k/


1
Chào mừng đến với PPCG! Bạn có thể sử dụng <s> 91 </ s> <s> 88 </ s> để vượt qua. Tôi đã thử thực hiện chỉnh sửa nhưng bạn đang chỉnh sửa cùng một lúc nên tôi sẽ cho phép bạn thực hiện.
Rainbolt

1
Hoặc bạn có thể làm điều đó trong 41 ký tự như thế này:function s(n){for(a=1;++a*a<n;);return a}
hoàng Custard

4

C 95 97

Chỉnh sửa Typedef, được đề xuất bởi @Michaelangelo

Điều này nên ít nhiều là một triển khai đơn giản của thuật toán Heron. Cách giải quyết duy nhất là trong tính toán mức tràn số nguyên trung bình: a = (m + n) / 2 không hoạt động đối với các số biiiig.

typedef uint64_t Z;
Z q(Z x)
{
   Z n=1,a=x,m=0;
   for(;a-m&&a-n;) n=a,m=x/n,a=m/2+n/2+(m&n&1);
   return a;
}

Công việc tuyệt vời với tính năng tránh tràn tràn không chỉ dành cho việc thực hiện chính xác mà còn quan tâm đến việc suy nghĩ về nó ngay từ đầu và kiểm tra nó. Chắc chắn đánh giá cao.
Todd Lehman

BTW, thật buồn cười khi phân chia đắt tiền có thể được trên một số CPU. Mặc dù thuật toán này thực thi trong khoảng một nửa bước so với thuật toán bàn tính, nó có thời gian chạy chậm hơn khoảng 5 lần so với thuật toán bàn tính khi tôi điểm chuẩn nó trên CPU Core i7 của tôi, không thích phân chia. Dù sao, nhưng thời gian chạy không quan trọng ở đây - chỉ kích thước. :) Làm tốt lắm !!!
Todd Lehman

4

C # 64 62 55

Vì đây là (và tôi rất tệ với môn toán) và thời gian chạy chỉ là một gợi ý, tôi đã thực hiện cách tiếp cận ngây thơ chạy trong thời gian tuyến tính:

decimal f(ulong a){var i=0m;while(++i*i<=a);return--i;}

( thử nghiệm trên dotnetfiddle )

Tất nhiên, nó cực kỳ chậm đối với đầu vào lớn hơn.


1
Bạn có thể cạo sạch một nhân vật bằng cách thay đổi return i-1thành return--i?
Todd Lehman

Trong biểu thức i*i<=a, điều đó có được đảm bảo là số học nguyên của loại thông thường không? (Tôi không quen thuộc với C #.) Nếu vậy, và nếu C # cho phép chuyển đổi số nguyên thành boolean giống như C, thì bạn có thể lưu thêm một ký tự bằng cách thay đổi nó thành a/i/i.
Todd Lehman

1
@ToddLehman Điều đó thực sự là số học điểm cố định ( Decimal, giá trị tối đa và độ chính xác cao hơn), để tránh tràn vì kết quả nhân có thể có khả năng đi qua một bước UInt64.MaxValue. Nhưng C # không có chuyển đổi ngầm định thành Boolean. Tôi có thể thay đổi returnmặc dù, cảm ơn. Tôi sẽ làm điều đó khi tôi quay trở lại máy tính.
Bob

3

Clojure - 51 hoặc 55 byte

Kiểm tra tất cả các số từ n đến 0, đưa ra số đầu tiên trong đó x^2 <= n. Thời gian chạy làO(n - sqrt n)

Vô danh:

(fn[x](first(filter #(<=(* % %)x)(range x -1 -1))))

Được đặt tên:

(defn f[x](first(filter #(<=(* % %)x)(range x -1 -1))))

Thí dụ:

(map (fn[x](first(filter #(<=(* % %)x)(range x -1 -1)))) (range 50))
=> (0 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 7)

3

Befunge 93 - 48 byte hoặc 38 ký tự

101p&02p>02g01g:*`#v_01g1-.@
        ^  p10+1g10<        

Hãy thử nó ở đây.


1
Ok, đó chỉ là mát mẻ. Công việc tốt đẹp! Tôi đã nhập 17, nhấp vào Creep và sau đó chạy, và nó đã xuất hiện với 4! :)
Todd Lehman

3

Rắn hổ mang - 62

do(n as uint64)as uint64
    o=n-n
    while o*o<n,o+=1
    return o

Mẻ - 74

set a=0
:1
set /ab=%a%*%a%
if %b% LSS %1 set /aa=%a%+1&goto 1
echo %a%

3

Haskell, 53 50 49 ký tự, O (log n)

s n=until((<=n).(^2))(\g->g-1-div(g^2-n-1)(2*g))n

giải pháp này thực hiện phương pháp newton-raphson, mặc dù nó tìm kiếm số nguyên thay vì số float. wiki: http://en.wikipedia.org/wiki/Newton%27s_method

sự phức tạp dường như là về O (log n), nhưng có bằng chứng nào về nó không? xin vui lòng trả lời trong các ý kiến.


\g->div(n+g^2)$2*gtiết kiệm 7 byte.
Anders Kaseorg

3

J (10)

Rất, rất, rất lấy cảm hứng từ câu trả lời của @Dennis :

<.@%~^&1.5

Và lâu hơn một chút, nhưng với hiệu suất tốt hơn (tôi nghi ngờ):

<.@(-:&.^.)

floor(halve under log)

Để thực hiện, các phần thụt vào là đầu vào:

   f=:<.@%~^&1.5
   f 0 8 12 16
0 2 3 4
   g=:<.@(-:&.^.)
   g 0 8 12 16
0 2 3 4

Làm thế nào để bạn thực hiện điều này cho một số nguyên nhất định?
Dennis

1

3

APL - 12 ký tự, 19 byte

{⌊(⍵*1.5)÷⍵}

ví dụ sử dụng:

{⌊(⍵*1.5)÷⍵}17

trả về 4

Vectơ kiểm tra

{⌊(⍵*1.5)÷⍵}¨0 1 2 3 4 8 9 15 16 65535 65536 18446744073709551615

trả lại

1 1 1 1 2 2 3 3 4 255 256 4294967296

Thử trực tuyến

Xin chân thành cảm ơn : người dùng "ssdecontrol" cho thuật toán


1
Chào mừng đến với PPCG! Chúng tôi thường chấm điểm APL là một byte cho mỗi ký tự. Trừ khi thử thách chỉ định nó, không cần phải tính vào UTF-8. Bất kỳ mã hóa hiện có nào cũng tốt và có một bảng mã APL cũ từ ngày trở lại sử dụng một byte cho mỗi ký tự. Việc APL có trước ASCII là một lý do tồi để phạt nó vì sử dụng các ký tự không phải ASCII. ;) (Điều đó nói rằng, thử thách khá cũ này dường như được ghi điểm bởi các nhân vật.)
Martin Ender

@MartinEnder Cảm ơn sự chào đón nồng nhiệt và lời khuyên :)
QuantumKarl

1
01! Sử dụng Dyalog APL , bạn có thể đặt ⎕DIV←1(mà nhiều người sử dụng làm mặc định) để có được kết quả chính xác.
Adám

2

C99 (108 ký tự)

Đây là giải pháp của riêng tôi trong C99, được điều chỉnh từ thuật toán trong một bài viết trên Wikipedia . Tôi chắc chắn rằng nó có thể làm tốt hơn nhiều so với điều này trong các ngôn ngữ khác.

Chơi gôn

uint64_t s(uint64_t n){uint64_t b=1,r=0;while(n/b/4)b*=4;for(;b;b/=4,r/=2)n>=r+b?r+=b,n-=r,r+=b:0;return r;}

Chơi gôn một phần:

uint64 uint64_sqrt(uint64 n)
{
  uint64 b = 1, r = 0;
  while (b <= n / 4)
    b *= 4;
  for (; b; b /= 4, r /= 2)
    if (n >= r + b)
      { r += b; n -= r; r+= b; }
  return r;
}

Ung dung:

uint64_t uint64_sqrt(uint64_t const n)
{
  uint64_t a, b, r;

  for (b = 1; ((b << 2) != 0) && ((b << 2) <= n); b <<= 2)
    ;

  a = n;
  r = 0;
  for (; b != 0; b >>= 2)
  {
    if (a >= r + b)
    {
      a -= r + b;
      r = (r >> 1) + b;
    }
    else
    {
      r >>= 1;
    }
  }

  // Validate that r² <= n < (r+1)², being careful to avoid integer overflow,
  // which would occur in the case where n==2⁶⁴-1, r==2³²-1, and could also
  // occur in the event that r is incorrect.
  assert(n>0? r<=n/r : r==0);  // Safe way of saying r*r <= n
  assert(n/(r+1) < (r+1));     // Safe way of saying n < (r+1)*(r+1)

  return r;
}

1
Gợi ý: Không cần a, sử dụng n.
edc65

À đúng rồi. Cảm ơn bạn. Trong phiên bản gốc của tôi, tôi đã duy trì nđể ngay trước khi quay lại tôi có thể đưa ra khẳng định (không hiển thị) rằng r ^ 2 <= n <(r + 1) ^ 2. Với sự khẳng định đó đã bị bỏ qua, cần phải giữ nnguyên vẹn.
Todd Lehman

@ edc65 - Cảm ơn một lần nữa vì đã chỉ ra điều đó. Tôi đã cập nhật mã của mình để phản ánh điều đó, cũng như thêm một vài thủ thuật đánh gôn khác. Cũng đã thêm các xác nhận ban đầu và thực hiện n consttrong phiên bản chưa được chỉnh sửa.
Todd Lehman

2

JavaScript 73 81 (để tuân thủ yêu cầu số 64 bit)

n=prompt();g=n/3;do{G=g,g=(n/g+g)/2}while(1E-9<Math.abs(G-g))alert(Math.floor(g))

Thực hiện thuật toán Heron of Alexandria ...


Đẹp! Điều này có hoạt động cho tất cả các đầu vào số nguyên 64 bit không dấu?
Todd Lehman

Hãy thử vì tôi có thể chỉ hoạt động tối đa 32 bit ...
Thật

Chắc chắn cuối cùng | 0 cắt bất kỳ giá trị nào thành 32 bit. Sử dụng Math.floor thay thế?
edc65

@ edc65 Thực sự bạn đúng, dường như |0ảnh hưởng đến 32 bit trong khi Math.floorhiệu quả hơn ở 64 bit ... Tôi đã cập nhật mã của mình, phải mất thêm 8 ký tự để làm như vậy ...
WallyWest

@ edc65 Tôi vừa có một suy nghĩ ... sẽ ~ ~ x hoạt động trong 64-bit?
WallyWest

2

Powershell (52) Giới hạn ở Int32 (-2,147,483,648 đến 2,147,483,647)

function f($n){($n/2)..0|%{if($_*$_-le$n){$_;exit}}}

Tôi đang hét lên với Powershell ngay bây giờ khi cố gắng làm cho trường hợp thử nghiệm cuối cùng hoạt động nhưng bất kể tôi làm gì, Powershell kết thúc bằng cách sử dụng biến đường ống $ _ như một Int32 và tôi không thể tìm ra cách nào để xử lý nó ngay bây giờ.

Vì vậy, bây giờ tôi sẽ giới hạn câu trả lời của tôi. Nếu tôi có thể tìm ra cách tốt hơn để xử lý uint64, tôi sẽ chỉnh sửa. (Nhân tiện, trường hợp thử nghiệm cuối cùng quá lớn so với loại Int64 bình thường của Powershell!)

Dưới đây là một vài trường hợp thử nghiệm (với một chút đầu ra bổ sung mà tôi đã sử dụng để theo dõi thời gian)

f 17
4
Elapsed Time: 0.0060006 seconds

f 65
8
Elapsed Time: 0.0050005 seconds

f 65540
256
Elapsed Time: 1.7931793 seconds

f 256554
506
Elapsed Time: 14.7395391 seconds

Tôi không biết chữ O () của mình, nhưng đây có vẻ là một bước nhảy khá ấn tượng.


2

Hãy cẩn thận: kể từ năm 2011, R không có hỗ trợ tích hợp cho số nguyên 64 bit như tôi đã giả định. Những câu trả lời này có thể không hợp lệ về tính kỹ thuật đó, nhưng một lần nữa R đã thay đổi rất nhiều trong 3 năm qua.


R, 85

Sử dụng phương pháp của Newton:

function(n){s=F
x=n
y=(1/2)*(x+n/x)
while(abs(x-y)>=1){x=y
y=(1/2)*(x+n/x)}
trunc(y)}

mà hội tụ bậc hai. +2 ký tự để gán hàm cho một biến để đo điểm chuẩn:

microbenchmark(q(113424534523616))
# Unit: microseconds
#                expr    min      lq median      uq    max neval
#  q(113424534523616) 24.489 25.9935 28.162 29.5755 46.192   100

R, 37

Lực lượng vũ phu:

function(n){t=0
while(t^2<n) t=t+1
t}

Và kiểm tra tương tự:

microbenchmark::microbenchmark(q(113424534523616),times=1)
# Unit: seconds
#                 expr      min       lq   median       uq      max neval
#   q(113424534523616) 4.578494 4.578494 4.578494 4.578494 4.578494     1

R, 30

Các giá rẻ / rực rỡ lừa lũy thừa :

function(n) trunc(n^(1.5)/n)

cũng xảy ra rất nhanh (mặc dù không nhanh như tích hợp):

microbenchmark(q(113424534523616),sqrt(113424534523616))
# Unit: nanoseconds
#                   expr min    lq median    uq  max neval
#     z(113424534523616) 468 622.5  676.5 714.5 4067   100
#  sqrt(113424534523616)  93 101.0  119.0 160.5 2863   100

2

C, 38

f(n){int m;while(++m*m<=n);return--m;}

Bản dịch của Forth của tôi. Chậm mà đúng. O (n). Đã thử nghiệm trên OS X (64 bit).


2

dc, 50 byte

dc -e"?dsist[lt2/dstd*li<B]dsBx[lt1+dstd*li!<A]dsAxlt1-f"

Khoảng cách và giải thích:

               # The idea here is to start with the input and reduce it quickly until it is
               # less than what we want, then increment it until it's just right
?              # Take input from stdin
d si st        # Duplicate input, store in `i' and in `t'
[              # Begin macro definition (when I write in dc, "macro"=="function")
 lt            # Load t, our test term
 2/            # Divide t by two
 d st          # Store a copy of this new term in `t'
 d*            # Duplicate and multiply (square)
 li<B          # Load i; if i<(t^2), execute B
] d sB x       # Duplicate, store function as `B', and execute
               # Loop ends when t^2 is less than i
[              # Begin macro definition
 lt            # Load t, our test term
 1+            # Increment
 d st          # Store a copy of this new term in `t'
 d*            # Duplicate and multiply (square)
 li!<A         # Load i; if i>=(t^2), execute A
] d sA x       # Duplicate, store function as `A', and execute
               # Loop ends when t^2 == i+1
lt 1- f        # Load t, decrement, and dump stack

Uh, có vẻ như trường hợp thử nghiệm cuối cùng gặp sự cố. Tôi sẽ cố gắng sửa nó.
Joe

Đã giải quyết. Bây giờ chấp nhận đầu vào rất lớn; Serendipitiously, sửa chữa cho phép tôi loại bỏ một số mã xấu xí lúc đầu.
Joe

2

C, 139 137 136 byte

Lần thử đầu tiên của tôi tại mã golf. Có vẻ như nó ngắn nhất trong C phù hợp với yêu cầu "hiệu quả", khi nó chạy trongO(log n) đúng lúc, chỉ sử dụng phép cộng và dịch chuyển bit. Mặc dù tôi chắc rằng nó có thể ngắn hơn ...

Nó chỉ hoạt động tốt đối với các giá trị nguyên lớn hơn miễn là a=32phần được thay đổi thành a=NUMBITS/2.

typedef uint64_t x;x f(x o){x a=32,t=0,r=0,y=0,z;for(;a--+1;){z=(x)3<<2*a;y*=2;t++<r?y++,r-=t++:t--;t*=2;r*=4;r+=(o&z)>>2*a;}return y;}

Công việc tốt đẹp! Tôi đã không chạy nó để kiểm tra, nhưng mã có vẻ thú vị. Có một lý do bạn đã viết (t++)thay vì chỉ t++trong bài tập đến r?
Todd Lehman

1
@ToddLehman Không, chỉ cần bỏ qua những người đó. Bắt tốt đẹp!
Chris

BTW, tôi yêu các a--+1như một cách để tránh phải viết a-- != UINT64_C(-1). Bạn đã học được mẹo đó ở đâu đó hoặc tự phát minh ra nó?
Todd Lehman

1
@ToddLehman Cảm ơn! Tôi tự tìm ra điều đó.
Chris

1

C - 50 (61 không có toàn cầu)

typedef uint64_t T;T n,i;f(){while(++i*i<=n);--i;}

Nó sử dụng các biến toàn cục làm tham số và trả về giá trị để tiết kiệm không gian.

Không có phiên bản toàn cầu:

typedef uint64_t T;T f(T n){T i=0;while(++i*i<=n);return--i;}

1
Tôi không nghĩ sử dụng các biến toàn cầu là hợp pháp. Ít nhất là cho biết bao lâu nó sẽ hợp pháp và cung cấp một phiên bản hợp pháp
tự hào

@proud haskeller Tại sao các biến toàn cầu sẽ bị cấm?
mantale

@mantal vì bạn phải cung cấp chương trình / phương thức có thể chạy được.
Marciano. Đồng hành

@ Marciano. Mã số được đưa ra là có thể chạy được.
mantale

1

C ++ 125

int main()
{
uint64_t y;cin>>y;
double x=y/2,d,z;
while((d=(x*x-y))>0.5)
{
d<0?x+=0.5:x-=0.5;
}
cout<<(uint64_t)x;
}

Đẹp! Làm thế nào về x+=(d<0)-0.5;... tiết kiệm thêm 5 ký tự?
Todd Lehman

BTW, đây không phải (nhưng nên) ở dạng hàm, như đã đề cập trong báo cáo vấn đề. (Được rồi, về mặt kỹ thuật, vâng, mainlà một chức năng, nhưng nó không thể gọi được từ bên trong một chương trình như thế f(y)này.)
Todd Lehman

Tôi nghĩ rằng bạn có thể bỏ qua cặp dấu ngoặc trong cùng và viết while((d=x*x-y)>0.5)thay vì while((d=(x*x-y))>0.5). Lưu thêm 2 ký tự. :)
Todd Lehman

Mỗi 0,5 có thể được thay đổi thành 0,5
Yytsi
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.