Tamsayı tamsayının karekökü [kapalı]


12

Sorun:

Dil seçiminize, işaretsiz bir 64 bit tamsayının kare kökünün tabanını döndüren en kısa işlevi yazın.

Test senaryoları:

İşlevinizin tüm girdiler için doğru şekilde çalışması gerekir, ancak fikri açıklamaya yardımcı olan birkaç şey aşağıdadır:

               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

Kurallar:

  1. İşlevinize istediğiniz herhangi bir ad verebilirsiniz. (Adsız, anonim veya lambda işlevleri, bir şekilde çağrılabildikleri sürece iyidir.)
  2. Karakter sayımı bu zorlukta en önemli olan şeydir, ancak çalışma zamanı da önemlidir. Eminim çok küçük bir karakter sayısı ile O ()n) zamanında cevap için yinelemeli olarak yukarı doğru tarayabilirsiniz, ancak O (log (n)) süresi gerçekten daha iyi olurdu (yani, n giriş değeri varsayarak, bit uzunluğu n değil).
  3. Büyük olasılıkla işlevi tamamen tamsayı ve / veya boole artitmetiği kullanarak uygulamak isteyeceksiniz. Ancak, gerçekten kayan nokta hesaplamaları kullanmak istiyorsanız, hiçbir kütüphane işlevi çağırmadıkça bu iyi olur. Bu nedenle, return (n>0)?(uint32_t)sqrtl(n):-1;doğru sonucu üretse bile , sadece C'de söylemek sınırların dışındadır. Eğer kayan noktalı aritmetik kullanıyorsanız, bunu yapmak için ise *, /, +, -, ve üs (örn **veya ^bir seçtiğiniz dilde operatör dahili fakat eğer güçlerin sadece üs alma az olmamak 1'den ). Bu kısıtlama çağırarak sqrt()veya bir varyant vererek veya ½ gücüne bir değer yükselterek "aldatmayı" önlemektir .
  4. Kayan nokta işlemleri kullanıyorsanız (bkz. # 3), dönüş türünün tamsayı olması gerekmez; yalnızca dönüş değerinin bir tamsayı olması, örneğin zemin (sqrt (n)) ve imzasız herhangi bir 32 bit değeri tutabilmesi.
  5. C / C ++ kullanıyorsanız, sen mesela işaretsiz 64 bit ve 32 bitlik tamsayı türleri olduğunu farzedelim edebilir uint64_tve uint32_ttanımlanan stdint.h. Aksi takdirde, tamsayı türünüzün 64 bit işaretsiz tamsayıyı tutabildiğinden emin olun.
  6. Diliniz 64 bit tam sayıları desteklemiyorsa (örneğin, Brainfuck görünüşe göre yalnızca 8 bit tam sayı desteğine sahipse), bununla elinizden gelenin en iyisini yapın ve yanıt başlığınızdaki sınırlamayı belirtin. Bununla birlikte, 64 bitlik bir tamsayıyı nasıl kodlayacağınızı ve 8 bitlik ilkel aritmetik kullanarak karekökünü nasıl doğru bir şekilde elde ederseniz, o zaman size daha fazla güç verebilirsiniz!
  7. Eğlenin ve yaratıcı olun!

7
"ama O (log₄ (n)) zamanı gerçekten daha iyi olurdu." - ne kadar iyi? Bir bonus var mı? Bu zor bir gereklilik mi? Aslında ayrı bir meydan okuma mı? Bu sadece puanlamayı gerçekten etkilemeyen güzel bir fikir mi?
John Dvorak

3
Normalde algoritmik karmaşıklığı türetmek için giriş değeri yerine girişin boyutu kullanılır. Bu anlamda artış ve yeniden deneme algoritması hız olarak üsteldir.
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
2/4 sayılır mı?
Milo

1
Çoğu kayan noktalı veri türü, bu görev için gereken kesinliğe sahip değildir. 53 önemli bit tüm girdi aralığı için yeterli değildir.
user2357112 Monica

Yanıtlar:


14

CJam, 17 (veya 10) bayt

{_1.5#\/i}

Test senaryolarını doğrulayarak çevrimiçi deneyin :

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

Yuvarlama sorunları nedeniyle son test durumunu geçmeyecek, ancak CJam'de 18446744073709551615bir Tamsayı olmadığından (bu bir Büyük Tamsayı ), hala iyiyiz, değil mi?

Değilse, aşağıdaki (ve biraz daha uzun) kod bu hataları düzeltir:

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

Artık en kısa çözüm değil, faaast .

Nasıl çalışır

__    " 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! Üzgünüz, tamam, beni orada bir teknikliğe kavuşturdun. Kesirli güçler söylememeliydim. Ancak kodunuz gerçekten belirtilen kurallara uyuyor, bu yüzden onaylıyorum. :)
Todd Lehman

2
CJam'in tüm girdi aralığını kapsayacak şekilde keyfi ondalık basamakları var mı?
isaacg

Ayrıca, int için NaN -> 0 kullanarak güzel kesmek.
isaacg

Temiz bir fikir, o da aynı karakter sayısına J temsil edilebilir: <.@%~^&1.5. Bunu ayrı bir cevap olarak gönderebilir miyim (temelde tam bir portunuz olduğu için)?
Sepıʇǝɥʇuʎs

@ ɐɔıʇǝɥʇuʎs: Devam et. Ancak, çözümümün son test durumu da dahil olmak üzere büyük sayılar için yanlış yuvarlanabileceğini anladım. Savunmamda, denetimimi sadece çünkü geçti 4294967295ve çok benzer 4294967296görünüyor ...
Dennis

10

Haskell, 28 26

Bunun golf için tasarlanmamış herhangi bir dilden en kısa giriş olduğuna inanıyorum.

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

sParametreye sahip bir işlevi adlandırır ave karesi büyük olan ilk eksi bir eksi döndürür a. İnanılmaz derecede yavaş çalışıyor (O (sqrt n), belki?).


1
Bir liste dizini ( [...]!!0) başlıktan daha kısa olmaz mı?
isaacg

@isaacg Evet, olur. Teşekkürler :-)
Zaq

7

Golfscript, 17 karakter

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

İşlevimi istediğim gibi adlandırabilirdim, ama hiç adlandırmamaya karar verdim. Ad vermek için iki karakter ekleyin, adlandırmak için üç karakter ekleyin ve yığın üzerinde bırakmayın, tam bir program sağlanıyorsa bir karakter çıkarın.

Bu iğrençlik, girişin değerinde logaritmik bir zamanda değil, O (sqrt n) zamanında değil, sonucu üretmek için bir boğmaca doğrusal zaman alır. Ayrıca bu kadar yer kaplıyor. Kesinlikle korkunç. Ama ... bu kod golfü.

Algoritma:

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

Onu seviyorum!! İyi iş! Bu çok sapkın.
Todd Lehman

7

Pyth , 14 karakter

DsbR;fgb*TTL'b

Girişten daha büyük olan kare için listeyi 0'dan n'ye filtreleyerek kare kökü hesaplayan adlandırılmış bir işlev (s) sağlar, ardından bu tür son sayıyı yazdırır. Hiçbir üs veya yüzer kullanmaz.

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

Örnek kullanım:

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 (rakipsiz - Dil meydan okumadan daha yeni), 43

Bu cevap üzerinde çalışırken, retina kullanarak tamsayı kare köklerini hesaplamak için benzer bir yöntemin kullanılabileceği bana geldi:

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

1+

Bu, mükemmel karelerin şu şekilde ifade edilebileceğine 1+3+5+7+...ve bu ifadedeki terimlerin sayısının kare kök olduğu sonucuna dayanır .

Çevrimiçi deneyin. (Birden fazla test senaryosunun çalıştırılmasına izin vermek için ilk satır eklendi.)

Açıkçası ondalık ve tekli dönüşüm nedeniyle, bu yalnızca nispeten küçük girdiler için işe yarayacaktır.


4
(Dil mücadeleden daha yeni)
mbomb007

@ mbomb007 Yeterince adil - Başlık düzenlendi. Bu cevap kesinlikle "çünkü yapılabilir" kategorisinde yer alır ve mücadelede anlamlı bir şekilde rekabet etmek anlamına gelmez.
Dijital Travma


6

Perl, 133 karakter

En kısa değil, ancak herhangi bir boyut girişini işlemek için bir basamak-basamak algoritması kullanır ve O (log n) zamanında çalışır. Dizge olarak sayılar ve sayı olarak sayılar arasında serbestçe dönüştürür. Mümkün olan en büyük ürün, tek bir basamağın karesiyle şimdiye kadar kök olduğundan, 64 bitlik bir sistemde 120 bit'e kadar sayıların kare kökünü alabilmelidir.

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}

