Raiz quadrada inteira de inteiro [fechado]


12

Problema:

Na sua escolha de idioma, escreva a função mais curta que retorna o piso da raiz quadrada de um número inteiro de 64 bits não assinado.

Casos de teste:

Sua função deve funcionar corretamente para todas as entradas, mas aqui estão algumas que ajudam a ilustrar a idéia:

               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

Regras:

  1. Você pode nomear sua função como quiser. (As funções sem nome, anônima ou lambda são boas, desde que sejam de alguma forma chamadas.)
  2. A contagem de caracteres é o que mais importa nesse desafio, mas o tempo de execução também é importante. Tenho certeza de que você poderia procurar iterativamente para cima a resposta em tempo O (√n) com uma contagem de caracteres muito pequena, mas o tempo O (log (n)) seria realmente melhor (ou seja, assumindo um valor de entrada n, não um comprimento de bit de n).
  3. Você provavelmente desejará implementar a função usando puramente inteiro e / ou aritmética booleana. No entanto, se você realmente deseja usar cálculos de ponto flutuante, tudo bem, desde que você não chame nenhuma função de biblioteca. Portanto, simplesmente dizer return (n>0)?(uint32_t)sqrtl(n):-1;em C está fora dos limites, mesmo que produza o resultado correto. Se você estiver usando aritmética de ponto flutuante, você pode usar *, /, +, -, e exponenciação (por exemplo, **ou ^se é um built-in operador no idioma de sua escolha, mas a exponenciação única de poderes não inferior a 1 ). Essa restrição é para evitar "trapaça" chamando sqrt()ou uma variante ou aumentando um valor para a ½ potência.
  4. Se você estiver usando operações de ponto flutuante (consulte # 3), não será necessário que o tipo de retorno seja inteiro; apenas que o valor de retorno seja um número inteiro, por exemplo, floor (sqrt (n)), e seja capaz de manter qualquer valor de 32 bits não assinado.
  5. Se você estiver usando C / C ++, poderá assumir a existência de tipos inteiros de 64 e 32 bits não assinados, por exemplo, uint64_te uint32_tconforme definido em stdint.h. Caso contrário, verifique se o tipo inteiro é capaz de conter qualquer número inteiro não assinado de 64 bits.
  6. Se o seu idioma não suportar números inteiros de 64 bits (por exemplo, o Brainfuck aparentemente possui apenas o número inteiro de 8 bits), faça o seu melhor com isso e indique a limitação no título da resposta. Dito isso, se você puder descobrir como codificar um número inteiro de 64 bits e obter corretamente a raiz quadrada usando aritmética primitiva de 8 bits, terá mais poder para você!
  7. Divirta-se e seja criativo!

7
"mas o tempo O (log₄ (n)) seria realmente melhor." - quanto melhor? Existe um bônus? Isso é um requisito difícil? É essencialmente um desafio separado? É apenas uma boa idéia que realmente não afeta a pontuação?
John Dvorak

3
Normalmente, usa-se o tamanho da entrada em vez do valor da entrada para derivar complexidade algorítmica. Nesse sentido, o algoritmo de incremento e repetição é exponencial em velocidade.
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 contam?
Milo

1
A maioria dos tipos de dados de ponto flutuante não tem a precisão necessária para esta tarefa. 53 bits significativos não são suficientes para toda a faixa de entrada.
User2357112 suporta Monica

Respostas:


14

CJam, 17 (ou 10) bytes

{_1.5#\/i}

Experimente online verificando os casos de teste:

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

Ele não passará no último caso de teste devido a problemas de arredondamento, mas como 18446744073709551615não é um número inteiro no CJam (é um número inteiro grande ), ainda somos bons, certo?

Caso contrário, o código a seguir (e um pouco mais longo) corrigirá esses erros:

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

Não é mais a solução mais curta, mas é uma faaast .

Como funciona

__    " 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! oops, ok, você me colocou no tecnicismo lá. Eu deveria ter dito nenhum poder fracionário. Mas o seu código realmente obedece às regras declaradas, então eu estou votando nele. :)
Todd Lehman

2
O CJam possui decimais de precisão arbitrária, para cobrir todo o intervalo de entrada?
Isaacg

Além disso, bom truque ao usar NaN -> 0 na conversão para int.
Isaacg

Idéia pura, ele também pode ser representado em J em exatamente o mesmo número de caracteres: <.@%~^&1.5. Posso postar isso como uma resposta separada (já que é basicamente uma porta exata sua)?
ɐɔıʇǝɥʇuʎs

@ :ıʇǝɥʇuʎs: Vá em frente. Mas acabei de descobrir que minha solução pode arredondar incorretamente para grandes números, incluindo o último caso de teste. Em minha defesa, passou minha inspeção só porque 4294967295e 4294967296olhar muito semelhante ...
Dennis

10

Haskell, 28 26

Acredito que esta é a entrada mais curta de qualquer idioma que não foi projetado para jogar golfe.

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

Nomeia uma função scom parâmetro ae retorna um menos o primeiro número cujo quadrado é maior que a. Corre incrivelmente devagar (O (sqrt n), talvez?).


1
Um índice de lista ( [...]!!0) não seria menor que o cabeçalho?
Isaacg

@isaacg Sim, seria. Obrigado :-)
Zaq

7

Golfscript, 17 caracteres

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

Eu poderia nomear minha função da maneira que quisesse, mas decidi não nomeá-la. Adicione dois caracteres para nomear, adicione três para nomear e não deixe na pilha; subtraia um caractere se o fornecimento de um programa completo estiver OK.

Essa abominação não é executada no tempo logarítmico no valor da entrada, e não no tempo O (sqrt n), leva uma quantidade linear de tempo para produzir o resultado. Também ocupa muito espaço. Absolutamente horrendo. Mas ... isso é código-golfe.

O algoritmo é:

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

Eu amo isso!! Bom trabalho! Isso é maravilhosamente perverso.
Todd Lehman

7

Pitão , 14 caracteres

DsbR;fgb*TTL'b

Fornece uma função nomeada, s, que calcula a raiz quadrada filtrando a lista de 0 a n para o quadrado ser maior que a entrada e, em seguida, imprime o último número desse tipo. Não usa exponenciação ou flutua.

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

Exemplo de uso:

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 (não concorrente - O idioma é mais novo que o desafio), 43

Enquanto trabalhava nessa resposta , ocorreu-me que um método semelhante pode ser usado para calcular raízes quadradas inteiras usando retina:

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

1+

Isso se baseia no fato de que quadrados perfeitos podem ser expressos como 1+3+5+7+...e pelo corolário que o número de termos nessa expressão é a raiz quadrada.

Experimente online. (Primeira linha adicionada para permitir a execução de vários casos de teste.)

Obviamente, devido à conversão decimal em unária, isso funcionará apenas para entradas relativamente pequenas.


4
(O idioma é mais recente que o desafio) #
mbomb007 13/06

@ mbomb007 Bastante justo - Título editado. Esta resposta está definitivamente na categoria "porque isso pode ser feito" e não pretende competir no desafio de maneira significativa.
Digital Trauma


6

Perl, 133 caracteres

Não é o menor de longe, mas usa um algoritmo dígito por dígito para lidar com qualquer entrada de tamanho e é executado no tempo O (log n). Converte livremente entre números como seqüências de caracteres e números como números. Como o maior produto possível é a raiz até agora com o quadrado de um único dígito, ele deve conseguir a raiz quadrada de até 120 ou mais números em um sistema de 64 bits.

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}

Descomprimido, ou seja:

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

Agradável! Eu queria saber quando alguém postaria uma resposta Perl. BTW, funciona dizer em if length%2vez de if(length)%2? Isso reduziria 1 caractere. Além disso, seria bom dizer em $y=$z,$d=$_ ifvez de ($y,$d)=($z,$_)if? Eu acho que isso cortaria mais 3 personagens.
27614 Todd Lehman

E isso está ficando um pouco perverso, mas acho que você pode raspar mais 1 ainda, reescrevendo o forloop da seguinte forma:$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
Todd Lehman

A primeira sugestão não funciona (tenta levar o tamanho de um hash chamado %2), mas as outras são válidas. Vou trabalhar com eles.
hobbs

1
O postfix do @ToddLehman fornão precisa de parênteses; adicionando isso às suas sugestões, me dá 6 caracteres no total. Obrigado!
Hbbs

5

Matlab (56) / Oitava (55)

Ele elabora a raiz quadrada usando um método de ponto fixo. Ele converge no máximo 36 etapas (para 2 ^ 64-1 como argumento) e depois verifica se é a mais baixa das raízes inteiras 'possíveis'. Como sempre usa 36 iterações, possui um tempo de execução de O (1) = P

O argumento é assumido como 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

Oitava:

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

Esse é um novo método para mim, e é bem legal. +1
veja