Açılmış, yani:

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

Güzel! Birinin ne zaman Perl cevabı göndereceğini merak ediyordum. BTW, if length%2bunun yerine söylemek işe yarıyor if(length)%2mu? Bu 1 karakteri tıraş ederdi. Ayrıca, $y=$z,$d=$_ ifbunun yerine söylemek işe yarar ($y,$d)=($z,$_)ifmı? Bence bu 3 karakter daha traş olur.
Todd Lehman

Ve bu biraz sapkın oluyor, ama sanırım fordöngüyü şu şekilde yeniden yazarak 1 tane daha tıraş edebilirsiniz :$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
Todd Lehman

İlk öneri işe yaramaz (adlı bir karma uzunluğunu almaya çalışır %2), ancak diğerleri geçerlidir. Onları çalışacağım.
Ocaklar

1
@ToddLehman postfix foriçin parantez gerekmez; Bunu önerilerinize eklemek bana toplam 6 karakter kazandırıyor. Teşekkürler!
Ocaklar

5

Matlab (56) / Oktav (55)

Bir sabit nokta yöntemi kullanarak kare kökü çalışır. En fazla 36 adımda (bağımsız değişken olarak 2 ^ 64-1 için) birleşir ve sonra 'olası' tamsayı köklerinden daha düşük olup olmadığını kontrol eder. Her zaman 36 yineleme kullandığından O (1) = P çalışma süresine sahiptir