1
É basicamente o en.wikipedia.org/wiki/…, que é um dos primeiros métodos numéricos conhecidos, estimados em cerca de 3700 anos. Ele pode ser justificada pela en.wikipedia.org/wiki/Banach_fixed-point_theorem que tem uma prova surpreendentemente fácil, é realmente agradável =)
flawr

5

Ruby - 36 caracteres

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

Bem feito! Qual é o pior tempo de execução?
Todd Lehman

O que acontece se g * g <n e a resposta ainda não estiverem próximas do valor desejado? O script não vai parar?
30514 WallyWest

1
@ToddLehman Eu sinceramente não sei. : - / Este é o método babilônico . Aqui está o que parece ser uma boa prova da complexidade média . O palpite inicial do número em si é muito ruim, mas eu precisaria me sentar e realmente reunir essa prova para entender o pior caso. Vou tentar quando tiver mais tempo livre. :-)
OI

@WallyWest Meu entendimento é que o whileloop termina exatamente quando g converge para o piso (√n), que é o valor desejado. Você vê algum caso em que isso não seria verdade?
OI

4

Python (39)

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

A abordagem recursiva natural. Conta as raízes quadradas em potencial até o quadrado ficar muito alto e diminui em 1. Use o Stackless Python se você estiver preocupado em exceder a profundidade da pilha.

O and/oridioma é equivalente ao operador ternário como

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

Edit: eu posso em vez de obter 25 caracteres , explorando a regra ", você pode usar *, /, +, -, e exponenciação (por exemplo, **ou ^se é uma exponenciação built-in operador no idioma de sua escolha, mas apenas de poderes não inferior a 1). " (Editar: Aparentemente, Dennis já encontrou e explorou esse truque.)

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

Eu uso o operador //de divisão inteira do Python 3 para arredondar para baixo. Infelizmente, gasto muitos caracteres para que o caso n=0não dê uma divisão por 0 erro. Se não fosse por isso, eu poderia fazer 18 caracteres

lambda n:n**1.5//n 

As regras também não diziam que a função tinha que ser nomeada (dependendo de como você interpreta "Você pode nomear sua função como quiser"), mas, se o fizer, serão mais dois caracteres.


- Obrigado, eu vou esclarecer isso. Só tem que ser uma função. Não precisa ser nomeado. Portanto, as funções lambda estão bem. Eu teria mencionado isso desde o início, se tivesse pensado nisso. Eu estava pensando demais em termos de C quando postei a pergunta.
Todd Lehman

4

C99 (58 caracteres)

Este é um exemplo de resposta que eu não consideraria uma boa, embora seja interessante para mim do ponto de vista do código de golfe, porque é muito perversa, e eu apenas pensei que seria divertido jogar na mistura:

Original: 64 caracteres

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

A razão pela qual este é terrível é que ele é executado no tempo O (√n) em vez do tempo O (log (n)). (Onde n é o valor de entrada.)

Editar: 63 caracteres

Alterando o r-1para --re confinando-o para return:

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

Editar: 62 caracteres

Movendo o incremento do loop para dentro da parte condicional do loop (nota: isso tem um comportamento não garantido, porque a ordem das operações com relação ao operador de pré-incremento é específica do compilador):

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

Editar: 60 caracteres

Adicionando a typedefpara ocultar uint64_t(crédito ao usuário technosaurus por esta sugestão).

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

Editar: 58 caracteres

Agora exigindo que o segundo parâmetro seja passado como 0 na invocação da função, por exemplo, em r(n,0)vez de apenas r(n). Ok, para a minha vida, neste momento não consigo ver como comprimir isso mais ... alguém?

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

Se você estiver disposto a chamá-lo de C ++ e decréscimo em vez de incremento de que você seria capaz de raspar um par de personagens: uint64_t s(uint64_t n){for(uint64_t r=n;--n>r/n;);return n;}.
Fors 27/07

@Fors - Boa abordagem! Infelizmente, isso não causará uma divisão por zero para a entrada 1? Além disso, o que fará para uma entrada de 0? Como --nquando n==0seria –1 e esses são valores não assinados, então –1 seria 2⁶⁴ – 1.
27614 Todd Lehman