Argümanın uint64 olduğu varsayılır.

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

Oktav:

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

Bu benim için yeni bir yöntem ve oldukça havalı. +1
seequ

1
Temel olarak, en eski bilinen 3700 yaşında olduğu tahmin edilen en eski sayısal yöntemlerden biri olan en.wikipedia.org/wiki/… . Şaşırtıcı derecede kolay bir kanıtı olan en.wikipedia.org/wiki/Banach_fixed-point_theorem tarafından haklı gösterilebilir , gerçekten güzel =)
flawr

5

Ruby - 36 karakter

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

Güzel yapılmış! En kötü uygulama süresi nedir?
Todd Lehman

G * g <n olması ve cevabın hala istenen değere yakın olmaması durumunda ne olur? Senaryo durmayacak mı?
WallyWest

1
@ToddLehman Dürüst olmak gerekirse bilmiyorum. : - / Bu Babil yöntemidir . Ortalama karmaşıklığın iyi bir kanıtı gibi görünen şey şu . Sayının ilk tahmininin oldukça kötü olduğunu, ancak oturup en kötü durumu anlamak için bu kanıtı gerçekten almam gerekirdi. Biraz daha boş zamanım olduğunda bir şans vereceğim. :-)
OI

@WallyWest Anladığım kadarıyla whileg, istenen değer olan zemine ( convern) yakınsa ilmik tam olarak sona eriyor. Bunun doğru olmayacağı bir durum görüyor musunuz?
OI

4

Python (39)

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

Doğal özyinelemeli yaklaşım. Kareleri çok yüksek olana kadar potansiyel kare kökleri sayar, sonra 1'e kadar iner . Yığın derinliğini aşmaktan endişe ediyorsanız Stackless Python'u kullanın .

and/orDeyim üçlü operatör olarak eşdeğerdir

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

Düzenleme: Ben yerine alabilirsiniz 25 karakter kuralı "Sen kullanabilir istismar ederek *, /, +, -ve üs alma (örneğin **ya ^da güçlerin bir seçtiğiniz dilde operatör dahili, ama sadece üs alma az 1'den ise). " (Düzenle: Görünüşe göre Dennis bu numarayı zaten buldu ve sömürdü.)

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

//Yuvarlamak için Python 3'ün tamsayı bölme işlecini kullanıyorum . Ne yazık ki, n=00 hatasıyla bölünme vermemesi için çok fazla karakter harcıyorum . Değilse, 18 karakter yapabilirdim

lambda n:n**1.5//n 

Kurallar ayrıca işlevin adlandırılması gerektiğini de söylemedi ("İşlevinize istediğiniz herhangi bir şeyi adlandırabilirsiniz."), Ancak varsa, bu iki karakter daha.


- Teşekkürler, bunu açıklığa kavuşturacağım. Sadece bir işlev olmalı. Adının verilmesi gerekmez. Yani, lambda fonksiyonları gayet iyi. Eğer bunu düşünseydim, en başından söz ederdim. Soruyu yayınladığımda C açısından çok fazla düşünüyordum.
Todd Lehman

4

C99 (58 karakter)

Bu bir kod golf açısından bana ilginç olsa da, iyi olduğunu düşünmeyeceğim bir cevap örneğidir, çünkü çok sapkın ve sadece karışıma atmanın eğlenceli olacağını düşündüm:

Orijinal: 64 karakter

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

Bunun korkunç olmasının nedeni, O (log (n)) zamanı yerine O (√n) zamanında çalışmasıdır. (Burada n, giriş değeridir.)

Düzenleme: 63 karakter

Değişen r-1için --rve bunu abutting return:

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

Düzenleme: 62 karakter

Döngü artışını döngünün koşullu kısmının içine taşımak (not: bu ön garanti operatörüne göre işlem sırası derleyiciye özgü olduğu için garanti edilmeyen bir davranışa sahiptir):

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

Düzenleme: 60 karakter

typedefGizlemek için bir ekleme uint64_t( bu öneri için kullanıcı technosaurus'a kredi ).

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

Düzenleme: 58 karakter

Şimdi işlevin çağrılmasında ikinci parametrenin 0 olarak geçirilmesini gerektirir, örneğin, r(n,0)sadece yerine r(n). Tamam, hayatım boyunca, bu noktada bunu daha fazla nasıl sıkıştıracağımı göremiyorum ... kimse?

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

Eğer artış yerine onu C ++ ve azalışını aramak isteyen varsa Birkaç karakter traş mümkün olacaktır: uint64_t s(uint64_t n){for(uint64_t r=n;--n>r/n;);return n;}.
Fors

@Fors - Güzel yaklaşım! Ne yazık ki, bu 1 girişi için sıfıra bölünmeye neden olmaz mı? Ayrıca, 0 girişi için ne yapacak? Çünkü --nne zaman n==0–1 olur ve bunlar işaretsiz değerlerdir, yani –1 2⁶⁴ – 1 olur.
Todd Lehman

1
#define Z uint64_t ... ya da typedef bir çift kurtaracak
technosaurus

@technosaurus - Ah evet, bu tasarruf 2. Teşekkür ederim. :-)
Todd Lehman

1
İfadenin n/++r/rtanımlanmamış davranışı var ....
aschepler

4

Golfscript - 14 karakter

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

En küçük sayı bulun iaz girdi daha niçin n < i*i. Dönüş i - 1.

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

Golfscript'i bilmeyenler için, girişli örnek çağrı için açıklama 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 

Ooh, önceki en iyi Golfscript cevabından 3 karakter daha kısa. İyi iş!
Todd Lehman

Girdi için doğru cevabı vermek için bunu düzeltmek 1muhtemelen iki karakter alır.
Peter Taylor

4

Haskell, 147 138 134 128 bayt

Dünyadaki en kısa kod değil, O (log n) ve rasgele boyutlu sayılarla çalışır:

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)

Bu, sqrt (n) 'ye en iyi düşük yaklaşımı bulmak için [0..n] aralığının ikili aramasını yapar. İşte ungolfed versiyonu:

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

Düzenleme: "Aksi" yan tümcelerini "0" daha kısa bir sürümü olarak "0" ile değiştirerek iki bayt ve g * g satırlarını birkaç satır kaydetti.

Ayrıca, O (sqrt (n)) ile memnunsanız,

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

35 karakter için, ama bu ne eğlenceli?

Edit 2: Çiftler min2Cycle yerine sözlük sırasına göre sıralandığından fark ettim. harita fst, sadece fst yapabilirim. min2Cycle. Golf kodunda, bu, f $ harita fst yerine fst $ f yerine 4 bayt tasarruf anlamına gelir.