1
#define Z uint64_t ... ou typedef irá salvar um casal
Technosaurus

@technosaurus - Ah sim, isso economiza 2. Obrigado. :-)
Todd Lehman

1
A expressão n/++r/rtem um comportamento indefinido ....
aschepler 29/07

4

Golfscript - 14 caracteres

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

Encontre o menor número imenor que a entrada npara a qual n < i*i. Retorno i - 1.

Ou seja, [0..n-1].first(i => n < i*i) - 1

Explicação para aqueles que também não conhecem o Golfscript, para exemplo de chamada com entrada 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, são 3 caracteres a menos que a melhor resposta anterior do Golfscript. Bom trabalho!
Todd Lehman

Corrigir isso para fornecer a resposta correta para entrada 1provavelmente leva dois caracteres.
Peter Taylor

4

Haskell, 147 138 134 128 bytes

Não é o código mais curto do mundo, mas é executado em O (log n) e em números de tamanho arbitrário:

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)

Isso faz uma pesquisa binária do intervalo [0..n] para encontrar a melhor aproximação mais baixa ao sqrt (n). Aqui está uma versão não destruída:

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

Editar: salvou dois bytes substituindo as cláusulas "caso contrário" por "0 <1" como uma versão mais curta de "True" e mais algumas inserindo g * g.

Além disso, se você está feliz com O (sqrt (n)), você pode simplesmente fazer

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

para 35 caracteres, mas que graça é essa?

Edit 2: Acabei de perceber que, uma vez que os pares são classificados por ordem do dicionário, em vez de fazer min2Cycle. mapear fst, eu posso apenas fazer fst. min2Cycle. No código golfed, isso significa substituir f $ map fst por fst $ f, economizando mais 4 bytes.

Editar 3: economizou mais seis bytes graças ao proudhaskeller!


1
você pode substituir (div x 2 + rem x 2) com div (x + 1) 2, em função de sua "metade"
haskeller orgulhoso

Na verdade, tenho uma solução do meu próprio que tem 49 caracteres, e resolve em O (log n), mas eu só tenho 2 upvotes ;-( Eu não entendo o porquê.
haskeller orgulhoso

4

JavaScript 91 88 86: Otimizado para velocidade

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: não otimizado para velocidade

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

Aqui está um JSFiddle: http://jsfiddle.net/rmadhuram/1Lnjuo4k/


1
Bem-vindo ao PPCG! Você pode usar <s> 91 </s> <s> 88 </s> para tachar. Tentei fazer a edição, mas você estava editando ao mesmo tempo, então deixarei você fazer.
Rainbolt 8/08/14

1
Ou você pode fazê-lo em 41 caracteres como este:function s(n){for(a=1;++a*a<n;);return a}
Rhubarb Custard

4

C 95 97

Editar Typedef, sugerido por @Michaelangelo

Isso deve ser mais ou menos uma implementação direta do algoritmo Heron. O único problema é calcular a média evitando o excesso de números inteiros: a = (m + n) / 2 não funciona para números 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;
}

Bom trabalho com a prevenção de transbordamento - não apenas para fazê-lo corretamente, mas tendo o cuidado de pensar sobre isso em primeiro lugar e testá-lo. Definitivamente apreciado.
Todd Lehman

BTW, é engraçado como a divisão pode ser cara em algumas CPUs. Embora esse algoritmo execute aproximadamente metade das etapas que o algoritmo do ábaco, ele tem um tempo de execução cerca de 5 vezes mais lento que o algoritmo do ábaco quando eu o comparo na minha CPU Core i7, que não gosta de fazer divisão. De qualquer forma, mas o tempo de execução não é importante aqui - apenas tamanho. :) Bom trabalho !!!
Todd Lehman

4

C # 64 62 55

Como esse é um (e eu sou péssimo em matemática), e o tempo de execução é apenas uma sugestão, eu fiz a abordagem ingênua que é executada em tempo linear:

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

( teste no dotnetfiddle )

Obviamente, é terrivelmente lento para entradas maiores.


1
Você pode raspar um personagem mudando return i-1para return--i?
Todd Lehman