Edit 3: proudhaskeller sayesinde altı bayt daha kaydetti!


1
(div x 2 + rem x 2) yerine "half" işlevinizde div (x + 1) 2 kullanabilirsiniz
gurur haskeller

Aslında 49 karakter ve kendi (log n) çözer kendi çözüm var, ama ben sadece 2 upvotes var ;-(. Anlamıyorum neden
gurur haskeller

4

JavaScript 91 88 86: Hız için optimize edildi

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: Hız için optimize edilmedi

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

İşte bir JSFiddle: http://jsfiddle.net/rmadhuram/1Lnjuo4k/


1
PPCG'ye Hoşgeldiniz! Üstü çizili olarak <s> 91 </s> <s> 88 </s> kullanabilirsiniz. Düzenlemeyi yapmayı denedim ama aynı anda düzenleme yapıyordunuz, böylece yapmanıza izin vereceğim.
Rainbolt

1
Veya bunu 41 karakterle yapabilirsiniz:function s(n){for(a=1;++a*a<n;);return a}
Ravent Custard

4

C 95 97

Düzenleme @Michaelangelo önerdiği TypeDef,

Bu, Heron algoritmasının az çok basit bir uygulaması olmalıdır. Tek sorun, tamsayı taşmasını önleyen ortalamanın hesaplanmasıdır: a = (m + n) / 2, biiiig sayıları için çalışmaz.

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

Taşmayı önleme ile iyi bir çalışma —— sadece doğru yapmak için değil, ilk etapta düşünmeye ve test etmeye özen gösterin. Kesinlikle takdir ediyorum.
Todd Lehman

BTW, bazı CPU'larda bölünmenin ne kadar pahalı olabileceği komik. Bu algoritma, abaküs algoritmasının kabaca yarısında çalışsa da, bölünmeyi sevmeyen Core i7 CPU'mda kıyasladığımda abaküs algoritmasından yaklaşık 5 kat daha yavaş bir çalışma süresine sahip. Her neyse, ama çalışma zamanı burada önemli değil - sadece boyut. :) Çok güzel bir iş !!!
Todd Lehman

4

C # 64 62 55

Bu bir (ve matematik ile berbatım) ve çalışma zamanı sadece bir öneri olduğundan, doğrusal zamanda çalışan naif bir yaklaşım yaptım:

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

( dotnetfiddle üzerinde test )

Tabii ki, daha büyük girdiler için son derece yavaş.


1
Sen değiştirerek bir karakter tıraş mümkün olabilir return i-1için return--i?
Todd Lehman

İfadede i*i<=a, normal tipin tamsayı aritmetiği olması garanti edilir mi? (C # aşina değilim.) Öyleyse ve C #, C'nin yaptığı gibi boolean için örtük tamsayı dönüştürmeye izin veriyorsa, bunu değiştirerek bir karakter daha kaydedebilirsiniz a/i/i.
Todd Lehman

1
@ToddLehman DecimalÇarpma sonucu potansiyel olarak bir adım öteye geçebileceğinden taşmayı önlemek için bu aslında sabit nokta aritmetiği ( , daha yüksek maksimum değer ve hassasiyet) olur UInt64.MaxValue. Ancak C # 'ın zaten Boolean'a örtülü dönüşümü yoktur. returnYine de değiştirebilmeliyim , teşekkürler. Bir bilgisayara geri döndüğümde yapacağım.
Bob

3

Clojure - 51 veya 55 bayt

N'den 0'a kadar olan tüm sayıları denetler, ilki nereye verir x^2 <= n. Çalışma zamanı:O(n - sqrt n)

Adsız:

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

Adlandırılan:

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

Misal:

(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 Bayt veya 38 Karakter

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

Burada deneyin .


1
Tamam, bu harika. İyi iş! 17 girdim, Creep ve ardından Run'a tıkladım ve 4 ile geldi! :)
Todd Lehman

3

Kobra - 62

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

Parti - 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 karakter, O (log n)

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

bu çözüm, şamandıralar yerine tamsayılar aramasına rağmen newton-raphson yöntemini uygular. wiki: http://en.wikipedia.org/wiki/Newton%27s_method

karmaşıklığın O (log n) ile ilgili olduğu görülüyor, ancak bunun bir kanıtı var mı? lütfen yorumlarda cevap verin.


\g->div(n+g^2)$2*g7 bayt kaydeder.
Anders Kaseorg

3

J (10)

@Dennis'in cevabından çok, çok, çok ilham alıyor :

<.@%~^&1.5

Ve biraz daha uzun, ancak daha iyi performansla (şüpheliyim):

<.@(-:&.^.)

floor(halve under log)

Yürütmek için girintili parçalar girilir:

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

Belirli bir tamsayı için bunu nasıl yürütürsünüz?
Dennis

1
@Dennis Cevapla
Sepıʇǝɥʇuʎs

3

APL - 12 karakter, 19 bayt

{⌊(⍵*1.5)÷⍵}

örnek kullanım:

{⌊(⍵*1.5)÷⍵}17

4 döndürür

Test vektörü

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

İadeler

1 1 1 1 2 2 3 3 4255254 4294967296

Çevrimiçi Deneyin

Çok teşekkürler : algoritma için kullanıcı "ssdecontrol"


1
PPCG'ye Hoşgeldiniz! Normalde APL'yi karakter başına bir bayt olarak puanlarız. Zorluk belirtmedikçe, UTF-8'de saymaya gerek yoktur. Varolan herhangi bir kodlama iyidir ve gün içinde her karakter için tek bir bayt kullanan eski bir APL kod sayfası vardır. APL'nin ASCII'den önce gelmesi, ASCII olmayan karakterleri kullanmaktan dolayı onu cezalandırmak için kötü bir nedendir. ;) (Yani, bu oldukça eski meydan okuma yine de karakterler tarafından skorlanıyor gibi görünüyor.)
Martin Ender

@MartinEnder Sıcak karşılama ve ipuçları için teşekkürler :)
QuantumKarl

1
01! Dyalog APL'yi kullanarak ⎕DIV←1(çoğu varsayılan olarak kullanılır) doğru sonucu elde etmek için ayarlayabilirsiniz .
Adám

2

C99 (108 karakter)

İşte Wikipedia'daki bir makaledeki bir algoritmadan uyarlanan C99'daki kendi çözümüm . Eminim diğer dillerde bundan daha iyisini yapmak mümkün olmalıdır.

golfed:

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

Kısmen golf:

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

Ungolfed:

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
Öneri: Gerek yok a, kullanın n.
edc65

Ah evet. Teşekkür ederim. Orijinal versiyonumda, ngeri dönmeden önce r ^ 2 <= n <(r + 1) ^ 2 iddiasını (gösterilmiyor) yapabilmem için koruyordum. Bu iddia göz ardı edildiğinde, nbozulmadan kalmak daha uzun süre gereklidir .
Todd Lehman

@ edc65 - Bunu işaret ettiğiniz için tekrar teşekkürler. Bunu yansıtmak için kodumu güncelledim ve birkaç golf hilesi daha ekledim. Ayrıca orijinal iddiaları ekledi ve constungolfed sürümünde n yaptı .
Todd Lehman

2

JavaScript 73 81 (64 bit sayı gereksinimine uymak için)

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

Uygulama İskenderiye'nin Heron algoritması ...


Güzel! Bu, tüm işaretsiz 64 bit tamsayı girişleri için çalışıyor mu?
Todd Lehman

Bu sadece 32-bit kadar görünüyor gibi deneyin ... Benim hayal kırıklığı çok ...
WallyWest

Tabii ki son | 0 herhangi bir değeri 32 bit olarak keser. Bunun yerine Math.floor mu kullanıyorsunuz?
edc65

@ edc65 Haklısın, |032-bit'e kadar etkiler Math.floor, 64- bit'de daha etkilidir ... Bunu yapmak için fazladan 8 karakter almak zorunda kaldığımda kodumu güncelledim ...
WallyWest

@ edc65 Bir düşüncem vardı ... ~~ x 64-bit çalışır?
WallyWest

2

Powershell (52) Int32 ile sınırlı (-2,147,483,648 ila 2,147,483,647)

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

Powershell'de şu anda son test senaryosunu çalıştırmaya çalışıyorum diye bağırıyorum ama ne yaparsam yapayım Powershell, $ _ boru hattı değişkenini Int32 olarak kullanarak açar ve şu anda bu konuda bir yol bulamıyorum.

Bu yüzden cevabımı şimdilik sınırlayacağım. Uint64'leri işlemek için daha iyi bir yol bulabilirsem düzenlerim. (Son test durumu bu arada Powershell'in normal Int64 tipi için çok büyük!)