Na expressão i*i<=a, isso é garantido como aritmética inteira do tipo usual? (Eu não estou familiarizado com C #.) Nesse caso, e se o C # permitir a conversão de números implícitos em booleano como C, então você poderá salvar mais um caractere alterando-o para a/i/i.
Todd Lehman

1
@ToddLehman Na verdade, é uma aritmética de ponto fixo ( Decimalmaior valor máximo e precisão), para evitar o estouro, pois o resultado da multiplicação pode potencialmente ir além UInt64.MaxValue. Mas o C # não tem conversões implícitas em booleano. Eu deveria ser capaz de mudar isso return, obrigado. Farei isso quando voltar ao computador.
Bob

3

Clojure - 51 ou 55 bytes

Verifica todos os números de n a 0, dando o primeiro onde x^2 <= n. O tempo de execução éO(n - sqrt n)

Sem nome:

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

Nomeado:

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

Exemplo:

(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 bytes ou 38 caracteres

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

Experimente aqui.


1
Ok, isso é legal. Bom trabalho! Entrei 17, cliquei em Creep e depois em Run, e veio com 4! :)
Todd Lehman

3

Cobra - 62

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

Lote - 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 caracteres, O (log n)

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

esta solução implementa o método newton-raphson, embora ele procure números inteiros em vez de números flutuantes. wiki: http://en.wikipedia.org/wiki/Newton%27s_method

a complexidade parece ser sobre O (log n), mas há uma prova disso? por favor responda nos comentários.


\g->div(n+g^2)$2*gsalva 7 bytes.
Anders Kaseorg

3

J (10)

Muito, muito, muito inspirado pela resposta de @Dennis :

<.@%~^&1.5

E um pouco mais, mas com melhor desempenho (suspeito):

<.@(-:&.^.)

floor(halve under log)

Para executar, são inseridas peças recuadas:

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

Como você executa isso para um determinado número inteiro?
Dennis

1
@Dennis Veja a resposta
ɐɔıʇǝɥʇuʎs

3

APL - 12 caracteres, 19 bytes

{⌊(⍵*1.5)÷⍵}

exemplo de uso:

{⌊(⍵*1.5)÷⍵}17

retorna 4

Vetor de teste

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

retorna

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

Experimente on-line

Muito obrigado a : usuário "ssdecontrol" do algoritmo


1
Bem-vindo ao PPCG! Normalmente, pontuamos APL como um byte por caractere. A menos que o desafio o especifique, não há necessidade de contar no UTF-8. Qualquer codificação existente é boa e há uma página de código APL antiga no dia anterior que usa um único byte para cada caractere. O fato de o APL ser anterior ao ASCII é um mau motivo para penalizá-lo pelo uso de caracteres não ASCII. ;) (Dito isto, este bastante antigo desafio parece marcar por personagens de qualquer maneira).
Martin Ender

Graças @MartinEnder para a recepção calorosa e dicas :)
QuantumKarl

1
01! Usando o Dyalog APL , você pode definir ⎕DIV←1(que muitos usam como padrão) para obter o resultado correto.
Adám 16/06/16

2

C99 (108 caracteres)

Aqui está minha própria solução no C99, que é adaptada de um algoritmo em um artigo na Wikipedia . Tenho certeza de que deve ser possível fazer muito melhor que isso em outros idiomas.

Golfe:

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

Parcialmente jogado:

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
Sugestão: Não há necessidade a, use n.
Edc65

Ah sim. Obrigado. Na minha versão original, eu estava mantendo npara que, pouco antes de retornar, pudesse fazer a afirmação (não mostrada) de que r ^ 2 <= n <(r + 1) ^ 2. Com essa afirmação omitida, é mais necessário manter-se nintacto.
27614 Todd Lehman

@ edc65 - Mais uma vez obrigado por apontar isso. Atualizei meu código para refletir isso, além de adicionar outros truques de golfe. Também foram adicionadas as asserções originais e transformadas constem n na versão ungolfed.
Todd Lehman

2

JavaScript 73 81 (para atender aos requisitos de números de 64 bits)

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

Implementando o algoritmo de Heron de Alexandria ...


Agradável! Isso funciona para todas as entradas inteiras de 64 bits não assinadas?
Todd Lehman

Tente como eu poderia isso só parece funcionar até 32-bit ... Muito para minha decepção ...
Wally West

Certamente, o último | 0 trunca qualquer valor para 32 bits. Usando Math.floor?
Edc65 29/07