İşte birkaç test örneği (zamanı izlemek için kullandığım biraz fazladan çıktı ile)

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

O () larımı bilmiyorum, ama bu oldukça dramatik bir sıçrama gibi görünüyor.


2

Dikkat: 2011 itibariyle, R'nin 64 bit tamsayı için yerleşik bir desteği yoktu. Bu cevaplar bu teknikte geçersiz olabilir, ancak daha sonra R son 3 yılda çok değişti.


R, 85

Newton yöntemini kullanarak:

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

ki bu da karesel olarak yakınsar. Karşılaştırma için bir değişkene işlevi atamak için +2 karakter:

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

Kaba kuvvet:

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

Ve aynı kontrol:

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

Ucuz / parlak üs alma hüner :

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

bu da çok hızlı olur (yerleşik kadar hızlı olmasa da):

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

Dördüncü gönderimin çevirisi. Yavaş ama doğru. O (√N). OS X (64 bit) üzerinde test edilmiştir.


2

dc, 50 bayt

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

Boşaltıldı ve açıklandı:

               # 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

Ah, son test senaryosu çöküyor gibi. Düzeltmeye çalışacağım.
Joe

Çözülmüş. Artık çok büyük girdileri kabul ediyor; tesadüfen, düzeltme başlangıçta bazı çirkin kodu kaldırmak için izin verdi.
Joe

2

C 139 137 136 bayt

İlk kod golf denemek. Görünüşe göre O(log n), sadece toplama ve bit kaydırmalarını kullanarak , zamanında çalıştığı için, C'deki "verimli" gereksinime uyan en kısa olanıdır. Yine de daha kısa olabileceğinden eminim ...

a=32Parça değiştirildiği sürece daha büyük tamsayı değerleri için de iyi çalışmalıdır 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;}

İyi iş! Test etmek için çalıştırmadım, ancak kod ilginç görünüyor. Yazdığınız bir nedeni var mı (t++)sadece yerine t++atamaya r?
Todd Lehman

1
@ToddLehman Hayır, bunları çıkarmayı kaçırdım. Güzel yakalayış!
Chris

BTW, aşka--+1 önlemek yazılı bir yolu olarak a-- != UINT64_C(-1). Bu numarayı bir yerde öğrendin mi yoksa kendin mi icat ettin?
Todd Lehman

1
@ToddLehman Teşekkürler! Bunu kendim anladım.
Chris

1

C - 50 (küresel olmayan 61)

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

Yerden tasarruf etmek için parametre ve dönüş değeri olarak global değişkenleri kullanır.

Global sürüm yok:

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

1
Küresel değişkenleri kullanmanın yasal olduğunu düşünmüyorum. En azından ne kadar meşru olacağını söyleyin ve meşru bir versiyon sağlayın
gururlu haskeller

@proud haskeller Global değişkenler neden yasaklanıyor?
mantale

@mantal çünkü çalıştırılabilir bir program / yöntem sağlamanız gerekir.
Marciano.

@ Marciano.Veriye verilen kod çalıştırılabilir.
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;
}

Güzel! Nasıl x+=(d<0)-0.5;... 5 karakter daha kaydeder?
Todd Lehman

BTW, bu, sorun ifadesinde belirtildiği gibi bir işlev biçiminde değildir (ancak böyle olmalıdır). (Tamam, teknik olarak, evet, mainbir işlevdir, ancak olması f(y)gerektiği gibi bir programın içinden çağrılamaz .)
Todd Lehman

En içteki parantez çiftini atlayabilir ve while((d=x*x-y)>0.5)bunun yerine yazabilirsiniz while((d=(x*x-y))>0.5). 2 karakter daha kaydeder. :)
Todd Lehman

Her 0,5 olarak değiştirilebilir .5
Yytsi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.