@ edc65 Você está certo, na verdade, parece |0afetar até 32 bits, enquanto Math.flooré mais eficaz em 64 bits ... Atualizei meu código, tendo que extrair 8 caracteres extras para fazer isso ...
WallyWest

@ edc65 Acabei de ter um pensamento ... ~~ x funcionaria em 64 bits?
amigos estão dizendo sobre wallywest

2

Powershell (52) Limitado ao Int32 (-2.147.483.648 a 2.147.483.647)

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

Estou gritando com o Powershell agora, tentando fazer com que o último caso de teste funcione, mas não importa o que eu faça, o Powershell acaba usando a variável de pipeline $ _ como um Int32, e não consigo encontrar uma maneira de contornar isso agora.

Então, vou limitar minha resposta por enquanto. Se eu conseguir encontrar uma maneira melhor de lidar com o uint64s, editarei. (O último caso de teste é muito grande para o tipo Int64 normal do Powershell, a propósito!)

Aqui estão alguns casos de teste (com um pouco de saída extra que eu costumava acompanhar o tempo)

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

Não conheço meus O (), mas isso parece um salto bastante dramático.


2

Advertência: a partir de 2011, o R não tinha suporte interno para números inteiros de 64 bits, como eu supunha. Essas respostas podem ser inválidas nesse aspecto técnico, mas, novamente, o R mudou muito nos últimos 3 anos.


R, 85

Usando o método de 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)}

que converge quadraticamente. +2 caracteres para atribuir a função a uma variável para benchmarking:

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

Força bruta:

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

E a mesma verificação:

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

O truque barato / brilhante de exponenciação :

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

que também é muito rápido (embora não tão rápido quanto o interno):

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

Tradução da minha quarta submissão. Lento, mas correto. O (√n). Testado no OS X (64 bits).


2

dc, 50 bytes

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

Espaçados e explicados:

               # 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

Parece que o último caso de teste travou. Vou tentar consertar.
30816 Joe

Resolvido. Agora aceita entrada muito grande; por acaso, a correção me permitiu remover um código feio no começo.
Joe

2

C, 139 137 136 bytes

Minha primeira tentativa no código de golfe. Parece que é o menor em C que se encaixa no requisito "eficiente", pois é executado no O(log n)tempo, usando apenas mudanças de adição e de bits. Embora eu tenha certeza que poderia ser mais curto ainda ...

Deve funcionar muito bem para valores inteiros maiores também, desde que a a=32peça seja alterada para 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;}

Bom trabalho! Eu não o executei para testar, mas o código parece interessante. Existe uma razão para a qual você escreveu, e (t++)não apenas t++na tarefa r?
Todd Lehman

1
@ToddLehman Não, apenas senti falta de tirá-los. Boa pegada!
Chris

BTW, eu amo o a--+1como uma forma de escrita evitar a-- != UINT64_C(-1). Você aprendeu esse truque em algum lugar ou inventou você mesmo?
Todd Lehman

1
@ToddLehman Thanks! Eu mesmo descobri isso.
Chris

1

C - 50 (61 sem global)

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

Ele usa variáveis ​​globais como parâmetro e retorna valor para economizar espaço.

Nenhuma versão global:

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

1
Eu não acho que o uso de variáveis ​​globais seja legal. Pelo menos dizer quanto tempo seria legitimamente e fornecer uma versão legítima
haskeller orgulhoso

@proud haskeller Por que variáveis ​​globais são proibidas?
mantale

@mantal porque você deve fornecer um programa / método executável.
Marciano.Andrade

@ Marciano.Andrade o código é dado é executável.
precisa saber é

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

Agradável! Que tal x+=(d<0)-0.5;... salva mais 5 caracteres?
Todd Lehman

BTW, isso não é (mas deve ser) na forma de uma função, conforme mencionado na declaração do problema. (Ok, tecnicamente, sim, mainé uma função, mas não é exigível a partir de dentro de um programa como um f(y)seria.)
Todd Lehman

Eu acho que você pode omitir o par mais interno de parênteses e escrever em while((d=x*x-y)>0.5)vez de while((d=(x*x-y))>0.5). Salva mais 2 caracteres. :)
Todd Lehman

Cada 0,5 pode ser alterado para 0,5
Yytsi 20/06/2016
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.